Программирование, основанное на правилах преобразований
10.2. Шаблоны
10.2.1. Использование шаблонов в глобальных правилах преобразования
В Mathematica, согласно А.Н. Прокопене и А.В. Чичурину [5, с. 89], "шаблоном является выражение, содержащее символ подчёркивания "_"(выражение Blanc[])". С подобными выражениями мы уже встречались в предыдущей лекции, посвящённой функциональному программированию, когда учились задавать пользовательские функции.
Е. М. Воробьёв [1, с. 148] весьма точно определяет назначение шаблонов: "В принципе шаблоны используются для выделения классов выражений "Математики"".
Самый широкий класс выражений очерчивает самый простой шаблон, состоящий только из одного символа подчёркивания " _ ". Этому классу соответствуют абсолютно все выражения Mathematica.
Можно объединить в один класс выражения, обладающие определённым заголовком head: для этого задаётся шаблон вида _head. Для того чтобы задать шаблон для действительных чисел, следует записать _Real, а чтобы выделить в отдельный класс все символьные выражения — задать _Symbol.
Для того чтобы определить, относится ли то или иное выражение expr к классу данных, задаваемому некоторым шаблоном pat, следует воспользоваться функцией MatchQ[expr,pat]: она возвращает значение True, если выражение expr соответствует шаблону pat, и значение False — если не соответствует.
В примере In[1] на рис. 10.9 мы проверяем на принадлежность классу данных, задаваемых шаблоном " _ ", ряд выражений различных типов данных. Как мы видим в Out[2], все они соответствуют данному шаблону. В примере In[2] для проверки мы задаём новый шаблон вида _Complex, описывающий комплексные числа, а в In[3] — _List для поиска списков.
Заголовок head не обязательно должен быть стандартным выражением Mathematica. Для иллюстрации этого факта воспользуемся примером, аналогичным приведённому в книге П. Веллина и др. [14, с. 152] — см. пример In[4] на рис. 10.9: в качестве шаблона мы задаём выражение _g, где g — заголовок неопределённой функции, а затем проверяем ряд выражений на соответствие этому шаблону.
Для иллюстрации следующей особенности задания шаблонов воспользуемся примером, приведённым в работе Е. М. Воробьёва [1, с.149] — см. In[1] на рис. 10.10. Очевидно и для пользователя, и для программы, что выражение x^3 соответствует шаблону _Symbol^_Integer. В математике любое целое число (в нашем примере показатель степени 3) можно представить как сумму двух других целых чисел. Поэтому у пользователя может возникнуть предположение, что приведённое выражение должно соответствовать и шаблону _Symbol^(_Integer+_Integer). Но в этой ситуации Mathematica не согласна с пользователем, поскольку, как справедливо пишет Е. М. Воробьёв [1, с. 149], она "сравнивает внутренние полные формы выражения и шаблона, а не устанавливает их математическую эквивалентность".
Шаблоны вида " _ " и _h являются анонимными: первый, как мы уже знаем, описывает абсолютно любые выражения, а второй задаёт ограничение по заголовку выражения. Однако для большей конкретизации критериев соответствия шаблону и, соответственно, сужения класса выражений может возникнуть необходимость прибегнуть к именованным шаблонам, которые задаются в виде symb_, где symb — некоторый символ. Проиллюстрируем разницу между шаблонами _Symbol и Symbol_ в примерах In[2] и In[3] на рис. 10.10. Мы воспользовались новой функцией Cases, которая, будучи заданной в виде Cases[{expr1,expr2,…},patts], выбирает из списка выражений {expr1,expr2,…} только те, которые соответствуют шаблонам patts. В обоих примерах (In[2] и In[3]) мы предоставляем для проверки соответствия шаблонам один и тот же список выражений {g^h,g^g,h^h,h^g,f[y]^f[y]}, но получаем (в Out[2] и Out[3], соответственно) совершенно разные результаты.
В первом примере (In[2]) при отборе играют роль два ключевых критерия.
Во-первых, отбираются только те выражения, и в основании, и в показателе степени которых содержатся выражения с заголовком Symbol: именно поэтому не функцией Cases игнорируется выражение f[y]^f[y], поскольку оба заголовка есть f.
Во-вторых, совершенно не имеет значения, совпадают выражения в основании и показателе степени (g^g,h^h) или нет (g^h,h^g): в обоих случаях выражения оказываются соответствующими шаблону.
Во втором примере Symbol определяет не требуемый для соответствия заголовок, а попросту указывает на присутствие некоторого выражения, поэтому никакого несоответствия в выражении f[y]^f[y] функция Cases не находит. Однако другой критерий задаёт необходимость того, чтобы выражения в основании и показателе степени были одинаковыми, поскольку в обеих частях шаблона используется одно и то же выражение Symbol_. Именно поэтому не проходят проверку на соответствие выражения g^h и h^g, и отбираются в результирующий список выражения g^g и h^h.
Подробней о шаблонах см. книги Е. М. Воробьёва [1, с. 148–151] и П. Веллина и др. [14, с. 151–153].
Для задания шаблонов могут использоваться выражения, содержащие два и три символа подчёркивания, — " __ " и " ___ ". Их внутренние представления имеют, соответственно, следующий вид: BlankSequence[] и BlankNullSequence[]. Таки шаблоны оказываются необходимыми и эффективными при работе со списками.
Выражение вида h__, то есть, содержащее двойное подчёркивание, очерчивает класс выражений, состоящих из одного или более выражений с заголовком h, разделённых запятыми.
Выражение вида h___ (содержащее тройное подчёркивание) очерчивает класс выражений, состоящих из нуля или более выражений с заголовком h, также разделённых запятыми.
Проиллюстрируем действие шаблонов с двойным и тройным подчёркиванием на примере, аналогичном приведённому в работе П. Веллина и др. [14, с. 153–154]. Пусть мы имеем двухуровневый вложенный список {{a,b},{c},{d,e},{},{f,g,h}}, элементами которого являются списки, содержащие от нуля до трёх элементов. В примере In[1] на рис. 10.11 при помощи функции Cases мы выберем из этого списка элементы, соответствующие шаблону x_, в примере In[2] — шаблону y__, a в примере In[3] — шаблону z___. В первом примере в результирующий список Out[1] попало только одно выражение, представляющее собой список, содержащий один элемент. Во втором примере результирующий список оказался значительно шире: помимо выражения {c}, отобранного и в предыдущем примере, в него также попали списки с двумя и тремя элементами. В последнем примере результирующий список оказался полностью идентичен исходному, поскольку помимо выражений, содержащихся и в Out[2], в него попали и пустые списки {}, проигнорированные в обоих предыдущих примерах.
Подробней о шаблонах с двумя и тремя подчёркиваниями см. книги Е. М. Воробьёва [1, с. 149–150] и П. Веллина и др. [14, с. 153–156].