Главная страница

Математика. Настоящий учебник посвящен системе Mathematica прикладному пакету компьютерной алгебры, при помощи которого можно решать любые задачи, в которых в той или иной форме встречается математика


Скачать 4.43 Mb.
НазваниеНастоящий учебник посвящен системе Mathematica прикладному пакету компьютерной алгебры, при помощи которого можно решать любые задачи, в которых в той или иной форме встречается математика
АнкорМатематика
Дата11.05.2022
Размер4.43 Mb.
Формат файлаpdf
Имя файла106-108.pdf
ТипУчебник
#521834
страница14 из 38
1   ...   10   11   12   13   14   15   16   17   ...   38
§ 8. Просто замены и сугубые замены:
Replace
, /. ReplaceAll, //. ReplaceRepeated
Как аллилуйи делятся на аллилуйи просто и сугубые аллилуйи.
Венедикт Ерофеев, Из записных книжек
Применение замен и правил подстановки к выражениям производится при помощи операторов замены таких как Replace и его усиленные ва- рианты ReplaceAll, ReplaceRepeated и ReplaceList. По сути, к той же категории относится и оператор ReplacePart, но он синтаксически отлича- ется от остальных перечисленных операторов и мы обсуждаем его в части
3 в контексте работы со списками.
Replace[x,y->z,
{n}]
однократная замена на n-м уровне
/.
ReplaceAll[x,y->z]
однократная замена на всех уровнях
//.
ReplaceRepeated[x,y->z]
многократная замена
ReplaceList[x,y->z]
список результатов всех замен
Самой деликатной из этих команд является Replace, которая позволяет явно контролировать,
на каких уровнях выражения производятся замены. При этом:
Формат команды Replace таков: Replace[expr,rule,level] имеет два аргумента, выражение и правило подстановки/преобразования и один па- раметр — спецификацию уровня.
К каждому подвыражению выражения expr, находящемуся на уровне действия Replace, описанному level, к которому можно применить под- становку rule, она применяется один раз.
По умолчанию Replace[x,y->z] вызванная без указания уровня при- меняется только к уровню 0, т.е. ко всему выражению x!!!
Уровень задается при помощи обычных спецификаций: {n} для n-го уровня, n для n-го и всех более высоких уровней,
{m,n} для всех уровней между m-м и n-м, Infinity для всех уровней,
{-1} для листьев и т.д. Все эти спецификации подробно обсуждаются в Главе 1.
Второй аргумент команды Replace может быть списком правил под- становки/преобразования. В этом случае к каждому подвыражению выра- жения expr команда Replace применяет только одну подстановку, из этого списка, а именно, первую из тех, которые можно применить.

200
Таким образом,
Replace[f[x],x->a]===f[x],
Replace[f[x],x->a,1]===f[a],
Replace[f[x],
{x->a,f[x]->f[b]}]===f[b].
Вот еще характерный пример того, как работает команда Replace. Полагая glist=NestList[List,g,2] или, что то же самое, glist=
{g,{g},{{g}}},
и применяя к этому списку командой Replace замену g->a, а потом список замен
{g->a,{g}->a,{{g}}->a} на уровнях 0,1,2,3, мы получим следующие ответы:
In[16]:=Map[Replace[glist,g->a,
{#}]&,Range[0,3]]
Out[16]=
{{g,{g},{{g}}},{a,{g},{{g}}},{g,{a},{{g}}},{g,{g},{{a}}}}
In[17]:=Map[Replace[glist,
{g->a,{g}->a,{{g}}->a},{#}]&,Range[0,3]]
Out[17]=
{{g,{g},{{g}}},{a,a,a},{g,{a},{a}},{g,{g},{{a}}}}
В реальных вычислениях особенно часто применяются две следующие сильные версии команды Replace:
Команда ReplaceAll[expr,rule] представляет собой просто сокраще- ние для Replace[expr,rule,Infinity]. Иными словами, ReplaceAll со- стоит в однократном применении правила rule ко всем уровням вы- ражения expr. Этот оператор используется настолько часто, что для него имеется специальная операторная запись expr /. rule.
С другой стороны, ReplaceRepeated[expr,rule], или, в сокращен- ной операторной записи, expr //. rule предписывает не просто применять подстановку rule ко всем уровням выражения expr, но и проделывать это многократно, до тех пор, пока выражение продолжает меняться при оче- редной подстановке!!!
Следующий диалог иллюстрирует драматическое различие между тем, как исполняются ReplaceAll и ReplaceRepeated, снова лежащее в области flow of calculation. Команда /. предписывает применить подстановку, заме- няющую ln(xy) на ln(x)+ln(y), ко всем частям выражения, но только один раз. С другой стороны, //. предписывает проделывать эту подстановку quantum satis (= до полного удовлетворения):
In[18]:=Log[a*b*c*d] /. Log[x *y ]->Log[x]+Log[y]
Out[18]=Log[a]+Log[b*c*d]
In[19]:=Log[a*b*c*d] //. Log[x *y ]->Log[x]+Log[y]
Out[19]=Log[a]+Log[b]+Log[c]+Log[d]
Обсудим, наконец, чем команда Replace отличается от ReplaceList. Ко- манда Replace применяет первое из тех правил rule, которое можно при- менить,
первым из тех способов, которыми его можно применить. В то же время команда ReplaceList дает список результатов, которые получаются при всевозможных применениях всех правил.
То, что происходит со списком подстановок, как раз совершенно ясно.
Скажем, Replace[x,
{x->a,x->b,x->c}] даст a, в то же время результа- том вычисления ReplaceList[x,
{x->a,x->b,x->c}] будет список {a,b,c}.

201
Если бы использование ReplaceList сводилось к этому, она была бы фак- тически излишня, так как она сразу выражалась бы через Replace и Map.
Однако в действительности роль ReplaceList гораздо шире. То, что дела- ет ее по-настоящему полезной, это не возможность одновременно увидеть результаты применения нескольких правил подстановки/преобразования, а возможность посмотреть на все способы применения одного и того же правила преобразования к данному выражению. Например,
Replace[a+b+c,x +y ->x*y]
дает единственный результат a(b+c). Команда ReplaceList делает значи- тельно больше:
In[20]:=ReplaceList[a+b+c,x +y ->x*y]
Out[20]=
{a(b+c),b(a+c),(a+b)c,(a+b)c,b(a+c),a(b+c)}
Обратите внимание, что при этом рассматриваются не только группиров- ки соседних членов, а вообще все группировки, в том порядке, как ядро их замечает! Ясно, что во всех сколь-нибудь сложных случаях подобный учет всех возможностей преобразования выражений при помощи какого- то правила находится за пределами комбинаторных способностей обычного человека.
§ 9. Чистка: Unset, Clear, ClearAll, Remove
Если мы все же не пользуемся подстановками и заменами, а придаем переменным постоянные значения посредством операторов присваивания,
то время от времени эти переменные следует чистить. Это производится при помощи следующих команд.
Unset[x]
очистить значения x
Clear[x,y,z]
очистить значения и определения x, y, z
ClearAll[x,y,z]
кроме того, очистить атрибуты и опции x, y, z
Remove[x,y,z]
смешать x, y, z с грязью и забыть их имена
Эти команды расположены здесь в порядке возрастающей силы:
Unset[x], или, в сокращенной записи x=. убирает все значения и пра- вила для переменной x. В отличие от остальных команд Unset применяется к одному аргументу и отменяет немедленное присваивание x = u.
Clear[x,y,z], убирает не только все значения переменных x, y, z, но и все их определения. Иными словами, Clear отменяет не только немедлен- ное присваивание x = u, но и отложенное присваивание x := u.
ClearAll[x,y,z], убирает не только все определения и значения пере- менных x, y, z, но и их атрибуты, дефолтные опции и связанные с ними сообщения. Тем не менее, Mathematica помнит, что переменные с такими именами использовались во время сессии.
Remove[x,y,z], убирает не только все определения и значения, атрибу- ты, и пр., но и сами имена переменных x, y, z. Эти переменные перестают

202
распознаваться программой — до тех пор, конечно, пока Вы их снова не создадите! Иными словами, программа считает, что эти переменные во время сессии вообще не использовались. Различие ClearAll и Remove ил- люстрируется следующими двумя диалогами:
In[21]:=x=3; ClearAll[x]; NameQ["x"]
Out[21]=True
In[22]:=x=3; Remove[x]; NameQ["x"]
Out[22]=False
Иногда, однако, мы можем слегка перестараться в нашей любви к очист- ке — не вздумайте печатать что-нибудь в духе ClearAll["*"]!!! Наряду с тем, что Вы намеревались очистить, это вычистит заодно и все ответы на информационные запросы, сообщения об ошибках и пр. На такие слу- чаи в системе предусмотрена двуступенчатая защита объектов от чисток,
очисток и зачисток:
Команды Clear, ClearAll и Remove не действуют на объекты, которые имеют атрибут Protected.
На случай, если Вам вздумается положить x + y := x ∗ y, или что- нибудь в таком духе, все встроенные объекты защищены атрибутом Pro- tected, который запрещает менять их определения и значения.
В некоторых случаях, скажем, если Вы пишете программу, которой пользуются несколько человек, возникает желание защитить некоторые из определенных Вами объектов и/или их значений от случайного переопре- деления. Это делается при помощи команды Protect[object], которая устанавливает на объект object атрибут Protected и делает его неуязви- мым (immune) по отношению к зачисткам.
Защиту большинства внутренних и системных объектов можно снять командой Unprotect[object]. После этого с объектом можно поступать так же, как с любым другим символом. Например, примитивисты и сюр- реалисты оценят возможности, которые предоставляют тексты наподобие следующего: Unprotect[Log]; Log[10]=2. После этого система будет ис- кренне верить, что Log[10]==2. Однако обычно подобная модификация внутренних объектов не есть слишком хорошая идея (is not such a smart idea).
Имеется небольшое число фундаментальных внутренних и системных объектов, любая модификация определений и/или значений которых при- вела бы к полному краху системы.
Эти объекты защищены атрибутом
Locked, который запрещает изменение каких-либо атрибутов этих объек- тов и, тем самым, в частности, удаление атрибута Protected. Это зна- чит, что внутри системы определения этих объектов вообще не могут быть изменены.

203
§ 10. Создание локальных и уникальных переменных
На случай, если Вы используете систему Mathematica для программи- рования в процедурном духе, в языке Mathematica предусмотрено два ос- новных способа локализации переменных или их значений, Module и Block,
отвечающие тому, что традиционно называлось `подпрограммой', `процеду- рой', `субрутиной' и т.д. и заключалось в текст begin ... end. Внутри этих конструкций Вы можете делать все что угодно!! Как и все, что помещено в новый контекст посредством Begin[context] и End[], то, что вы не вернули в глобальный контекст, не окажет никакого влияния на все, что происходит в глобальном контексте. Кроме того, если Вы любите использовать буков- ку x для обозначения своих переменных, Вы можете не устраивать никаких модулей и блоков, а непосредственно пользоваться командой Unique, кото- рая каждый раз создает новый символ формата x$nnn или xnnn. Если
Вы не меняли настроек системы, используемый при этом серийный но- мер nnn представляет собой ту же системную переменную $ModuleNumber,
которая считает, сколько раз во время сессии использовались конструкции локализации переменных и при каждом таком использовании увеличивает- ся на 1. Наконец, имеется полупрозрачная конструкция With, при помощи которой можно придавать локальные значения глобальным переменным.
Module[
{x,y},body]
лексическая локализация переменных x, y
Block[
{x,y},body]
динамическая локализация переменных x, y
With[
{x=a,y=b},body]
локальная фиксация значений x, y
Unique[x]
создание уникальной переменной x$nnn
Unique["x"]
создание уникальной переменной xnnn
Для всех обычных юзеров, не являющихся профессиональными програм- мистами, действие Module и Block практически полностью эквивалентно.
По причинам исторического, психологического и философского (нежелание увеличивать без нужды количество сущностей) характера авторы предпо- читают пользоваться командой Block, которая не создает новых пере- менных, но оставляет внутри себя все значения переменных, перечислен- ных как локальные.
Комментарий: локальные переменные и локальные значения. На уровне им- плементации разница между командами Module и Block может быть объяснена следую- щим образом. Команда Module, подобно большинству традиционных языков програм- мирования, локализует переменные на лексическом уровне. Эта команда предписывает рассматривать body как часть системного кода и появляющиеся в этом коде переменные
x, y, . . . , рассматриваются как локальные. С другой стороны, команда Block вообще не смотрит на то, как выглядит body и какие переменные туда входят. Вместо этого она трактует все значения переменных x, y, . . . , используемые во время вычисления body,
как локальные. Команда Module несомненно удобнее, если Вы пишете такие програм- мы, в которых субрутины вызываются сотни раз и которые нуждаются в отладке, так что Вам интересно знать, чем текущее значение переменной x$273 отличается от текуще- го значения переменной x$237. Для тех, кто (как мы) пишет программы, проверяемые строка за строкой, необходимости в этом никогда не возникает.
Поясним некоторые важнейшие моменты, связанные с использованием конструкций, включающих Module или Block:

204
Второй аргумент команд Module или Block может быть программой.
В этом случае отдельные команды, входящие в эту программу должны разделяться точкой с запятой: Block[
{x,y},body1;body2;body3]. Ис- пользование здесь запятой вместо точки с запятой является грубой син- таксической ошибкой.
В первом аргументе этих команд локальным переменным можно явно присваивать начальные значения:
Module[
{x=a,y=b},body]
и
Block[
{x=a,y=b},body].
Эффективно Block[
{x=a,y=b},body] является просто более удобным спо- собом ввести следующий текст: Block[
{x,y},x=a;y=b;body].
Использование команд Module или Block абсолютно необходимо в тех случаях, когда в тексте встречаются циклы, итерации, условные опе- раторы и другие управляющие структуры, в процессе своей работы меняющие значения входящих в них переменных!!
Поясним это на примере. Некоторые итерационные команды (такие как
Table, Sum, Product, Do) автоматически локализуют значения своих итера- торов. Однако это относится далеко не ко всем конструкциям!!! Допустим,
Вы включили в текст определения функции цикл
For[i=1,test,i++,body],
предписывающий проделывать операцию body до тех пор, пока выполня- ется критерий test, после каждого выполнения этой операции увеличивая
i на 1. Знаете ли Вы, чему будет равно i в конце выполнения этого цикла?
Ну, скажем, в таком совсем простеньком случае m:=1; For[i=1,Prime[i]<10000,i++,If[OddQ[i],m=m*i,m=m/i]].
Ну так вот, в конце этого цикла i = 1230 и если теперь Вы снова исполь- зуете i в качестве итератора без указания начального значения, итерация начнется с i = 1230, а вовсе не с i = 1, как Вы, скорее всего, имели в виду!!!
В начале 1990-х годов мы систематически допускали трудно отслеживае- мые ошибки такого рода. Против этого есть два лекарства:
явное задание начальных значений итераторов при каждом обраще- нии к ним;
явная локализация всех итераторов и/или их значений при помощи
Module или Block;
а еще гораздо лучше и то и другое!!!
Проиллюстрируем особенности использования конструкции, включаю- щей команду Block на реальном примере. Следующий инпут определяет функцию twin[n], вычисляющую первые n пар близнецов (p, p + 2), где p
и p + 2 оба простые.
In[23]:=twins[n ]:=
Block[
{i,p,m=0,x={}},

205
For[i=1,mIf[PrimeQ[p+2],AppendTo[x,
{p,p+2}];m=m+1]];
Return[x]]
Здесь происходит следующее: мы локализуем значения четырех перемен- ных i, p, m и x. При этом i является просто итератором, p представляет собой i-е простое число, x — промежуточный список близнецов, порож- денный в процессе работы цикла, m — длину этого списка. Формально переменные p, m излишни, так как мы могли обращаться к ним как к
Prime[i] и Length[x], однако явное введение этих переменных не только сокращает и делает более понятным текст, но и ускоряет работу програм- мы.
Более того, если бы мы писали этот текст для его использования другими людьми, мы, скорее всего, дали бы переменным p, m, x говорящие имена типа prime, length, list!! Если бы мы не произвели присваивания p=Prime[i], то внутри условного оператора три раза происходило бы обра- щение к функции Prime. Точно так же явное увеличение значения m на 1
работает быстрее, чем вычисление длины списка x.
Заключенная в Block программа работает следующим образом. В начале ее работы x — пустой список, а m = 0. Для каждого i начиная с 1, до тех пор пока текущая длина m построенного списка пар x меньше n, мы делаем следующее: присваиваем p значение, равное i-му простому числу,
после чего в случае, когда тест PrimeQ[p+2] дает значение True — т.е.
число p + 2 тоже является простым — мы добавляем к x пару (p, p + 2)
увеличивая при этом m на 1. После этого текущее значение i увеличивается на 1 и начинается новая итерация. Как только m стало равным n, цикл прерывается и текущее значение x возвращается на глобальный уровень.
Начинающий должен обратить внимание на несколько ключевых син- таксических моментов:
Явное провозглашение итератора i локальной переменной!!!
Использование команды Return для возвращения значения локальной переменной x на глобальный уровень.
Разделение цикла For и возврата значения Return точкой с запятой;
Внутри цикла разделение присваивания p=Prime[i] и условного опе- ратора If точкой с запятой;
Внутри условного оператора If разделение инкрементации x и инкре- ментации
1   ...   10   11   12   13   14   15   16   17   ...   38


написать администратору сайта