Основы работы в системе компьютерной алгебры Mathematica

Функциональное программирование

Разбить на страницы
Показывать лекцию целиком

Цель лекции: познакомиться с основами реализации функционального стиля программирования в языке программирования Mathematica

9.0. Введение

Хотя Mathematica предназначена, по большей части, для решения математических задач без применения программистских навыков, и всёже она позволяет при необходимости использовать программирование в случаях, когда имеющихся встроенных функций оказывается недостаточно, или в случае, если требуется создать собственный оригинальный алгоритм для их решения. Фактически, Mathematica является ориентированным на математические расчеты языком программирования сверхвысокого уровня. Следует подчеркнуть, что речь идет не о языке реализации самой системы Mathematica (коим является C++), а о системе как языке программирования самой по себе. Mathematica является типичным интерпретатором и не предназначена для создания исполняемых файлов.

Как отмечает Е. М. Воробьёв [1, с. 125], поскольку Mathematica является языком программирования, то написание программ — наиболее логичная форма взаимодействия с ней. Mathematica позволяет программировать в трех стилях:

  • функциональном;
  • процедурном;
  • стиле, основанном на создании правил преобразований
  • В данной лекции мы рассмотрим функциональный стиль программирования, за основу взяв книги Е. М. Воробьёва [1], П. Веллина и др. [14] и С. Мангано [11].

    В Mathematica имеется более 1000 встроенных функций. Хотя каждая функция осуществляет достаточно узкоспециализированные операций, в своей совокупности они позволяют выполнять широчайший набор действий. Конкретные действия выполняются определённой комбинацией встроенных функций, и если то или иное действие достаточно часто встречается при проведении расчётов, было бы удобно применение комбинации функций заменить применением всего-навсего одной функции. Это стремление, по сути, лежит в основе функционального программирования.

    9.1. Функции для управления выражениями

    В предыдущих лекциях мы научились применять встроенные функции Mathematica к выражениям, пользуясь их внутренним представлением в виде h[e1,e2,…] или операторной формой (например, операторы сложения " + " и вычитания " - "). Однако существует ещё один менее наглядный, менее интуитивный, но крайне необходимый для функционального программирования способ управления выражениями. Заключается он в применении ряда специальных функций, инициирующих действие других функций.

    9.1.1. Функция Map

    Функция Map[func,expr] применяет функцию func к каждому элементу выражения expr, если оно является списком, или непосредственно к выражению expr в остальных случаях. В примере In[1] на рис. 9.1 при помощи функции Map мы применили встроенную функцию извлечения квадратного корня Sqrt к элементам списка {4,9/16,7,a}. В примере In[2] первым аргументом Map мы указали ранее не определённое в качестве функции выражение f, но Mathematica самостоятельно расценила его как функцию и также применила к элементам заданного списка.

    К функции Map можно обратиться в постфиксной форме: для того чтобы применить функцию func к выражению expr, следует записать следующее выражение func/@expr. В примере In[3] на рис. 9.1 мы выполняем то же действие, что и в In[2], но пользуемся постфиксной формой записи функции Map.

    При помощи Map к выражениям можно также применять и функции, заданные пользователем. Задавать функции самостоятельно мы научимся чуть позже, а сейчас на рис. 9.1 приведём лишь пример In[4] задания функции, удваивающей выражение, указанное в качестве её аргумента (назовём функцию Double), а в примере In[5] применим её к нашему списку при помощи функции Map.

    (рис 9.1) Функция Map при работе с одномерными списками

    Если вторым аргументом expr в Map[func,expr] является вложенный список, то указанная в первом аргументе функция func будет применена к элементам списка первого уровня. В примере In[1] на рис. 9.2 мы задали трёхуровневый список, к которому в примере In[2] применили неопределённую ранее функцию f. Для того чтобы применить func только к элементам внутреннего списка конкретного уровня, следует задать третий аргумент, и тогда функция Map будет выглядеть следующим образом: Map[func,expr,listlevel]. Если выражение listlevel задано в фигурных скобках, {listlevel}, то функция func будет применена только к элементам внутреннего списка уровня listlevel. В примерах In[3] и In[4] мы применяем функцию f к элементам внутренних списков второго и третьего уровней, соответственно.

    Если третий аргумент функции Map задан в виде списка {listlevelst,listlevelfin}, то функция func применяется к элементам внутренних списков от уровня listlevelst до listlevelfin. В примере In[5] на рис. 9.2 мы применяем функцию f к элементам внутренних списков от второго уровня до третьего.

    Если listlevel задан без фигурных скобок, то функция func будет применена к элементам всех уровней от первого до listlevel включительно. В примере In[6] мы применяем f к элементам списков до второго уровня.

    Подробней о функции Map см. книги Е. М. Воробьёва [1, с. 118–120] и П. Веллина и др. [14, с. 78–79].

    (рис 9.2) Функция Map при работе с вложенными списками

    9.1.2. Функция Apply

    Если функция Map применяет некоторую функцию к уже имеющемуся выражению, то функция Apply вносит изменения в само это выражение. Заданная в виде Apply[func,expr] функция изменяет заголовок заданного вторым аргументом выражения expr на первый аргумент func. В примере In[1] на рис. 9.3 мы заменили заголовок s выражения s[a,b,c] заголовком t при помощи функции Apply.

    Функция Apply также может быть задана в постфиксной форме: func@@expr (пример In[2] на рис. 9.3).

    Если вторым аргументом функции Apply указан список, то первый аргумент заменит заголовок List, а все элементы прежнего списка станут аргументами новой функции; при этом совершенно не имеет значения, задан ли список во внутреннем представлении или в традиционном при помощи фигурных скобок. В примерах In[3] и In[4] на рис. 9.3 мы опробовали действие функции Apply на списках, заданных обоими способами: Одинаковые выходные данные Out[3] и Out[4] говорят о полной эквивалентности обоих вариантов применения Apply.

    (рис 9.3) Замена заголовка выражения при помощи функции Apply

    Функцию Apply можно применять и к вложенным спискам, при этом точно таким же образом, что и в функции Map, можно выбирать, к какому уровню списка её следует применять. Единственное, что отличает эти функции в данном вопросе, — по умолчанию функция Map применяется к уровню 1 списка, а функция Apply — к уровню 0, на котором находится заголовок списка. На рисунке 9.4 мы проиллюстрировали действие функции Apply на вложенный список, взяв за основу примеры на рис. 9.2.

    Подробней о функции Apply см. книги Е. М. Воробьёва [1, с. 120] и П. Веллина и др. [14, с. 82–84].

    (рис 9.4) Функция Apply при работе с вложенными списками

    9.1.3. Функции MapThread и Thread

    Рассмотренная ранее функция Map позволяет применить функцию одного аргумента к элементам некоторого выражения. Если к элементам выражения необходимо применить функцию нескольких аргументов, то применяется MapThread. Функция задаётся в виде MapThread[func,{{a1,a2,…},{Mb1,b2,…},…}], где func также представляет собой функцию, которую следует применить к выражению во втором аргументе; второй аргумент представляет собой вложенный список, при этом внутренние списки на каждом из уровней должны иметь одинаковое число элементов. Результатом применения функции MapThread будет список выражений {func[a1,b1,...],func[a2,b2,...],...}. В примере In[1] на рис. 9.5 при помощи функции MapThread мы применили некоторую неопределенную функцию u к двухуровневому вложенному списку, содержащему три внутренних списка с одинаковым числом элементов. В примере In[2] на том же рисунке в качестве второго аргумента мы попытались задать вложенный список, внутренние списки которого имеют разную длину: результатом вычислений оказалось сообщение об ошибке и дублирование выражения ячейки In[2] в ячейке Out[2].

    Функция MapThread может быть применена и к вложенным спискам, содержащим один элемент — пример In[3] на рис. 9.5.

    Если вложенный список во втором аргументе содержит несколько уровней, то номер уровня, к которому следует применить функцию func, можно задать третьим аргументом. Так в примере In[4] на рис. 9.5 мы применяем некоторую неопределённую функцию w к трёхуровневому списку без указания дополнительного аргумента, а в примере In[5] третьим аргументом мы указываем уровень 2, к которому должна быть применена функция w. Результаты вычислений Out[4] и Out[5] наглядно демонстрируют действие функции MapThread в обоих случаях.

    (рис 9.5) Работа с данными во вложенных списках при помощи функции MapThread

    По выполняемым действиям на функцию MapThread очень похожа функция Thread. В простейшем варианте Thread[func[args]] функция содержит всего одни аргумент и в процессе вычисления применяет func к каждому элементу выражения args.

    Если выражение args включает в себя списки, то функция func будет применена не к спискам в целом, а к их элементам. В примере In[1] на рис. 9.6 в ячейке входных данных мы задали некоторую неопределённую функцию f, аргументами которой являются списки {a,b,c} и {1,2,3}, а в примере In[2] мы воспользовались функцией Thread применительно к f. Сравним результаты в ячейках Out[1] и Out[2]: если в первом случае аргументом f были списки полностью, то во втором — отдельные элементы каждого из списков.

    Функция func не обязательно должна быть задана во внутреннем представлении. В следующих двух примерах мы записываем равенство, в правой и левой частях которого содержатся списки одинаковой длины, и применяем к этому равенству функцию Thread. Однако в примере In[3] равенство мы задаём во внутренней форме, при помощи функции Equal, а в примере In[4] — в инфиксной форме, при помощи оператора " == ". Поскольку полученные в ячейках Out[3] и Out[4] результаты вычислений одинаковы, напрашивается вывод, что форма задания функции func не влияет на действие функции Thread.

    (рис 9.6) Применение функции Thread к функциям, аргументами которых являются списки

    Если выражение args содержит выражения, отличные от списков, то функцию Thread необходимо задавать в виде Thread[func[args],h], где второй аргумент h указывает заголовок выражения, к элементам которого следует применять выражение func. При этом функция также необязательно должна быть задана во внутренней форме. Так в примере In[1] на рис. 9.7 мы задали функцию извлечения квадратного корня Sqrt из суммы $$x+y+z$$. В примере In[2] мы задали эту же сумму во внутренней форме при помощи функции с заголовком Plus и при помощи Thread применили заголовок Sqrt к элементам суммы. В примере In[3] мы задали сумму в операторной форме осуществили действие, аналогичное предыдущему примеру. Результаты вычислений в Out[1], Out[2] и Out[3] наглядно демонстрируют действие функции Thread на выражения.

    Подробней о функциях Thread и MapThread см. книги Е. М. Воробьёва [1, с. 120–122] и П. Веллина и др. [14, с. 79–80].

    (рис 9.7) Применение функции Thread к функциям, аргументы которых отличны от списков

    9.1.4. Атрибут функций Listable

    П. Веллин и др. [14, с. 80–82] наравне с функциями уделяет особое внимание такому атрибуту функций, как Listable. И нам следует познакомиться с этим атрибутом ближе.

    Множество встроенных функций Mathematica, имеющих один аргумент, если они содержат в качестве аргумента список, применяются в отдельности к каждому элементу списка. Одна из таких функций — функция нахождения синуса угла Sin. Так выражения Sin[{a,b,c}] и Map[Sin,{a,b,c}] при вычислении имеют одинаковые результаты (см. примеры In[1] и In[2] на рис. 9.8, соответственно).

    Множество встроенных функций Mathematica, имеющих два и более аргументов, если аргументы являются списками, применяются к соответствующим элементам списков в отдельности. Среди таких функций — Plus. Так одинаковыми являются результаты вычислений выражений Plus[{1,2,3},{x,y,z}] и MapThread[Plus,{{1,2,3},{x,y,z}}] (примеры In[3] и In[4] на рис. 9.8, соответственно).

    Функции, которые автоматически применяются не к целым спискам, а к элементам списков, обладают атрибутом Listable. Убедиться в наличии этого атрибута у функции можно при помощи функции Attributes, что мы и делаем в примерах In[5] и In[6] на рис. 9.8. для функций Sin и Plus.

    Если выражение не обладает атрибутом Listable, как применённая нами к некоторому списку {k,l,m,n} неопределённая функция g в примере In[7], то добавить его в список атрибутов выражения можно при помощи функции SetAttributes. В примере In[8] мы устанавливаем функции g атрибут Listable, а в In[9] вновь пытаемся вычислить выражение In[7]. Если в Out[7] функция g оказалась применена ко всему списку, то в In[9] — к каждому элементу списка в отдельности.

    Избавить выражение от любого присущего ему атрибута можно при помощи функции ClearAttributes. В примере In[10] мы удаляем из списка атрибутов функции Sin атрибут Listable: теперь, как иллюстрирует пример In[11], Sin будет применяться ко всему списку в целом.

    (рис 9.8) Атрибут Listable

    9.1.5. Функции Inner и Outer

    Функция Outer[func,list1,list2,...] применяет функцию func ко всевозможным комбинациям элементов списков list1,list2,... (пример In[1] на рис. 9.9), при этом совершенно необязательно, чтобы списки содержали одинаковое число элементов (пример In[2]). При помощи Outer можно, например, из нескольких списков сформировать один вложенный список со всевозможными сочетаниями элементов: для этого первым аргументом следует задать заголовок List (пример In[3]).

    Если аргументы list1,list2,... являются вложенными списками, то уровень вложенного списка, с которым следует проводить манипуляции, указывается в качестве дополнительно аргумента n, то есть, функция задаётся в виде Outer[func,list1,list2,...,n]. В примере In[4] на рис. 9.9 мы воспользовались функцией Outer без дополнительных аргументов для двух двухуровневых вложенных списков. По умолчанию функция func (в нашем примере это неопределённая функция g) применяется к комбинациям элементов самого низшего уровня вложенных списков (в нашем примере это уровень 2). В примере In[5] мы указали дополнительным аргументом, что оперировать следует элементами списков первого уровня.

    Кроме того, для разных списков можно задавать разные уровни элементов, участвующих в вычислениях. Для этого соответствующая функция задаётся в виде Outer[func,list1,list2,...,n1,n2,...]. В примере In[6] на рис. 9.9 мы указываем, что в первом списке следует оперировать элементами второго уровня, а во втором — первого.

    Глубина списков в аргументах функции Outer может быть разной. Так в примере In[7] на рис. 9.9 мы задали в качестве аргументов одноуровневый и трёхуровневый списки и получили результирующий четырёхуровневый список.

    (рис 9.9) Применение функции Outer для создания списков

    Функция Inner[func1,{a1,a2,…},{b1,b2,…},func2] в качестве результата генерирует выражение func2[func1[a1,b1],func1[a2,b2],...], то есть, сначала она формирует из двух списков аргументы для функции func1, а полученный результат задаёт в качестве аргументов функции func2. На рис. 9.10 мы привели абстрактный пример In[1] последовательного применения неопределённых функций f1 и f2 к спискам из четырёх элементов {w,x,y,z} и {10,20,30,40}, а также конкретный пример In[2] перемножения соответствующий элементов тех же списков (при помощи функции Times) и последующего сложения результатов (Plus).

    Подробней о функциях Inner и Outer см. книги Е. М. Воробьёва [1, с. 122–123] и П. Веллина и др. [14, с. 84].

    (рис 9.10) Применение функции Inner к спискам

    9.2. Суперпозиция функций

    9.2.1. Итерационные функции

    В математике композиция (суперпозиция) функций — это применение одной функции к результату другой. Частным случаем такой операции является применение к выражению одной и той же функции несколько раз.

    Функция Nest[func,exps,n] последовательно применяет n раз функцию func к выражению expr. Для того чтобы проследить за процессом получения конечного выражения, используется функция NestList с теми же аргументами. Эта функция генерирует список результатов, полученных при применении функции func к выражению expr на каждой итерации. На рис. 9.11 мы воспользовались функциями Nest и NestList для того, чтобы пять раз применить к выражению 16 неопределённую функцию g (примеры In[1] и In[2]) и функцию вычисления квадратного корня Sqrt (примеры In[3] и In[4]). Результаты вычислений в ячейках Out[1] и Out[3], как и ожидалось, совпадают с последними элементами списков в Out[2] и Out[4], соответственно.

    (рис 9.11) Использование итерационных функций Nest и NestList

    Функции Fold и FoldList работает с функциями двух аргументов. Заданная в виде Fold[func,var,{a1,a2,…}] функция на первой итерации применяет функцию func к аргументам var и a1, на второй итерации применяет func к результату предыдущей итерации и аргументу a2 и так далее, до последнего элемента в списке. На рис. 9.12 приведены примеры использования функций Fold и FoldList для неопределённой функции f (примеры In[1] и In[2]) и функции возведения в степень Power (примеры In[3]In[6]).

    Подробней об итерационных функциях см. книги Е. М. Воробьёва [1, с. 132–134] и П. Веллина и др. [14, с. 86–87].

    (рис 9.12) Использование итерационных функций Fold и FoldList

    9.2.2. Композиция нескольких функций

    В более широком смысле композиции функций в качестве аргумента функции можно использовать результат выполнения другой функции. В Mathematica последовательное применение к выражению нескольких функций называется вызовом вложенной функции (nested function call) (П. Веллин и др. [14, с. 89]). В примере на рис. 9.13 сначала мы применили к числу 2. функцию Log (пример In[1]), а затем к полученному результату последовательно — функции Sin и Sqrt (примеры In[2]In[3]). Однако все указанные операции можно совершить за одно действие. В примере In[4] мы вызываем вложенную функцию Sqrt[Sin[Log[2.]]]: сравнив выходные данные в Out[3] и Out[4], мы убедимся, что оба подхода применения к аргументу последовательно нескольких функций приводят к одинаковому результату.

    Когда мы пользуемся первым подходом, мы можем проследить за процессом вычисления, поскольку помимо конечного результата получаем промежуточные результаты действия каждой функции (в нашем случае это Out[1] и Out[2]). Для получения промежуточных результатов при использовании второго подхода используется функция Trace. В примере In[5] на рис. 9.13 мы применяем Trace к вложенной функции In[4].

    (рис 9.13) Вычисления с использованием вложенных функций

    Композицию функций можно осуществлять менее наглядным, но более традиционным для Mathematica способом — при помощи встроенной функции Composition. Указывается она в следующем виде: Composition[func1,func2,...][arg], где funci — функции в порядке, обратном порядку их применения к аргументу (или аргументам) arg, т.е., последней применяется функция func1, предпоследней — func2 и т.д. В примере In[1] на рис. 9.14 при помощи Composition мы осуществляем тот же набор действии, что и на рис. 9.13.

    Для получения промежуточных результатов выполнения вложенной функции при данном подходе используется функция ComposeList[{func1,func2,...},arg]. Однако, у ComposeList порядок функций в аргументе обратный в сравнении с Composition: функция func1 применяется первой — ср. примеры In[1] и In[2] на рис. 9.13. Также обратим внимание на то, что формат вывода результата данных функциями Trace и ComposeList различный: если в первом случае мы имеем вложенный список, содержащий как вычисляемое выражение в явном виде, так и результат вычислений, то во втором — одномерный список с результатами вычислений.

    Подробней о композиции функций см. книгу Е. М. Воробьёва [1, с. 134–136].

    (рис 9.14) Задание вложенных функций при помощи функции Composition и ComposeList

    9.3. Определение пользовательских функций

    9.3.1 Явные пользовательские функции

    Функции — это объекты, которые после проведения манипуляций с полученными на входе выражениями, возвращают однозначно соответствующие им выражения на выходе. При этом манипуляции могут быть как простыми, в одно действие, так и сложными, в несколько шагов. В принципе, функции Mathematica мы можем расценивать так же, как и математические функции.

    Теперь, пользуясь подходом Е. М. Воробьёва [1, с. 126–127], попробуем ответить на вопрос, каким же образом задаются пользовательские функции.

    Если мы попытаемся определить некоторую функцию, используя уже хорошо нам известную операцию присваивания (или отложенного присваивания), то потерпим фиаско. Проиллюстрируем это на рис. 9.15. В примере In[1] мы пытаемся определить функцию возведения в квадрат синуса угла, присваивая выражению SinSquare[x] значение (Sin[x])2. В примере In[2] мы пытаемся применить нашу функцию SinSquare к числу Pi, выражениям y и x. Как мы видим в Out[2], вычисление произошло только для аргумента x. Дело в том, что, хотя пользователь рассматривает x как переменную, которая может принимать любые значения из области определения функции, Mathematica расценивает x как фиксированный символ, неизменное выражение. Поэтому выражение SinSquare[x] следует рассматривать как единое целое, а не как выражение, состоящее из двух функционально обособленных элементов SinSquare и [x]. Точно так же едиными и отличными от SinSquare[x] выражениями являются SinSquare[Pi] и SinSquare[y]. Поэтому SinSquare[x] везде будет заменяться выражением Sin[x]2, а два других будут оставаться неизменными.

    (рис 9.15) Неудачная попытка определения пользовательской функции при помощи оператора присваивания

    Таким образом, наша задача сделать так, чтобы указываемый в квадратных скобках аргумент x функции с заголовком SinSquare воспринимался как переменная. Для этого необходимо вместе с x употребить подчёркивание " _ ", внутренняя форма которого — Blank[].

    Таким образом, общий вид пользовательской функции следующий:

    funcname[arg1_,arg2_,...]:= body

    Левая часть выражения содержит имя функции funcname, которое в обязательном порядке должно являться символом. За именем функции следуют квадратные скобки, содержащие один или несколько разделённых запятыми символьных выражений, каждое из которых оканчивается нижним подчёркиванием. Эти символы выступают в качестве имён аргументов функции.

    В правой части выражения содержится непосредственно определение функции, которое называется телом функции: оно может состоять из одного единственного выражения — так называемый "однострочник" (one-liner), или включать целый ряд выражений — составная функция (compound function). Имена аргументов, заданных в левой части, используются в правой части без нижнего подчёркивания.

    Пользовательские функции принято определять при помощи отложенного присваивания " := ". При этом выражение, которым задаётся пользовательская функция, ничего не генерирует, и не образуется ячейки Out; в момент задания тело функции не вычисляется. Вычисление происходит лишь в момент повторного обращения к функции.

    В примере In[1] на рис. 9.16 мы, наконец, задаём функцию возведения в квадрат синуса некоторого выражения. В In[2] на рис. 9.16 мы проводим проверку действия нашей функции на разные выражения (аналогично примеру In[2] на рис. 9.15) и по полученному результату Out[2] убеждаемся в её работоспособности.

    Подробней о задании пользовательских функций см. книги Е. М. Воробьёва [1, с. 126–129] и П. Веллина и др. [14, с. 88–89].

    (рис 9.16) Определение пользовательской функции

    9.3.2 Чистые и анонимные функции

    В предыдущем пункте мы научились самостоятельно определять функции: при этом все заданные таким образом функции в обязательном порядке являются именованными, то есть, имеют заголовки. Однако заголовки имеет смысл определять, если эти функции будут использоваться в дальнейшем тексте программы. Если же новая функция будет использована единожды или считанное число раз, "полезно работать с так называемыми чистыми функциями, которые используются только в момент их создания" (Е. М. Воробьёв [1, с. 129]). Для определения чистых функций служит выражение Function. В простейшей своей форме Function[x,body] задаёт функцию одной переменной x, причём в качестве переменной вместо x может использоваться любой символ: в использовании нижнего подчёркивания здесь необходимости нет. Выражение Function[{x1,x2,...},body] задаёт функцию нескольких переменных. Выражение body задаётся так же, как правая часть выражения, задающего пользовательскую функцию. В качестве примера In[1] на рис. 9.17 зададим чистую функцию возведения в квадрат синуса угла, аналогичную таковой в примере In[1] на рис. 9.16.

    Выражение, к которому следует применить заданную чистую функцию, необходимо указать в квадратных скобках сразу после выражения Function[...] (примеры In[2] и In[3] на рис. 9.17). Чистую функцию очень просто превратить в именованную, присвоив её выражение некоторому символу (примеры In[4]In[6] на рис. 9.17).

    (рис 9.17) Задание чистых функций

    Е. М. Воробьёв [1, с. 130] пишет: "Существует более компактная и удобная форма представления чистой функции, которую иногда называют анонимной функцией. В анонимных функциях вместо связанных переменных используются специальные выражения "Математики" с заголовком Slot, имеющие вид #, #1 и т.д. При этом для функций одной переменной используется #, а в функциях нескольких переменных первый аргумент обозначается #1, второй #2 и т.д. Кроме того, в конце анонимной функции ставится знак ". В примере In[1] на рис. 9.15 мы снова определяем функцию вычисления квадрата синуса угла, но делаем это посредством задания анонимной функции. Чтобы применить заданную функцию к некоторому выражению, следует также указать его в квадратных скобках сразу после определения функции (примеры In[2] и In[3] на рис. 9.18).

    На рис. 9.18 также приведён пример In[4] задания именованной анонимной функции двух переменных для вычисления суммы квадратов двух выражений, а также примеры In[5] и In[6] использования этой функции.

    (рис 9.18) Задание анонимных функций

    Использование анонимных функций оказывается наиболее эффективным в сочетании с другими функциями. В примере In[1] на рис. 9.19 зададим некоторый список list={1,7,3,5,6,4,3}. В примере In[2] при помощи функции Map выполним заданный набор действий (возведём во вторую степень и удвоим результат) с элементами списка list, причём набор действий определим при помощи анонимной функции.

    Очень полезными оказываются анонимные функции при работе со списками. Предназначенная для сортировки элементов списка функция Sort, с которой мы познакомились в третьей лекции настоящего курса, может иметь необязательный аргумент, задающий условие сортировки: он должен быть двухместным предикатом, и сортировка производится так, чтобы он принимал значение True (Е. М. Воробьёв [1, с. 131]). Этот аргумент может быть задан в форме анонимной функции. В примере In[3] на рис. 9.19 мы осуществляем сортировку элементов списка list по убыванию их абсолютного значения, причём условие сортировки задаём как анонимную функцию. В примере In[4] на рис. 9.19 при помощи функции Select мы выбираем только элементы, принадлежащие отрезку от -4 до 4, причём условие выбора мы также задаём в виде анонимной функции.

    Подробней о чистых и анонимных функциях см. книги Е. М. Воробьёва [1, с. 129–131] и П. Веллина и др. [14, с. 102–105].

    (рис 9.19) Использование анонимных функций в сочетании с другими функциями

    9.4. Вспомогательные функции

    9.4.1. Составные функции

    Как отмечает П. Веллин и др. [14, с. 96], левая часть составных функций задаётся так же, как и левая часть любых пользовательских функций. Правая часть содержит заключённую в круглые скобки последовательность разделённых точкой с запятой выражений:

    funcname[arg1_,arg2_,...]:=(expr1,expr2,...)

    Составные функции очень похоже на суперпозицию функций, однако аргументами одних выражений не обязательно должны быть результаты других входящих в составную функцию выражений. Когда подаётся команда на вычисление составной функции некоторого аргумента, то выражения, указанные в правой части, вычисляются по очереди, и результатом функции является результат вычисления последнего выражения.

    В примере In[1] на рис. 9.20 мы последовательно задаём функции возведения некоторого выражения в куб Cube, квадрат Sqr и удвоения выражения Double, и только затем определяем функцию Exprn, осуществляющую с аргументом $$x$$ следующий набор действий: $$x*(x^3+2*x)/(x^2)+1$$. В примере In[5] мы применяем функцию Exprn к числу 5.

    Теперь зададим составную функцию с тем же заголовком Exprn, выполняющую весь описанный набор действий. В In[6] на рис. 9.20 для наглядности мы очищаем выражения Cube, Sqr, Double и Exprn. В примере In[7] мы сразу же задаём функцию Exprn, обращение к которой даёт нам требуемый результат. Функции для получения промежуточных выражений мы включаем непосредственно в тело Exprn. В примере In[8] мы применяем вновь заданную нами функцию Exprn к числу 5: результаты в Out[5] и Out[8] полностью идентичны.

    (рис 9.20) Задание составных функций

    П. Веллин и др. [14, с. 96] акцентирует внимание на следующих моментах, связанных с составными функциями.

    Поскольку выражения в правой части вычисляются в заданном порядке, требуемые значения должны быть присвоены, а пользовательские функции определены до обращения к ним. Имена аргументов пользовательский функций должны быть отличными от имён аргументов составной функции (так в примере In[7] на рис. 9.20 аргументом составной функции Exprn мы выбрали символ n, а аргументами содержащихся в её теле пользовательских функций — символы x, y и z).

    Когда мы задаём составную функцию, вместе с ней (в её теле) мы также определяем другие пользовательские функции и значения переменных. Если мы удалим заданную нами составную функцию, например, при помощи Clear, то определённые в её теле пользовательские функции и значения переменных сохранятся. В связи с этим, при дальнейших расчётах могут возникнуть проблемы, если мы снова захотим воспользоваться переменными или выражениями, которые являются заголовками этих функций.

    В программистской практике является плохим тоном оставлять вспомогательные функции, в использовании которых однозначно не будет необходимости в дальнейших вычислениях.

    9.4.2. Локализация имён: функция Module

    П. Веллин и др. [14, с. 98] пишет, что для того, чтобы пользовательские функции, заданные в теле составной функции, были изолированы от остального текста программы, то есть, обращение к их заголовкам не приводило бы к выполнению какого-либо набора действий, к правой части составной функции применяется функция Module:

    funcname[arg1_,arg2_,...]:=Module[{name1,name2=val,...},exprs]

    Первый аргумент функции Module — список имён функций или переменных, которые мы хотим локализовать, то есть, сделать так, чтобы они рассматривались в заданном виде только в пределах функции funcname. Второй аргумент — exprs — включает все выражения, которые требуется задать.

    Проиллюстрируем действие функции Module в примерах на рис. 9.21.

    В примере In[1] мы вновь задали функцию Exprn точно таким же способом, что и в примере In[7] на рис. 9.20. В In[2] мы списком вывели результат действия на число 5 функций Exprn, Cube, Sqr и Double: в Out[2] мы получили список численных значений соответствующих функций. В примере In[3] мы очистили заголовок функции Exprn. В примере In[4] мы осуществили то же действие, что и в In[2]: в списке Out[4] элемент, соответствующий функции Exprn, принял неопределённое значение Exprn[5], остальные элементы так и остались числами.

    Для чистоты демонстрации в In[5] освободим все выражения, использованные ранее в качестве заголовков пользовательских функций.

    Теперь локализуем имя функции Cube в пределах функции Exprn. Для этого воспользуемся при задании Exprn функцией Module, указав первым её аргументом выражение Cube (пример In[6]). В In[7] мы снова вывели результат действия на число 5 вновь заданной функции Exprn, Cube, Sqr и Double: в списке Out[7] элемент, соответствующий функции Cube, не является числом, то есть, применённое вне пределов Exprn выражение Cube является просто символьным выражением. В In[8] мы очистили выражение Exprn, а в In[9] ещё раз вывели список: в Out[2] единственные элементы, обладающие численными значениями, соответствуют функциям Sqr и Double, поскольку только их мы не освободили от определённой ранее роли.

    (рис 9.21) Локализация имён выражений в пределах составной функции

    9.4.3. Локализация значений: функция Block

    Пусть при проведении расчётов мы пользуемся некоторым выражением, которому присвоено строго определённое значение. Если на каком-то этапе вычислений возникла необходимость произвести расчёт с другим значением этого выражения, а в дальнейших вычислениях использовать прежнее его значение, П. Веллин и др. [14, с. 99] предлагает использовать функцию Block. Заданная в виде Block[{ex1=val1,ex2=val2,...},exprs] она вычисляет выражения exps при заданных в первом аргументе значениях val1,val2,... выражений ex1,ex2,…, при этом значения val1,val2,... принимаются выражениями лишь в пределах функции Block.

    В примере In[1] на рис. 9.22 мы присвоили выражению h значение 10. В примере In[2] мы указали неопределённую функцию f[x], в которой x равняется h^2, и вычисления мы проводили при h равном 5. При этом мы воспользовались функцией Block для того, чтобы h равнялось 5 только при вычислении функции f[x]. В примере In[3] мы убеждаемся в том, что вне функции Block выражение h по-прежнему имеет значение 10.

    (рис 9.22) Локализация значений выражений в пределах некоторой функции

    9.4.4. Локализация констант: функция With

    Согласно П. Веллину и др. [14, с. 99], функция With[{ex1=val1,ex2=val2,...},exprs] вместо входящих в exprs выражений ex1,ex2,... подставляет величины val1,val2,..., при этом за пределами функции With выражения ex1,ex2,... принимают отличные от val1,val2,... значения, заданные ранее.

    В примере In[1] на рис. 9.23 мы присвоили выражению a значение 10. В примере In[2] мы определили пользовательскую функцию, в левой части которой в качестве переменной значится символ x, а в правой — выражение, включающее только символ a: при этом при помощи функции With символ a мы заменили выражением x^2. В примере In[3] мы применили определённую нами в In[2] функцию к величине 2. В In[4] мы присвоили a новое значение 5, однако, на результате применения функции f это ни коим образом не отразилось — см. пример In[5].

    (рис 9.23) Локализация констант в пределах пользовательской функции

    Ключевые термины

    Анонимной функцией называется компактная форма задания чистой функции, в которой вместо символов переменных используются специальные выражения Mathematica с заголовком Slot.

    Вызовом вложенной функции называется последовательное применение к выражению нескольких функций.

    Композиция (суперпозиция) функций — это применение одной функции к результату другой.

    Функции — это объекты, которые после проведения манипуляций с полученными на входе выражениями, возвращают однозначно соответствующие им выражения на выходе.

    Чистыми называются функции, которые используются только в момент их создания.

    Краткие итоги

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

    Вопросы

  • Какими способами Mathematica позволяет применить заданную функцию func к некоторому выражению expr?
  • Чем отличаются действия, оказываемые на выражения функциями Map и Apply? Какова полная и постфиксная форма этих функций?
  • Какие функции Mathematica позволяют применять к элементам выражения функции нескольких аргументов? В каком виде задаются их аргументы?
  • Что вносит атрибут Listable в действия, которые выполняют функции?
  • Для каких целей используются функции Inner и Outer? Какие аргументы они имеют?
  • Что такое суперпозиция функций? Каким образом Mathematica позволяет осуществлять суперпозицию функций?
  • В каком виде в Mathematica задаются явные пользовательские функции?
  • Что такое чистые функции? Каким образом они определяются в Mathematica?
  • Что такое анонимные функции? Каким образом они определяются в Mathematica?
  • Каким образом в Mathematica задаются составные функции? В чём состоит отличие составной функции от суперпозиции функций?
  • Для каких целей в Mathematica применяются локализация имён, значений, констант при задании пользовательских функций? Посредством каких функций они осуществляются?
  • Упражнения

  • Используя исходный вложенный список l1={{1,7},{8,3},{9,5},{7,5},{6,8},{5,6},{2,2}}, создайте одномерный список l2={8,11,14,12,14,11,4}, элементами которого являются суммы элементов второго уровня списка l1. Осуществите это двумя способами:
  • при помощи функции Apply;
  • при помощи встроенной функции Map и пользовательской функции, выполняющей суммирование требуемых элементов.
  • Пользуясь функцией MapThread, получите транспонированную матрицу m2 из исходной m1, заданной списком m1={{a,b,c},{d,e,f},{g,h,i}}. Результат представьте в форме таблицы.

    Создайте матрицу m3, заменив в матрице m2 при помощи подстановок каждый символ его порядковым номером в латинском алфавите.

    Пользуясь функцией MapThread, получите вектор-столбец, элементами которого являются суммы элементов строк матрицы m3.

  • Используя функцию Inner, создайте пользовательскую функцию divfield[field,coord], вычисляющую дивергенцию векторного поля, заданного вектором field={fx,fy,fz}, в трёхмерном декартовом пространстве; переменные по пространственным координатам заданы вектором coord={x,y,z}.
  • Выполните усложнённое задание, которое даёт П. Веллин и др. в своей книге [14, с. 85]: создайте пользовательскую функцию, вычисляющую дивергенцию векторного поля field={f1,f2,...,fn} в n-мерном пространстве; переменные заданы вектором coord={c1,c2,...,cn}. Дивергенция в этом случае будет задаваться выражением df1/dc1+df2/dc2+...+dfn/dcn.
  • Колода игральных карт есть множество, являющееся прямым произведением двух множеств. Первое множество — масти: пики (spears), черви (hearts), трефы (clubs) и бубны (diamonds). Второе множество — достоинств карт: туз (Ass), король (King), дама (Dame), валет (Jacket) и карты от 10 до 2. Выполните следующее задание, которое даёт Е. М. Воробьём в книге [1, с. 138]. С помощью функции Outer создайте вложенный список, элементами которого являются списки, содержащие по два элемента, первым из которых являются символы s, с, h или d, а вторым — символы А, К, D, J, 10,..., 2.
  • Создайте пользовательскую функцию одного численного аргумента $$arg$$, которая
  • извлекает кубический корень из $$arg$$;
  • находит ближайшее к $$arg$$ число, нацело делящееся на 3, и вычисляет произведение этого числа и $$arg$$;
  • создаёт двухмерный рисунок, содержащий случайным образом расположенные на плоскости круги единичного радиуса, количество которых равно $$arg$$;
  • выводит на экран значение выражения $$e\pi$$ с точностью до $$arg$$ знаков после запятой;
  • создаёт единичную матрицу размерностью argxarg и в табличном виде выводит её на экран;
  • решает задачу Коши $$y'(x)+2y(x)-3x=0, y(0)=arg$$ и визуализирует решение в пределах $$x$$ от $$-arg$$ до $$arg$$.
  • Создайте пользовательскую функцию нескольких аргументов $$arg1, arg2,...$$ которая
  • находит аналитические решения квадратного уравнения $$arg1*x^2+arg2*x+arg3=0$$;
  • выводит на экран изображение куба единичных размеров с координатами $$(arg1,arg2,arg3)$$ и сферы единичного радиуса с центром в точке $$(arg1+1, arg2+2 и arg3+1)$$; цвета куба и сферы должны определяться аргументами $$arg4$$ и $$arg5$$ соответственно;
  • находит расстояние между двумя точками на плоскости, координаты которых заданы как $$(arg1,arg2)$$ и $$(arg3,arg4)$$;
  • находит сумму $$arg1+arg2$$, если оба аргумента чётные, разность $$arg1-arg2$$, если оба аргумента нечётные, произведение $$arg1*arg2$$, если один из аргументов чётный, а другой — нечётный, и выдаёт сообщение "задайте другие аргументы", если ни одно из условий не выполняется;
  • решает задачу Коши $$y''(x)+2y(x)=3, y(0)=arg1, y'(x)=arg2$$ и визуализирует решение в пределах $$x$$ от $$arg3$$ до $$arg4$$;
  • дублирует действие встроенной функции Power[x,y]; Subtract[x,y]; Log[b,z].
  • Страницы:

    Цель лекции: познакомиться с основами реализации функционального стиля программирования в языке программирования Mathematica

    9.0. Введение

    Хотя Mathematica предназначена, по большей части, для решения математических задач без применения программистских навыков, и всёже она позволяет при необходимости использовать программирование в случаях, когда имеющихся встроенных функций оказывается недостаточно, или в случае, если требуется создать собственный оригинальный алгоритм для их решения. Фактически, Mathematica является ориентированным на математические расчеты языком программирования сверхвысокого уровня. Следует подчеркнуть, что речь идет не о языке реализации самой системы Mathematica (коим является C++), а о системе как языке программирования самой по себе. Mathematica является типичным интерпретатором и не предназначена для создания исполняемых файлов.

    Как отмечает Е. М. Воробьёв [1, с. 125], поскольку Mathematica является языком программирования, то написание программ — наиболее логичная форма взаимодействия с ней. Mathematica позволяет программировать в трех стилях:

  • функциональном;
  • процедурном;
  • стиле, основанном на создании правил преобразований
  • В данной лекции мы рассмотрим функциональный стиль программирования, за основу взяв книги Е. М. Воробьёва [1], П. Веллина и др. [14] и С. Мангано [11].

    В Mathematica имеется более 1000 встроенных функций. Хотя каждая функция осуществляет достаточно узкоспециализированные операций, в своей совокупности они позволяют выполнять широчайший набор действий. Конкретные действия выполняются определённой комбинацией встроенных функций, и если то или иное действие достаточно часто встречается при проведении расчётов, было бы удобно применение комбинации функций заменить применением всего-навсего одной функции. Это стремление, по сути, лежит в основе функционального программирования.

    9.1. Функции для управления выражениями

    В предыдущих лекциях мы научились применять встроенные функции Mathematica к выражениям, пользуясь их внутренним представлением в виде h[e1,e2,…] или операторной формой (например, операторы сложения " + " и вычитания " - "). Однако существует ещё один менее наглядный, менее интуитивный, но крайне необходимый для функционального программирования способ управления выражениями. Заключается он в применении ряда специальных функций, инициирующих действие других функций.

    9.1.1. Функция Map

    Функция Map[func,expr] применяет функцию func к каждому элементу выражения expr, если оно является списком, или непосредственно к выражению expr в остальных случаях. В примере In[1] на рис. 9.1 при помощи функции Map мы применили встроенную функцию извлечения квадратного корня Sqrt к элементам списка {4,9/16,7,a}. В примере In[2] первым аргументом Map мы указали ранее не определённое в качестве функции выражение f, но Mathematica самостоятельно расценила его как функцию и также применила к элементам заданного списка.

    К функции Map можно обратиться в постфиксной форме: для того чтобы применить функцию func к выражению expr, следует записать следующее выражение func/@expr. В примере In[3] на рис. 9.1 мы выполняем то же действие, что и в In[2], но пользуемся постфиксной формой записи функции Map.

    При помощи Map к выражениям можно также применять и функции, заданные пользователем. Задавать функции самостоятельно мы научимся чуть позже, а сейчас на рис. 9.1 приведём лишь пример In[4] задания функции, удваивающей выражение, указанное в качестве её аргумента (назовём функцию Double), а в примере In[5] применим её к нашему списку при помощи функции Map.

    (рис 9.1) Функция Map при работе с одномерными списками

    Если вторым аргументом expr в Map[func,expr] является вложенный список, то указанная в первом аргументе функция func будет применена к элементам списка первого уровня. В примере In[1] на рис. 9.2 мы задали трёхуровневый список, к которому в примере In[2] применили неопределённую ранее функцию f. Для того чтобы применить func только к элементам внутреннего списка конкретного уровня, следует задать третий аргумент, и тогда функция Map будет выглядеть следующим образом: Map[func,expr,listlevel]. Если выражение listlevel задано в фигурных скобках, {listlevel}, то функция func будет применена только к элементам внутреннего списка уровня listlevel. В примерах In[3] и In[4] мы применяем функцию f к элементам внутренних списков второго и третьего уровней, соответственно.

    Если третий аргумент функции Map задан в виде списка {listlevelst,listlevelfin}, то функция func применяется к элементам внутренних списков от уровня listlevelst до listlevelfin. В примере In[5] на рис. 9.2 мы применяем функцию f к элементам внутренних списков от второго уровня до третьего.

    Если listlevel задан без фигурных скобок, то функция func будет применена к элементам всех уровней от первого до listlevel включительно. В примере In[6] мы применяем f к элементам списков до второго уровня.

    Подробней о функции Map см. книги Е. М. Воробьёва [1, с. 118–120] и П. Веллина и др. [14, с. 78–79].

    (рис 9.2) Функция Map при работе с вложенными списками

    9.1.2. Функция Apply

    Если функция Map применяет некоторую функцию к уже имеющемуся выражению, то функция Apply вносит изменения в само это выражение. Заданная в виде Apply[func,expr] функция изменяет заголовок заданного вторым аргументом выражения expr на первый аргумент func. В примере In[1] на рис. 9.3 мы заменили заголовок s выражения s[a,b,c] заголовком t при помощи функции Apply.

    Функция Apply также может быть задана в постфиксной форме: func@@expr (пример In[2] на рис. 9.3).

    Если вторым аргументом функции Apply указан список, то первый аргумент заменит заголовок List, а все элементы прежнего списка станут аргументами новой функции; при этом совершенно не имеет значения, задан ли список во внутреннем представлении или в традиционном при помощи фигурных скобок. В примерах In[3] и In[4] на рис. 9.3 мы опробовали действие функции Apply на списках, заданных обоими способами: Одинаковые выходные данные Out[3] и Out[4] говорят о полной эквивалентности обоих вариантов применения Apply.

    (рис 9.3) Замена заголовка выражения при помощи функции Apply

    Функцию Apply можно применять и к вложенным спискам, при этом точно таким же образом, что и в функции Map, можно выбирать, к какому уровню списка её следует применять. Единственное, что отличает эти функции в данном вопросе, — по умолчанию функция Map применяется к уровню 1 списка, а функция Apply — к уровню 0, на котором находится заголовок списка. На рисунке 9.4 мы проиллюстрировали действие функции Apply на вложенный список, взяв за основу примеры на рис. 9.2.

    Подробней о функции Apply см. книги Е. М. Воробьёва [1, с. 120] и П. Веллина и др. [14, с. 82–84].

    (рис 9.4) Функция Apply при работе с вложенными списками

    9.1.3. Функции MapThread и Thread

    Рассмотренная ранее функция Map позволяет применить функцию одного аргумента к элементам некоторого выражения. Если к элементам выражения необходимо применить функцию нескольких аргументов, то применяется MapThread. Функция задаётся в виде MapThread[func,{{a1,a2,…},{Mb1,b2,…},…}], где func также представляет собой функцию, которую следует применить к выражению во втором аргументе; второй аргумент представляет собой вложенный список, при этом внутренние списки на каждом из уровней должны иметь одинаковое число элементов. Результатом применения функции MapThread будет список выражений {func[a1,b1,...],func[a2,b2,...],...}. В примере In[1] на рис. 9.5 при помощи функции MapThread мы применили некоторую неопределенную функцию u к двухуровневому вложенному списку, содержащему три внутренних списка с одинаковым числом элементов. В примере In[2] на том же рисунке в качестве второго аргумента мы попытались задать вложенный список, внутренние списки которого имеют разную длину: результатом вычислений оказалось сообщение об ошибке и дублирование выражения ячейки In[2] в ячейке Out[2].

    Функция MapThread может быть применена и к вложенным спискам, содержащим один элемент — пример In[3] на рис. 9.5.

    Если вложенный список во втором аргументе содержит несколько уровней, то номер уровня, к которому следует применить функцию func, можно задать третьим аргументом. Так в примере In[4] на рис. 9.5 мы применяем некоторую неопределённую функцию w к трёхуровневому списку без указания дополнительного аргумента, а в примере In[5] третьим аргументом мы указываем уровень 2, к которому должна быть применена функция w. Результаты вычислений Out[4] и Out[5] наглядно демонстрируют действие функции MapThread в обоих случаях.

    (рис 9.5) Работа с данными во вложенных списках при помощи функции MapThread

    По выполняемым действиям на функцию MapThread очень похожа функция Thread. В простейшем варианте Thread[func[args]] функция содержит всего одни аргумент и в процессе вычисления применяет func к каждому элементу выражения args.

    Если выражение args включает в себя списки, то функция func будет применена не к спискам в целом, а к их элементам. В примере In[1] на рис. 9.6 в ячейке входных данных мы задали некоторую неопределённую функцию f, аргументами которой являются списки {a,b,c} и {1,2,3}, а в примере In[2] мы воспользовались функцией Thread применительно к f. Сравним результаты в ячейках Out[1] и Out[2]: если в первом случае аргументом f были списки полностью, то во втором — отдельные элементы каждого из списков.

    Функция func не обязательно должна быть задана во внутреннем представлении. В следующих двух примерах мы записываем равенство, в правой и левой частях которого содержатся списки одинаковой длины, и применяем к этому равенству функцию Thread. Однако в примере In[3] равенство мы задаём во внутренней форме, при помощи функции Equal, а в примере In[4] — в инфиксной форме, при помощи оператора " == ". Поскольку полученные в ячейках Out[3] и Out[4] результаты вычислений одинаковы, напрашивается вывод, что форма задания функции func не влияет на действие функции Thread.

    (рис 9.6) Применение функции Thread к функциям, аргументами которых являются списки

    Если выражение args содержит выражения, отличные от списков, то функцию Thread необходимо задавать в виде Thread[func[args],h], где второй аргумент h указывает заголовок выражения, к элементам которого следует применять выражение func. При этом функция также необязательно должна быть задана во внутренней форме. Так в примере In[1] на рис. 9.7 мы задали функцию извлечения квадратного корня Sqrt из суммы $$x+y+z$$. В примере In[2] мы задали эту же сумму во внутренней форме при помощи функции с заголовком Plus и при помощи Thread применили заголовок Sqrt к элементам суммы. В примере In[3] мы задали сумму в операторной форме осуществили действие, аналогичное предыдущему примеру. Результаты вычислений в Out[1], Out[2] и Out[3] наглядно демонстрируют действие функции Thread на выражения.

    Подробней о функциях Thread и MapThread см. книги Е. М. Воробьёва [1, с. 120–122] и П. Веллина и др. [14, с. 79–80].

    (рис 9.7) Применение функции Thread к функциям, аргументы которых отличны от списков

    9.1.4. Атрибут функций Listable

    П. Веллин и др. [14, с. 80–82] наравне с функциями уделяет особое внимание такому атрибуту функций, как Listable. И нам следует познакомиться с этим атрибутом ближе.

    Множество встроенных функций Mathematica, имеющих один аргумент, если они содержат в качестве аргумента список, применяются в отдельности к каждому элементу списка. Одна из таких функций — функция нахождения синуса угла Sin. Так выражения Sin[{a,b,c}] и Map[Sin,{a,b,c}] при вычислении имеют одинаковые результаты (см. примеры In[1] и In[2] на рис. 9.8, соответственно).

    Множество встроенных функций Mathematica, имеющих два и более аргументов, если аргументы являются списками, применяются к соответствующим элементам списков в отдельности. Среди таких функций — Plus. Так одинаковыми являются результаты вычислений выражений Plus[{1,2,3},{x,y,z}] и MapThread[Plus,{{1,2,3},{x,y,z}}] (примеры In[3] и In[4] на рис. 9.8, соответственно).

    Функции, которые автоматически применяются не к целым спискам, а к элементам списков, обладают атрибутом Listable. Убедиться в наличии этого атрибута у функции можно при помощи функции Attributes, что мы и делаем в примерах In[5] и In[6] на рис. 9.8. для функций Sin и Plus.

    Если выражение не обладает атрибутом Listable, как применённая нами к некоторому списку {k,l,m,n} неопределённая функция g в примере In[7], то добавить его в список атрибутов выражения можно при помощи функции SetAttributes. В примере In[8] мы устанавливаем функции g атрибут Listable, а в In[9] вновь пытаемся вычислить выражение In[7]. Если в Out[7] функция g оказалась применена ко всему списку, то в In[9] — к каждому элементу списка в отдельности.

    Избавить выражение от любого присущего ему атрибута можно при помощи функции ClearAttributes. В примере In[10] мы удаляем из списка атрибутов функции Sin атрибут Listable: теперь, как иллюстрирует пример In[11], Sin будет применяться ко всему списку в целом.

    (рис 9.8) Атрибут Listable

    9.1.5. Функции Inner и Outer

    Функция Outer[func,list1,list2,...] применяет функцию func ко всевозможным комбинациям элементов списков list1,list2,... (пример In[1] на рис. 9.9), при этом совершенно необязательно, чтобы списки содержали одинаковое число элементов (пример In[2]). При помощи Outer можно, например, из нескольких списков сформировать один вложенный список со всевозможными сочетаниями элементов: для этого первым аргументом следует задать заголовок List (пример In[3]).

    Если аргументы list1,list2,... являются вложенными списками, то уровень вложенного списка, с которым следует проводить манипуляции, указывается в качестве дополнительно аргумента n, то есть, функция задаётся в виде Outer[func,list1,list2,...,n]. В примере In[4] на рис. 9.9 мы воспользовались функцией Outer без дополнительных аргументов для двух двухуровневых вложенных списков. По умолчанию функция func (в нашем примере это неопределённая функция g) применяется к комбинациям элементов самого низшего уровня вложенных списков (в нашем примере это уровень 2). В примере In[5] мы указали дополнительным аргументом, что оперировать следует элементами списков первого уровня.

    Кроме того, для разных списков можно задавать разные уровни элементов, участвующих в вычислениях. Для этого соответствующая функция задаётся в виде Outer[func,list1,list2,...,n1,n2,...]. В примере In[6] на рис. 9.9 мы указываем, что в первом списке следует оперировать элементами второго уровня, а во втором — первого.

    Глубина списков в аргументах функции Outer может быть разной. Так в примере In[7] на рис. 9.9 мы задали в качестве аргументов одноуровневый и трёхуровневый списки и получили результирующий четырёхуровневый список.

    (рис 9.9) Применение функции Outer для создания списков

    Функция Inner[func1,{a1,a2,…},{b1,b2,…},func2] в качестве результата генерирует выражение func2[func1[a1,b1],func1[a2,b2],...], то есть, сначала она формирует из двух списков аргументы для функции func1, а полученный результат задаёт в качестве аргументов функции func2. На рис. 9.10 мы привели абстрактный пример In[1] последовательного применения неопределённых функций f1 и f2 к спискам из четырёх элементов {w,x,y,z} и {10,20,30,40}, а также конкретный пример In[2] перемножения соответствующий элементов тех же списков (при помощи функции Times) и последующего сложения результатов (Plus).

    Подробней о функциях Inner и Outer см. книги Е. М. Воробьёва [1, с. 122–123] и П. Веллина и др. [14, с. 84].

    (рис 9.10) Применение функции Inner к спискам

    9.2. Суперпозиция функций

    9.2.1. Итерационные функции

    В математике композиция (суперпозиция) функций — это применение одной функции к результату другой. Частным случаем такой операции является применение к выражению одной и той же функции несколько раз.

    Функция Nest[func,exps,n] последовательно применяет n раз функцию func к выражению expr. Для того чтобы проследить за процессом получения конечного выражения, используется функция NestList с теми же аргументами. Эта функция генерирует список результатов, полученных при применении функции func к выражению expr на каждой итерации. На рис. 9.11 мы воспользовались функциями Nest и NestList для того, чтобы пять раз применить к выражению 16 неопределённую функцию g (примеры In[1] и In[2]) и функцию вычисления квадратного корня Sqrt (примеры In[3] и In[4]). Результаты вычислений в ячейках Out[1] и Out[3], как и ожидалось, совпадают с последними элементами списков в Out[2] и Out[4], соответственно.

    (рис 9.11) Использование итерационных функций Nest и NestList

    Функции Fold и FoldList работает с функциями двух аргументов. Заданная в виде Fold[func,var,{a1,a2,…}] функция на первой итерации применяет функцию func к аргументам var и a1, на второй итерации применяет func к результату предыдущей итерации и аргументу a2 и так далее, до последнего элемента в списке. На рис. 9.12 приведены примеры использования функций Fold и FoldList для неопределённой функции f (примеры In[1] и In[2]) и функции возведения в степень Power (примеры In[3]In[6]).

    Подробней об итерационных функциях см. книги Е. М. Воробьёва [1, с. 132–134] и П. Веллина и др. [14, с. 86–87].

    (рис 9.12) Использование итерационных функций Fold и FoldList

    9.2.2. Композиция нескольких функций

    В более широком смысле композиции функций в качестве аргумента функции можно использовать результат выполнения другой функции. В Mathematica последовательное применение к выражению нескольких функций называется вызовом вложенной функции (nested function call) (П. Веллин и др. [14, с. 89]). В примере на рис. 9.13 сначала мы применили к числу 2. функцию Log (пример In[1]), а затем к полученному результату последовательно — функции Sin и Sqrt (примеры In[2]In[3]). Однако все указанные операции можно совершить за одно действие. В примере In[4] мы вызываем вложенную функцию Sqrt[Sin[Log[2.]]]: сравнив выходные данные в Out[3] и Out[4], мы убедимся, что оба подхода применения к аргументу последовательно нескольких функций приводят к одинаковому результату.

    Когда мы пользуемся первым подходом, мы можем проследить за процессом вычисления, поскольку помимо конечного результата получаем промежуточные результаты действия каждой функции (в нашем случае это Out[1] и Out[2]). Для получения промежуточных результатов при использовании второго подхода используется функция Trace. В примере In[5] на рис. 9.13 мы применяем Trace к вложенной функции In[4].

    (рис 9.13) Вычисления с использованием вложенных функций

    Композицию функций можно осуществлять менее наглядным, но более традиционным для Mathematica способом — при помощи встроенной функции Composition. Указывается она в следующем виде: Composition[func1,func2,...][arg], где funci — функции в порядке, обратном порядку их применения к аргументу (или аргументам) arg, т.е., последней применяется функция func1, предпоследней — func2 и т.д. В примере In[1] на рис. 9.14 при помощи Composition мы осуществляем тот же набор действии, что и на рис. 9.13.

    Для получения промежуточных результатов выполнения вложенной функции при данном подходе используется функция ComposeList[{func1,func2,...},arg]. Однако, у ComposeList порядок функций в аргументе обратный в сравнении с Composition: функция func1 применяется первой — ср. примеры In[1] и In[2] на рис. 9.13. Также обратим внимание на то, что формат вывода результата данных функциями Trace и ComposeList различный: если в первом случае мы имеем вложенный список, содержащий как вычисляемое выражение в явном виде, так и результат вычислений, то во втором — одномерный список с результатами вычислений.

    Подробней о композиции функций см. книгу Е. М. Воробьёва [1, с. 134–136].

    (рис 9.14) Задание вложенных функций при помощи функции Composition и ComposeList

    9.3. Определение пользовательских функций

    9.3.1 Явные пользовательские функции

    Функции — это объекты, которые после проведения манипуляций с полученными на входе выражениями, возвращают однозначно соответствующие им выражения на выходе. При этом манипуляции могут быть как простыми, в одно действие, так и сложными, в несколько шагов. В принципе, функции Mathematica мы можем расценивать так же, как и математические функции.

    Теперь, пользуясь подходом Е. М. Воробьёва [1, с. 126–127], попробуем ответить на вопрос, каким же образом задаются пользовательские функции.

    Если мы попытаемся определить некоторую функцию, используя уже хорошо нам известную операцию присваивания (или отложенного присваивания), то потерпим фиаско. Проиллюстрируем это на рис. 9.15. В примере In[1] мы пытаемся определить функцию возведения в квадрат синуса угла, присваивая выражению SinSquare[x] значение (Sin[x])2. В примере In[2] мы пытаемся применить нашу функцию SinSquare к числу Pi, выражениям y и x. Как мы видим в Out[2], вычисление произошло только для аргумента x. Дело в том, что, хотя пользователь рассматривает x как переменную, которая может принимать любые значения из области определения функции, Mathematica расценивает x как фиксированный символ, неизменное выражение. Поэтому выражение SinSquare[x] следует рассматривать как единое целое, а не как выражение, состоящее из двух функционально обособленных элементов SinSquare и [x]. Точно так же едиными и отличными от SinSquare[x] выражениями являются SinSquare[Pi] и SinSquare[y]. Поэтому SinSquare[x] везде будет заменяться выражением Sin[x]2, а два других будут оставаться неизменными.

    (рис 9.15) Неудачная попытка определения пользовательской функции при помощи оператора присваивания

    Таким образом, наша задача сделать так, чтобы указываемый в квадратных скобках аргумент x функции с заголовком SinSquare воспринимался как переменная. Для этого необходимо вместе с x употребить подчёркивание " _ ", внутренняя форма которого — Blank[].

    Таким образом, общий вид пользовательской функции следующий:

    funcname[arg1_,arg2_,...]:= body

    Левая часть выражения содержит имя функции funcname, которое в обязательном порядке должно являться символом. За именем функции следуют квадратные скобки, содержащие один или несколько разделённых запятыми символьных выражений, каждое из которых оканчивается нижним подчёркиванием. Эти символы выступают в качестве имён аргументов функции.

    В правой части выражения содержится непосредственно определение функции, которое называется телом функции: оно может состоять из одного единственного выражения — так называемый "однострочник" (one-liner), или включать целый ряд выражений — составная функция (compound function). Имена аргументов, заданных в левой части, используются в правой части без нижнего подчёркивания.

    Пользовательские функции принято определять при помощи отложенного присваивания " := ". При этом выражение, которым задаётся пользовательская функция, ничего не генерирует, и не образуется ячейки Out; в момент задания тело функции не вычисляется. Вычисление происходит лишь в момент повторного обращения к функции.

    В примере In[1] на рис. 9.16 мы, наконец, задаём функцию возведения в квадрат синуса некоторого выражения. В In[2] на рис. 9.16 мы проводим проверку действия нашей функции на разные выражения (аналогично примеру In[2] на рис. 9.15) и по полученному результату Out[2] убеждаемся в её работоспособности.

    Подробней о задании пользовательских функций см. книги Е. М. Воробьёва [1, с. 126–129] и П. Веллина и др. [14, с. 88–89].

    (рис 9.16) Определение пользовательской функции

    9.3.2 Чистые и анонимные функции

    В предыдущем пункте мы научились самостоятельно определять функции: при этом все заданные таким образом функции в обязательном порядке являются именованными, то есть, имеют заголовки. Однако заголовки имеет смысл определять, если эти функции будут использоваться в дальнейшем тексте программы. Если же новая функция будет использована единожды или считанное число раз, "полезно работать с так называемыми чистыми функциями, которые используются только в момент их создания" (Е. М. Воробьёв [1, с. 129]). Для определения чистых функций служит выражение Function. В простейшей своей форме Function[x,body] задаёт функцию одной переменной x, причём в качестве переменной вместо x может использоваться любой символ: в использовании нижнего подчёркивания здесь необходимости нет. Выражение Function[{x1,x2,...},body] задаёт функцию нескольких переменных. Выражение body задаётся так же, как правая часть выражения, задающего пользовательскую функцию. В качестве примера In[1] на рис. 9.17 зададим чистую функцию возведения в квадрат синуса угла, аналогичную таковой в примере In[1] на рис. 9.16.

    Выражение, к которому следует применить заданную чистую функцию, необходимо указать в квадратных скобках сразу после выражения Function[...] (примеры In[2] и In[3] на рис. 9.17). Чистую функцию очень просто превратить в именованную, присвоив её выражение некоторому символу (примеры In[4]In[6] на рис. 9.17).

    (рис 9.17) Задание чистых функций

    Е. М. Воробьёв [1, с. 130] пишет: "Существует более компактная и удобная форма представления чистой функции, которую иногда называют анонимной функцией. В анонимных функциях вместо связанных переменных используются специальные выражения "Математики" с заголовком Slot, имеющие вид #, #1 и т.д. При этом для функций одной переменной используется #, а в функциях нескольких переменных первый аргумент обозначается #1, второй #2 и т.д. Кроме того, в конце анонимной функции ставится знак ". В примере In[1] на рис. 9.15 мы снова определяем функцию вычисления квадрата синуса угла, но делаем это посредством задания анонимной функции. Чтобы применить заданную функцию к некоторому выражению, следует также указать его в квадратных скобках сразу после определения функции (примеры In[2] и In[3] на рис. 9.18).

    На рис. 9.18 также приведён пример In[4] задания именованной анонимной функции двух переменных для вычисления суммы квадратов двух выражений, а также примеры In[5] и In[6] использования этой функции.

    (рис 9.18) Задание анонимных функций

    Использование анонимных функций оказывается наиболее эффективным в сочетании с другими функциями. В примере In[1] на рис. 9.19 зададим некоторый список list={1,7,3,5,6,4,3}. В примере In[2] при помощи функции Map выполним заданный набор действий (возведём во вторую степень и удвоим результат) с элементами списка list, причём набор действий определим при помощи анонимной функции.

    Очень полезными оказываются анонимные функции при работе со списками. Предназначенная для сортировки элементов списка функция Sort, с которой мы познакомились в третьей лекции настоящего курса, может иметь необязательный аргумент, задающий условие сортировки: он должен быть двухместным предикатом, и сортировка производится так, чтобы он принимал значение True (Е. М. Воробьёв [1, с. 131]). Этот аргумент может быть задан в форме анонимной функции. В примере In[3] на рис. 9.19 мы осуществляем сортировку элементов списка list по убыванию их абсолютного значения, причём условие сортировки задаём как анонимную функцию. В примере In[4] на рис. 9.19 при помощи функции Select мы выбираем только элементы, принадлежащие отрезку от -4 до 4, причём условие выбора мы также задаём в виде анонимной функции.

    Подробней о чистых и анонимных функциях см. книги Е. М. Воробьёва [1, с. 129–131] и П. Веллина и др. [14, с. 102–105].

    (рис 9.19) Использование анонимных функций в сочетании с другими функциями

    9.4. Вспомогательные функции

    9.4.1. Составные функции

    Как отмечает П. Веллин и др. [14, с. 96], левая часть составных функций задаётся так же, как и левая часть любых пользовательских функций. Правая часть содержит заключённую в круглые скобки последовательность разделённых точкой с запятой выражений:

    funcname[arg1_,arg2_,...]:=(expr1,expr2,...)

    Составные функции очень похоже на суперпозицию функций, однако аргументами одних выражений не обязательно должны быть результаты других входящих в составную функцию выражений. Когда подаётся команда на вычисление составной функции некоторого аргумента, то выражения, указанные в правой части, вычисляются по очереди, и результатом функции является результат вычисления последнего выражения.

    В примере In[1] на рис. 9.20 мы последовательно задаём функции возведения некоторого выражения в куб Cube, квадрат Sqr и удвоения выражения Double, и только затем определяем функцию Exprn, осуществляющую с аргументом $$x$$ следующий набор действий: $$x*(x^3+2*x)/(x^2)+1$$. В примере In[5] мы применяем функцию Exprn к числу 5.

    Теперь зададим составную функцию с тем же заголовком Exprn, выполняющую весь описанный набор действий. В In[6] на рис. 9.20 для наглядности мы очищаем выражения Cube, Sqr, Double и Exprn. В примере In[7] мы сразу же задаём функцию Exprn, обращение к которой даёт нам требуемый результат. Функции для получения промежуточных выражений мы включаем непосредственно в тело Exprn. В примере In[8] мы применяем вновь заданную нами функцию Exprn к числу 5: результаты в Out[5] и Out[8] полностью идентичны.

    (рис 9.20) Задание составных функций

    П. Веллин и др. [14, с. 96] акцентирует внимание на следующих моментах, связанных с составными функциями.

    Поскольку выражения в правой части вычисляются в заданном порядке, требуемые значения должны быть присвоены, а пользовательские функции определены до обращения к ним. Имена аргументов пользовательский функций должны быть отличными от имён аргументов составной функции (так в примере In[7] на рис. 9.20 аргументом составной функции Exprn мы выбрали символ n, а аргументами содержащихся в её теле пользовательских функций — символы x, y и z).

    Когда мы задаём составную функцию, вместе с ней (в её теле) мы также определяем другие пользовательские функции и значения переменных. Если мы удалим заданную нами составную функцию, например, при помощи Clear, то определённые в её теле пользовательские функции и значения переменных сохранятся. В связи с этим, при дальнейших расчётах могут возникнуть проблемы, если мы снова захотим воспользоваться переменными или выражениями, которые являются заголовками этих функций.

    В программистской практике является плохим тоном оставлять вспомогательные функции, в использовании которых однозначно не будет необходимости в дальнейших вычислениях.

    9.4.2. Локализация имён: функция Module

    П. Веллин и др. [14, с. 98] пишет, что для того, чтобы пользовательские функции, заданные в теле составной функции, были изолированы от остального текста программы, то есть, обращение к их заголовкам не приводило бы к выполнению какого-либо набора действий, к правой части составной функции применяется функция Module:

    funcname[arg1_,arg2_,...]:=Module[{name1,name2=val,...},exprs]

    Первый аргумент функции Module — список имён функций или переменных, которые мы хотим локализовать, то есть, сделать так, чтобы они рассматривались в заданном виде только в пределах функции funcname. Второй аргумент — exprs — включает все выражения, которые требуется задать.

    Проиллюстрируем действие функции Module в примерах на рис. 9.21.

    В примере In[1] мы вновь задали функцию Exprn точно таким же способом, что и в примере In[7] на рис. 9.20. В In[2] мы списком вывели результат действия на число 5 функций Exprn, Cube, Sqr и Double: в Out[2] мы получили список численных значений соответствующих функций. В примере In[3] мы очистили заголовок функции Exprn. В примере In[4] мы осуществили то же действие, что и в In[2]: в списке Out[4] элемент, соответствующий функции Exprn, принял неопределённое значение Exprn[5], остальные элементы так и остались числами.

    Для чистоты демонстрации в In[5] освободим все выражения, использованные ранее в качестве заголовков пользовательских функций.

    Теперь локализуем имя функции Cube в пределах функции Exprn. Для этого воспользуемся при задании Exprn функцией Module, указав первым её аргументом выражение Cube (пример In[6]). В In[7] мы снова вывели результат действия на число 5 вновь заданной функции Exprn, Cube, Sqr и Double: в списке Out[7] элемент, соответствующий функции Cube, не является числом, то есть, применённое вне пределов Exprn выражение Cube является просто символьным выражением. В In[8] мы очистили выражение Exprn, а в In[9] ещё раз вывели список: в Out[2] единственные элементы, обладающие численными значениями, соответствуют функциям Sqr и Double, поскольку только их мы не освободили от определённой ранее роли.

    (рис 9.21) Локализация имён выражений в пределах составной функции

    9.4.3. Локализация значений: функция Block

    Пусть при проведении расчётов мы пользуемся некоторым выражением, которому присвоено строго определённое значение. Если на каком-то этапе вычислений возникла необходимость произвести расчёт с другим значением этого выражения, а в дальнейших вычислениях использовать прежнее его значение, П. Веллин и др. [14, с. 99] предлагает использовать функцию Block. Заданная в виде Block[{ex1=val1,ex2=val2,...},exprs] она вычисляет выражения exps при заданных в первом аргументе значениях val1,val2,... выражений ex1,ex2,…, при этом значения val1,val2,... принимаются выражениями лишь в пределах функции Block.

    В примере In[1] на рис. 9.22 мы присвоили выражению h значение 10. В примере In[2] мы указали неопределённую функцию f[x], в которой x равняется h^2, и вычисления мы проводили при h равном 5. При этом мы воспользовались функцией Block для того, чтобы h равнялось 5 только при вычислении функции f[x]. В примере In[3] мы убеждаемся в том, что вне функции Block выражение h по-прежнему имеет значение 10.

    (рис 9.22) Локализация значений выражений в пределах некоторой функции

    9.4.4. Локализация констант: функция With

    Согласно П. Веллину и др. [14, с. 99], функция With[{ex1=val1,ex2=val2,...},exprs] вместо входящих в exprs выражений ex1,ex2,... подставляет величины val1,val2,..., при этом за пределами функции With выражения ex1,ex2,... принимают отличные от val1,val2,... значения, заданные ранее.

    В примере In[1] на рис. 9.23 мы присвоили выражению a значение 10. В примере In[2] мы определили пользовательскую функцию, в левой части которой в качестве переменной значится символ x, а в правой — выражение, включающее только символ a: при этом при помощи функции With символ a мы заменили выражением x^2. В примере In[3] мы применили определённую нами в In[2] функцию к величине 2. В In[4] мы присвоили a новое значение 5, однако, на результате применения функции f это ни коим образом не отразилось — см. пример In[5].

    (рис 9.23) Локализация констант в пределах пользовательской функции

    Ключевые термины

    Анонимной функцией называется компактная форма задания чистой функции, в которой вместо символов переменных используются специальные выражения Mathematica с заголовком Slot.

    Вызовом вложенной функции называется последовательное применение к выражению нескольких функций.

    Композиция (суперпозиция) функций — это применение одной функции к результату другой.

    Функции — это объекты, которые после проведения манипуляций с полученными на входе выражениями, возвращают однозначно соответствующие им выражения на выходе.

    Чистыми называются функции, которые используются только в момент их создания.

    Краткие итоги

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

    Вопросы

  • Какими способами Mathematica позволяет применить заданную функцию func к некоторому выражению expr?
  • Чем отличаются действия, оказываемые на выражения функциями Map и Apply? Какова полная и постфиксная форма этих функций?
  • Какие функции Mathematica позволяют применять к элементам выражения функции нескольких аргументов? В каком виде задаются их аргументы?
  • Что вносит атрибут Listable в действия, которые выполняют функции?
  • Для каких целей используются функции Inner и Outer? Какие аргументы они имеют?
  • Что такое суперпозиция функций? Каким образом Mathematica позволяет осуществлять суперпозицию функций?
  • В каком виде в Mathematica задаются явные пользовательские функции?
  • Что такое чистые функции? Каким образом они определяются в Mathematica?
  • Что такое анонимные функции? Каким образом они определяются в Mathematica?
  • Каким образом в Mathematica задаются составные функции? В чём состоит отличие составной функции от суперпозиции функций?
  • Для каких целей в Mathematica применяются локализация имён, значений, констант при задании пользовательских функций? Посредством каких функций они осуществляются?
  • Упражнения

  • Используя исходный вложенный список l1={{1,7},{8,3},{9,5},{7,5},{6,8},{5,6},{2,2}}, создайте одномерный список l2={8,11,14,12,14,11,4}, элементами которого являются суммы элементов второго уровня списка l1. Осуществите это двумя способами:
  • при помощи функции Apply;
  • при помощи встроенной функции Map и пользовательской функции, выполняющей суммирование требуемых элементов.
  • Пользуясь функцией MapThread, получите транспонированную матрицу m2 из исходной m1, заданной списком m1={{a,b,c},{d,e,f},{g,h,i}}. Результат представьте в форме таблицы.

    Создайте матрицу m3, заменив в матрице m2 при помощи подстановок каждый символ его порядковым номером в латинском алфавите.

    Пользуясь функцией MapThread, получите вектор-столбец, элементами которого являются суммы элементов строк матрицы m3.

  • Используя функцию Inner, создайте пользовательскую функцию divfield[field,coord], вычисляющую дивергенцию векторного поля, заданного вектором field={fx,fy,fz}, в трёхмерном декартовом пространстве; переменные по пространственным координатам заданы вектором coord={x,y,z}.
  • Выполните усложнённое задание, которое даёт П. Веллин и др. в своей книге [14, с. 85]: создайте пользовательскую функцию, вычисляющую дивергенцию векторного поля field={f1,f2,...,fn} в n-мерном пространстве; переменные заданы вектором coord={c1,c2,...,cn}. Дивергенция в этом случае будет задаваться выражением df1/dc1+df2/dc2+...+dfn/dcn.
  • Колода игральных карт есть множество, являющееся прямым произведением двух множеств. Первое множество — масти: пики (spears), черви (hearts), трефы (clubs) и бубны (diamonds). Второе множество — достоинств карт: туз (Ass), король (King), дама (Dame), валет (Jacket) и карты от 10 до 2. Выполните следующее задание, которое даёт Е. М. Воробьём в книге [1, с. 138]. С помощью функции Outer создайте вложенный список, элементами которого являются списки, содержащие по два элемента, первым из которых являются символы s, с, h или d, а вторым — символы А, К, D, J, 10,..., 2.
  • Создайте пользовательскую функцию одного численного аргумента $$arg$$, которая
  • извлекает кубический корень из $$arg$$;
  • находит ближайшее к $$arg$$ число, нацело делящееся на 3, и вычисляет произведение этого числа и $$arg$$;
  • создаёт двухмерный рисунок, содержащий случайным образом расположенные на плоскости круги единичного радиуса, количество которых равно $$arg$$;
  • выводит на экран значение выражения $$e\pi$$ с точностью до $$arg$$ знаков после запятой;
  • создаёт единичную матрицу размерностью argxarg и в табличном виде выводит её на экран;
  • решает задачу Коши $$y'(x)+2y(x)-3x=0, y(0)=arg$$ и визуализирует решение в пределах $$x$$ от $$-arg$$ до $$arg$$.
  • Создайте пользовательскую функцию нескольких аргументов $$arg1, arg2,...$$ которая
  • находит аналитические решения квадратного уравнения $$arg1*x^2+arg2*x+arg3=0$$;
  • выводит на экран изображение куба единичных размеров с координатами $$(arg1,arg2,arg3)$$ и сферы единичного радиуса с центром в точке $$(arg1+1, arg2+2 и arg3+1)$$; цвета куба и сферы должны определяться аргументами $$arg4$$ и $$arg5$$ соответственно;
  • находит расстояние между двумя точками на плоскости, координаты которых заданы как $$(arg1,arg2)$$ и $$(arg3,arg4)$$;
  • находит сумму $$arg1+arg2$$, если оба аргумента чётные, разность $$arg1-arg2$$, если оба аргумента нечётные, произведение $$arg1*arg2$$, если один из аргументов чётный, а другой — нечётный, и выдаёт сообщение "задайте другие аргументы", если ни одно из условий не выполняется;
  • решает задачу Коши $$y''(x)+2y(x)=3, y(0)=arg1, y'(x)=arg2$$ и визуализирует решение в пределах $$x$$ от $$arg3$$ до $$arg4$$;
  • дублирует действие встроенной функции Power[x,y]; Subtract[x,y]; Log[b,z].
  • Вернуться к учебному плану