Немедленное и задержанное присваивание
Использование оператора простого присваивания L = Rозначает, что результат вычисления правой части R немедленно присваивается левой части L, где левая часть сама может быть любым выражением. После этого каждый раз, когда появляется L, оно заменяется на R. Присваивание в форме f[args] = R устанавливает привило преобразования связанное с символом f.
Простое присваивание будем называть также немедленным присваиванием.
Математика позволяет производить одновременное присваивание нескольким выражениям:
{l1, l2, …} = {r1, r2, …}.
Другой возможный способ присваивания – задержанное или отложенное присваивание (SetDelayed):
L := R
– правая часть выражения вычисляется только тогда, когда происходит обращение к L. Разница между двумя операторами присваивания видна из следующих двух примеров:
x = Random[]и y := Random[]
Обе величины: x и y – являются случайными, но x при повторных обращениях сохраняет свое первоначальное значение, в то время как y при каждом обращении изменяется.
Пример 8.1.
Определим собственную функцию для вычисления факториала:
In[1] := fac[n_Integer/;n>=0] = If[n==0, 1, n*fac[n–1]]
В этом определении может быть использовано как простое, так и задержанное присваивание. Однако если при использовании оператора простого присваивания переменной n предварительно было присвоено какое-либо значение, возникает ошибка. Например, если переменной n предварительно присвоено значение 3, то мы получим функцию тождественно равную 6.
Замечание. Если в определении функции f(x) используется оператор немедленного присваивания, и аргументу x предварительно присвоено некоторое значение x=a, то определение дает функцию-константу . При использовании в определении функции немедленного присваивания для избежания возможных ошибок следует непосредственно перед присваиванием очистить все идентификаторы, используемые в качестве аргументов функции.
Пример 8.2.
Следующее определение функции f(x)=x2 реально порождает функцию-константу :
In[ ] := {x=3; f[x_]=x^2; Clear[x]; f[x], f[t], f[2]}Out[ ] = {9, 9, 9}
Проблемы с определением функции не возникает, если значение x предварительно очищено:
In[ ] := {Clear[x]; f[x_]=x^2; x=3, f[x], f[t], f[2]} Out[ ] = {3, 9, t2, 4}
Проблемы также не возникает, если используется задержанное присваивание:
In[ ] := {x=3, g[x_]:=x^2; g[x], g[t], g[2]}Out[ ] = {3, 9, t2, 4}
Рассмотрим пример еще одной ошибки.
Пример 8.3.
Определим двумя разными способами производную одной и той же функции:
f3[x_] = D[x^2, x]и f4[x_] := D[x^2, x]
Обращения f3[x] и f4[x] дают очевидный ответ: 2 x. Аналогично на вопрос f3[5] получаем правильный ответ: 10. В то же время попытка найти значение производной в фиксированной точке с помощью функции f4 приводит к ошибке. Например, на вопрос f4[5] получаем замечание, что 5 не является переменной и ответ: d525. Получается, что Математика здесь действует формально с учетом приоритета операций: сначала подставляет 5 вместо x, а после этого оказывается, что дифференцирование невозможно.
В рассматриваемом примере для исключения ошибки можно положить f4[x_]:=f3[x].
Аналогичная ошибка возникает, если отложенное присваивание используется для вычисления первообразной.
Пример 8.4.
Найдем значение экспоненты с помощью разложения в ряд Маклорена:
ex = 1 + x + x2/2! + x^3/3! + …
Для этого напишем простую программу:
ex[x_, n_] := ( For[ u=1; e=1; k=1, k<n, u=u*x/k; e=e+u; k++]; e )
Программа состоит из двух операторов, заключенных в объединяющие круглые скобки: оператора цикла и оператора, предписывающего вывести значение найденной суммы – значение переменной “e”. Члены ряда Маклорена вычисляются последовательно с помощью рекурсивной формулы:
uk = uk-1*x/k.
Число n задает количество суммируемых членов ряда. Если мы теперь напишем вопрос:
In[ ] :=ex[x, 5], то получим ответ: Out[ ] = 1 + x + x2/2 + x3/6 + x4/24,
на вопрос In[ ] :=ex[1, 9]//N получим ответ: Out[ ] = 2.71828 и т.д. В программе используется оператор задержанного присваивания. Если вместо этого использовать операцию немедленного присваивания, возникнет ошибка: невозможно вычислить сумму с неопределенным числом слагаемых.
Итак, подытожим сказанное. В тех случаях, когда перед подстановкой численных значений необходимо сначала произвести аналитические преобразования, например, при вычислении производных, следует использовать оператор немедленного присваивания. При этом во избежание возможных ошибок следует непосредственно перед присваиванием очистить все идентификаторы, используемые в качестве аргументов функции. Если немедленное присваивание невозможно, например, не определено количество членов формулы, следует использовать операцию задержанного присваивания.
8.3. Альтернативные определения функций
Рассмотрим альтернативные определения функций.
Пример 8.5.
Определим двумя способами функцию, принимающую значение x2 при x>0 и значение –x2
при x<=0.
1). Определение из двух частей: In[]:= g[x_/;x>0] = x^2; g[x_/; x<=0] = -x^2;
2). Определение с использованием условной функции If: In[]:= g1[x_] = If[x>=0, x^2, -x^2];
Пример 8.6.
Определим функцию: f[x_]=Sin[x]/x;Если ограничиться этим описанием, то функция оказывается неопределенной в точке x=0, так как в пакете Математика пределы автоматически не вычисляются. Поэтому в точке x=0 функцию требуется доопределить:
In[]:=g[x_]=If[x==0, 1, Sin[x]/x];
Данная функция определена на всей оси x.
Если описание функции включает более одной альтернативы, то для ее определения удобнее пользоваться функцией Which. Синтаксис этой функции:
Which[test1, value1, test2, value2, …] – поочередно проверяется выполнение условий test; возвращается значение value, соответствующее первому истинному условию. Можно ввести значение по умолчанию, сделав последнее условие истинным.
Пример 8.7. Определим “кусочно линейную” функцию:
g3[x_] = Which[x < -1, -1, -1 <= x <= 1, x, x > 1, 1];
В этом определении может быть использован любой из операторов присваивания.