//@
- это «обход дерева после заказа». Он посещает каждый узел в древовидной структуре, дочерние узлы которого посещаются до самого узла. Предоставленная функция вызывается с каждым узлом в качестве аргумента, дочерние узлы которого уже «расширены» предыдущим вызовом. Древовидные структуры данных являются общими, а также необходимость их обхода. Но я осмелюсь сказать, что основной вариант использования //@
в контексте Mathematica - реализация оценщиков.
Давайте начнем с создания случайного древовидного выражения:
In[1]:=
$expr = 500 //.
n_Integer /; RandomInteger[100] < n :>
RandomChoice[{p, m}] @@ RandomInteger[Floor[n/2], 2]
$expr//TreeForm
Out[2]= p[m[p[34, 22], m[11, 24]], p[m[6, 7], 10]]
Допустим, мы хотим создать оценщик для мини-языка, используя выражения этой формы, где p
означает «плюс», а m
означает минус. Мы можем написать оценщик рекурсивного спуска для этого мини-языка таким образом:
In[4]:=
eval1[p[a_, b_]] := eval1[a] + eval1[b]
eval1[m[a_, b_]] := eval1[a] - eval1[b]
eval1[a_] := a
In[7]:=
eval1[$expr]
Out[7]= 78
Становится утомительным, когда приходится явно писать рекурсивные вызовы eval1
в каждом из правил. Кроме того, легко забыть добавить рекурсивный вызов в правило. Теперь рассмотрим следующую версию того же оценщика:
In[8]:=
eval2[p[a_, b_]] := a + b
eval2[m[a_, b_]] := a - b
eval2[a_] := a
«Шум» рекурсивных вызовов был удален, чтобы правила легче было читать. Конечно, мы можем найти способ автоматически вставить необходимые рекурсивные вызовы? Введите //@
:
In[11]:=
eval2 //@ $expr
Out[11]= 78
Он делает то, что нам нужно. С небольшим злоупотреблением терминологией, заимствованной из функционального программирования, мы можем сказать, что мы подняли eval2
, чтобы стать функцией рекурсивного спуска. Вы можете увидеть эффект на следующей диаграмме.
In[12]:=
"eval2" //@ $expr // TreeForm
Постскриптум
В Mathematica всегда есть много способов добиться эффекта. Для этого игрушечного оценщика все предыдущее обсуждение является излишним:
In[13]:=
$expr /. {p -> Plus, m -> Subtract}
Out[13]= 78
... если бы всегда было так легко проверить, дает ли оценщик правильные результаты:)