Работа с выражениями
Одним из важнейших понятий системы Mathematica является математическое выражение, или просто выражение — ехрг (от английского слова expression). Работа с математическими выражениями в символьном виде — основа основ символьной математики.
Выражение может быть представлено в общепринятом виде (как математическая формула или ее часть) с помощью операторов, например, а* (х + у + z) или х ^ у, оно может задавать и некоторую функцию f [х, у,...] или их комбинацию. Наряду с такой формой существует так называемая полная форма представления выражений, при которой основные арифметические операции задаются не операторами, а только соответствующими функциями. Ее примеры даны ниже.
Выражение ехрг | Полная форма ехрг | Комментарий |
х + у + z | Plus [х, у, z] | Сложение |
х у z | Times [x, у, z] | Умножение |
х^n | Power [x,n] | Возведение в степень |
{a,b,c} | List [a,b, c] | Создание списка |
a->b | Rule [a,b] | Подстановка |
a=b | Set [a,b] | Присваивание |
Для вывода выражения ехрг в полной форме используется функция FullForm [ехрг ]. Примеры перевода выражений в полную форму:
1+х^2+(у+г)^2+2
3 + х2 + (y+z)2
FullForm[%]
Plus[3, Power[x, 2], Power[Plus[у, z] , 2]]
Integrate[a*Sin[b*x]*Exp[-c*x],x]
a [(be-cxCos[bx])/{-ib + c) (ib + c)-( ce+cxSin[bx]) \(-ib + c) (ib + c) ]
FullForm[%]
Times[a, Plus[Times[-1, b, Power[Plus[Times[Complex[0, -1], b], c] , -1], Power[Plus[Times[Complex[0, 1], b], c] , -1], Power[E, Times[-l, c, x] ] , Cos[Times[b, x] ] ] , Times[-1, c, Power[Plus[Times[Complex[0, -1], b] , c], -1] , Power[Plus[Times[Complex[0, 1] , b], c] , -1] , Power[E, Times[-1, c, x] ] , Sin[Times[b, x]]]]]
Для определения типа выражения служит функция Head [ехрr ]. Применительно к числовым выражениям она возвращает тип результата, как показано в приводимых ниже примерах.
Ввод (In) |
Вывод (Out) |
1+2+3 | 6 |
Head[%] | Integer |
Head[123/12345] | Rational |
Head[2*0.25] | Real |
Следующие примеры поясняют действие функции Head для символьных выражений:
Другая пара примеров показывает применение Head в списках с разнородными выражениями:
{Head[l + 2], Head[аЬ] , Head[ 5/7], Headfl + 3i], Head[e2]}
{Integer, Times, Rational, Complex, Power}
Head/@{l, 1/3, 2.1, 2 + 3i, x, f [x] , {1, 2, 3}, a+b, a/b}
{Integer, Rational, Real, Complex, Symbol, f, List, Plus, Times}
Обратите внимание на второй пример — в нем функция Head применяется к каждому выражению списка, что дает более компактную запись.
Основные формы записи выражений
Возможны четыре основные формы записи выражений:
Далее приведены примеры применения этих форм.
Ввод (In) | Вывод (Out) |
F[x_] = 2*х^2 | 2X 2 |
F[a] | 2a 2 |
a//F | 2 a 2 |
f [x_, y_] = х^ 2 + у^2 | y 2 +x 2 |
f[a,b] | a 2 +b 2 |
a-f-b | a 2 + b 2 |
Можно использовать ту или иную форму выражений в зависимости от класса решаемых математических задач.
Части выражений и работа с ними
Сложные выражения состоят из частей, которые могут интерпретироваться различным образом.
Тип части | Зависимость | Пример |
Function | От аргументов или параметров | Ехр[х], f [х,у] |
Command | От аргументов или параметров | Expand [ (х-1) ^2] |
Operator | От операндов | x + y+z, a = b |
Head | От элементов | {a,b,c} |
Object type | От контекста | RGBColor [r,g,b] |
Работа с частями выражений напоминает работу со списками. Для выделения любой заданной части выражения используются функция Part или двойные квадратные скобки;
Ввод (In) | Вывод (Out) |
f :=а + b*х^2 + с*х^3 | |
Part[f, 3] |
ex 3 |
Part[f, 2] | bx 2 |
f[[1]] | a |
f[[3]] | ex 3 |
f[[-1]] | ex 3 |
Нередко выражения рассматриваются как возможные значения переменных. В этом случае используются операторы присваивания переменным заданных значений. Mathematica имеет два типа присваивания — с помощью символов «: =» и с помощью символа «=». Они различаются временем вычисления выражения, следующего за этими символами. Знак «: =» используется для задержки присваивания до вычисления правой части, например:
f[x_] := % + 2 х
Вывода здесь нет. Продолжим наш эксперимент: 1 + у^2
1 + y2
g[х_] = % + 2 х
1 + 2 х + у2
Теперь вывод есть, так как % (ссылка на предыдущий результат) определена в виде выражения 1 + у^2 и при задании [х_] использован оператор немедленного присваивания. Далее:
2 + z
2 + z
{f[a],g[a]>
{2 + 2a+z, 1 + 2 а + у2}
Следующие функции возвращают особые части выражения:
Ниже приводятся примеры применения этих функций.
Ввод (In) | Вывод (Out) |
Denominator [ (х + 1) / (х ^ 2 + 2*х + 3) ] | 3+ 2х+ х^2 |
ехрг = а * b + с - d | ab+ с- d |
First [expr] | ab |
Last [expr] | -d |
Rest [expr] | c-d |
Работа с выражениями, умение их преобразовывать и выделять нужные фрагменты является важнейшей частью культуры символьных преобразований.
Иногда возникает необходимость в удалении части выражения. Для этого используются следующие функции:
Следующие примеры иллюстрируют применение этих функций.
Ввод (In) | Вывод (Out) |
ехрr = а * b + с - d | ab + с - d |
Delete [expr, 1] | c-d |
Delete [expr, 3] | ab+ с |
Delete [expr, {{!}, {3}}] | с |
DeleteCases [expr, а*b] | c-d |
DeleteCases [expr, с, 1] | ab-d |
Обратите внимание на то, что в общем случае выражения могут быть многоуровневыми. Уровень задается спецификацией levspec.
Другие манипуляции с выражениями
В процессе преобразования выражений с ними возможны и иные манипуляции. Наиболее важные из них выполняются следующими функциями:
Действие этих функций достаточно очевидно и поясняется следующими примерами.
Ввод (In) | Вывод (Out) |
Append [a + с, b] | a+ b+ с |
х = {а, b, с} | {a, b, c} |
AppendTo [ х , 15 ] | {a, b, c, 15} |
x | {a, b, c, 15} |
Apply[f, а А 2 + b ^ 2, 2] | f[a, 2] +f[b, 2] |
Cancel [(z-1) ^ 2/ (z - 1) ] | -1 + Z |
Cases[{a, 3.5, 2, 5, "HELLO"}, _Integer] | {2, 5} |
Exp[N[-лI]] | -1. - 1. 22461 x 10 -16 I |
Chop[%] | -1. |
Ехр[N[-лI]] | -1. - 1. 22461 x10 16 I |
Chop[%, 1*10^-10] | -1. |
Replace[s ^ 2, s ^ 2 -> a] | a |
s^2 /. s -> a | a 2 |
Заинтересованному в таких манипуляциях читателю рекомендуется просмотреть множество примеров, имеющихся в справочной системе Mathematica, и, разумеется, попробовать свои собственные примеры.
При создании программного обеспечения на языке Mathematica, а иногда и в ходе диалоговой работы с системой необходим контроль за некоторыми свойствами выражений. Следующие функции обеспечивают такой контроль:
Следующие примеры показывают действие этих функций.
Ввод (In) | Вывод (Out) |
AtomQ [{a | False |
AtomQ[2+3/4] | True |
AtomQ [Sin] | True |
FreeQ[a*x^b,a] | False |
FreeQ [ a*x ^ b+c , 1 ] | True |
FreeQ [a*x^b+c, 1,1] | True |
FreeQ[a*x^b+c,b,2]}] | True |
Приложение имени функции к выражению или его части
Функции в системе Mathematica характеризуются именем (обобщенно — f) и выражением ехрг, задающим функциональную зависимость. Обычно функция в ответ на обращение к ней возвращает значение выражения — численное или символьное. Однако в системе Mathematica понятие функции значительно расширено, и она может возвращать любой объект, в том числе графический или звуковой. Можно сказать, что входной язык общения с системой Mathematica основан на принципах функционального программирования с применением полных форм представления выражений.
Следующие функции позволяют прикладывать имя функции к выражению или к частям выражения:
Приведем примеры действия этих функций.
Ввод (In) | Вывод (Out) |
Apply [f, {a, b, x}] | f [a, b, x] |
Nest[f , x, 3] | f[f[f[x]]] |
s[x_, y_, z_] := x + y + b | |
N[Apply[s, {1, 2, a}]] | 3. + b |
Map[f, {а, Ь, с}] | {f[a], f[b], f[c]} |
MapAll [f, a*x + b] | f[f[b] + f[f[a] f[x]]] |
MapAll [f, {а, Ь, с}] | f[{f[a], f[b] , f[c]}] |
Из описания указанных функций вытекает, что они наряду с полной формой могут задаваться укороченной формой.
Укороченная форма | Полная форма |
f @ ехрг | f [expr] |
f @@ ехрг | Apply [f, expr] |
f /@ ехрг | Map[f, expr] |
f //@ ехрг | MapAll [f, expr] |
Смысл укороченных выражений очевиден. Приведем несколько примеров их использования.
Ввод (In) |
Вывод (Out) |
f@{a, b, с} | f[{a, b, c}] |
f@@{a, b, c} | f [a, b, c] |
f /@{a, b, c} | {f[a], f[b], f[c]} |
f//@{a, b, x} | f[{f[a], f[b], f[x]}] |
Укороченная форма функций может оказаться полезной для сокращения записи алгоритмов и программ.
Выделения и подстановки в функциях
Функция Slot[n], или, в укороченной форме, #n, представляет n-й аргумент функции. Это иллюстрируют следующие примеры.
Ввод (In) | Вывод (Out) |
(5* Slot [1] + Slot [2] *Slot[3] A 2) &[a, b, с] | 5a+bc 2 |
#1 A #2 S[a, b] | a b |
Объект # эквивалентен #1, a #0 — заголовку абстрактной функции. Таким образом, F[#.#2]&F[a,b] эквивалентно F[a,b].
Функция SlotSequence [n], или, в укороченной форме, ##n, где п = 1, 2, ..., представляет порядок применения формальных аргументов к абстрактной функции. Таким образом, объект ##n определяет последовательность аргументов, начиная с n-го.
Ввод (In) | Вывод (Out) |
(Times[5, ##2] +Times[##2, ##3 A 2]) &[а, b, с] | Sbobc 3 |
Представленные средства обеспечивают работу с функциями на абстрактном уровне.
Интересные возможности связаны с использованием подстановок при определении функций. Система допускает использование подстановок в виде f [x] = value и f[x_] = value.
Поясним это несколькими примерами.
Ввод (In) | Вывод (Out) |
f[x] =u | u |
f[x] +f[y] | u+f ty] |
f [x_] = х^2 | x 2 |
f[x] +f[y] | u+y 2 |
Clear [f] | |
f[x]+f[y] | f [x] + f [y] |
Как нетрудно заметить из этих примеров, подстановки в функциях могут существенно изменить исходную функциональную зависимость. А потому важной областью их применения является модификация функций.
Использование подстановок при определении функций позволяет легко реализовывать рекуррентные алгоритмы, то есть алгоритмы, при которых очередной шаг вычислений основан на определенном преобразовании предшествующих шагов. Примером может служить задание функции вычисления факториала fact [n], представленное ниже.
Операция | Комментарий |
fact[n_] :=n*fact[n-l] | Задана рекурсивная функция факториала |
fact[l]=l | Выполнена инициализация функции |
1 | |
fact[3] | Вычислено значение 3! |
6 | |
fact[10] | Вычислено значение 10! |
3628800 | |
?fact[l] | Выполнена проверка определения функции |
Global ' fact | |
fact[l] = 1 | |
fact[n_] :=nfact[n-l] |
Обратите внимание на использование знака вопроса перед именем функции в конце примера, показанного выше. Оно позволяет вывести текст декларации (определения) функции. После объявления функция может быть использована в последующих ячейках документа.
Дополнительные примеры работы с функциями
Приведем еще ряд примеров действия функций Apply, Map и Nest.
Ввод (In) | Вывод (Out) |
Nest[f ,x,3] | f [f[f [X]]] |
Apply[f,{a,b,c}] | f[a, b, c] |
s [x_,y_, z_] : =х+у+b | |
N[Apply[s,{l,2,a}]] | 3. + b |
Map[f,{a,b,c}] | {f [a], f [b], f [c] } |
N[Map[Exp, {1,2,3}]] | {2.71828, 7.38906, 20.0855} |
Map[f ,1+2+c] | f[3] + f[c] |
m={{a,b},{c,d}} | {{a, b}, {c, d}} |
Map[f,m] | {f [{a, b}], f [{c, d}] } |
take2[list_] :=Take[list,2] | |
Map[take2,{{a,b,c},{c,a,b),{c,c,a}}] | {{a, b}, {c, a}, {c, c}} |
Большинство описанных операций для работы с функциями могут использоваться и при работе со списками. Порой это резко упрощает запись алгоритмов вы-числений для данных, представленных списками, поскольку дает общее определение функций для произвольного числа их параметров. Примерами могут служить определения следующих статистических функций.
Вычисление среднего для элементов списка:
Mean[list_] := Apply[Plus, list] / Length[list] /;
VectorQ[list] && Length[list] > 0
General: :spell! : Possible spelling error: new
symbol name "list" is similar to existing symbol "List".
Вычисление среднего геометрического для списка:
GeometricMean[list_] : = Apply
[Times, list"4 (I/Length [list])] /;
VectorQ[list] && Length[list] > 0
Вычисление гармонического среднего для списка:
HarmonicMean[list_] := Length[list]
/ Apply[Plus, I/list] /;
VectorQ[list] && Length[list] > 0
Обратите внимание на то, что при задании первой функции Mathematica предупреждает о том, что введенный идентификатор list подозрительно напоминает зарезервированный идентификатор List. Все приведенные выше функции не имеют смысла, если список пустой. Поэтому в них введен контроль за такой ситуацией.
Теперь можно выполнить расчеты по этим формулам.
Ввод (In) | Вывод (Out) |
data={l,2,3,4} | {1, 2, 3, 4} |
Mean [data] | 5/2 |
GeometricMean [data] | 2 3/4 3 l/4 |
N[%] | 2.21336 |
HarmonicMean [data] | 48/25 |
Большое число операций для работы с функциями полезно при организации функционального программирования, а также при создании пакетов расширения системы для выполнения символьных преобразований и расчетов. Разумеется, это разумно делать профессионалам-математикам, а не обычным пользователям. Последних, скорее всего, более чем удовлетворит уже имеющийся в системе набор таких операций и функций.
Инверсными функциями называют функции, полученные в результате обращения заданных функций. Например, для функции Sin [x] инверсной будет ArcSin [х] и т. д. Следующие функции обеспечивают представление инверсных функций:
Следующие примеры иллюстрируют работу с этими функциями.
Ввод (In) | Вывод (Out) |
InverseFunction [Sin] | ArcSin |
%[х] | ArcSin[x] |
Composition [ f , g , h] | Ccrrposition[f , g, h] |
InverseFunction [Composition [% , q] ] | Corpositiont [q- 1 , h- 1 , g- 1 ,f- 1] |
Обратите внимание на то, что в этих примерах фигурируют заголовки функций — например, для получения инверсной функции от Sin [х] следует использовать
Sin в качестве аргумента f функции InverseFunction [f].
Задание математических отношений
Символьные преобразования- при всей их кажущейся таинственности осуществляются по определенным, хотя и весьма многочисленным, а потому для нас запутанным, правилам. Основные из них давно известны из математики и описаны в многочисленных справочниках и монографиях. Они записаны в ядре системы и вызываются из него при создании условий, необходимых для выполнения того или иного преобразования. Если этих условий нет, исходное выражение просто повторяется. А если обнаружена явная ошибка в преобразованиях, то о ее сути выводится соответствующее сообщение. При ситуациях, лишь близких к ошибочным, выводится предупреждающее сообщение, и вычисления продолжаются.
Однако математика и использующие ее науки непрерывно развиваются. Появляются все новые и новые правила преобразований. Пользователь-математик может пожелать изменить встроенные правила преобразований — например, для создания новых разделов математики, базирующихся на каких-либо новых представлениях. Блестящий пример этого — теория относительности Эйнштейна.
Таким образом, возникает необходимость расширения математических символьных систем и обучения их новым правилам математических преобразований. Система Mathematica имеет и такие возможности. Поясним на простых примерах, как это делается.
В математике можно найти множество примеров математических отношений. Например, хорошо известно такое отношение для логарифма и экспоненциальной функции:
log(exp(x)) = х.
Не обременяя себя поиском действительно новых закономерностей (порой на это может не хватить жизни, да и везет не каждому ученому), зададим приведенную закономерность для введенных по-новому функций log и ехр. Центральным моментом тут является введение новых имен функций, которые начинаются с малых букв, а не с больших, как у встроенных функций Log и Ехр. Поэтому система воспринимает log и ехр как новые функции.
Итак, вводим «новую» закономерность следующим образом:
log[exp[x_]] :=x
General::spelll : Possible spelling error:
new symbol name "log" is similar to existing symbol "Log".
General::spelll : Possible spelling error:
new symbol name "exp" is similar to existing symbol "Exp".
Система на всякий случай сообщает о рискованности эксперимента — символы log и ехр похожи на зарезервированные имена функций Log и Ехр. Проигнорировав это предупреждение, проверим введенную закономерность в работе.
Ввод (In) | Вывод (Out) |
log [exp [15]] | 15 |
1оg[ехр[у^2+1]] | 1+y 2 |
Итак, наша «новая» закономерность работает. Можно ввести, скажем, и такое известное отношение:
log[x_^n_] :=n*log[x]
Проверим, какие отношения заданы нами для функции log:
?log
Global' log
log[exp[x_] ] : = x
log[x_n-] := n log[x]
Проверим введенные правила, например, так:
5lоg[[1+х]^5]
5lоg[1+х]
Рассмотрим еще пару примеров задания «новых» математических правил. В первом примере задано правило — логарифм произведения равен сумме логарифмов сомножителей:
log[x_*y_] := log[x] +log[y]
Любопытно, что эта закономерность действует при любом числе сомножителей:
log[a*b*c*d*e]
log [a] + log[b] + log[c] + log[d] + log[e]
Второй пример иллюстрирует задание объекта, ассоциированного со списком:
а /: а[х_] +а[у_] :=а[х + у]
а[х] + а[у] +a[z]
а[х+ у+ z]
Введенные здесь обозначения х_, у_ и n_ представляют собой образцы, на место которых могут подставляться произвольные выражения. Позже мы обсудим применение образцов более детально.
Описанные выше примеры наглядно демонстрируют возможности выполнения так называемого математического программирования, в основе которого лежит задание определенных математических соотношений между математическими понятиями, прежде всего такими, как функции.