Функциональное программирование
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.
Функцию Apply можно применять и к вложенным спискам, при этом точно таким же образом, что и в функции Map, можно выбирать, к какому уровню списка её следует применять. Единственное, что отличает эти функции в данном вопросе, — по умолчанию функция Map применяется к уровню 1 списка, а функция Apply — к уровню 0, на котором находится заголовок списка. На рисунке 9.4 мы проиллюстрировали действие функции Apply на вложенный список, взяв за основу примеры на рис. 9.2.
Подробней о функции Apply см. книги Е. М. Воробьёва [1, с. 120] и П. Веллина и др. [14, с. 82–84].
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 в обоих случаях.
По выполняемым действиям на функцию 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.
Если выражение args содержит выражения, отличные от списков, то функцию Thread необходимо задавать в виде Thread[func[args],h], где второй аргумент h указывает заголовок выражения, к элементам которого следует применять выражение func. При этом функция также необязательно должна быть задана во внутренней форме. Так в примере In[1] на рис. 9.7 мы задали функцию извлечения квадратного корня Sqrt из суммы . В примере In[2] мы задали эту же сумму во внутренней форме при помощи функции с заголовком Plus и при помощи Thread применили заголовок Sqrt к элементам суммы. В примере In[3] мы задали сумму в операторной форме осуществили действие, аналогичное предыдущему примеру. Результаты вычислений в Out[1], Out[2] и Out[3] наглядно демонстрируют действие функции Thread на выражения.
Подробней о функциях Thread и MapThread см. книги Е. М. Воробьёва [1, с. 120–122] и П. Веллина и др. [14, с. 79–80].