Использует для MapAll (//@)
Функция MapAll
считалась достаточно важной, чтобы гарантировать короткую форму //@
, но я редко ее использую, особенно по сравнению с другими, такими как /@
, /.
и @@@
, которые я использую почти везде.
-
Какие приложения лучше всего использовать MapAll
?
-
Используется ли он в основном в определенных полях или стилях программирования?
-
Как часто его можно использовать по сравнению с другими операторами?
Ответы
Ответ 1
//@
- это "обход дерева после заказа". Он посещает каждый node в древовидной структуре, причем каждый node ребенок посещается до самого node. Поставляемая функция вызывается с каждым node в качестве аргумента, а дети node уже были "расширены" предыдущим вызовом. Дерево структуры данных являются общими, а также необходимость их прохождения. Но осмелюсь сказать, что основной случай использования //@
в контексте 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]]
![expression tree]()
Скажем, что мы хотим создать оценщика для мини-языка с использованием выражений этой формы, где 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
![eval2 expansion]()
Postscript
В Mathematica всегда есть много способов добиться эффекта. Для этого оценщика игрушек все предыдущее обсуждение является излишним:
In[13]:=
$expr /. {p -> Plus, m -> Subtract}
Out[13]= 78
... если только всегда было легко проверить, дает ли оценщик правильные результаты:)
Ответ 2
Я использовал его несколько раз, чтобы сделать инертное представление кода, который может выполняться и который вы хотите преобразовать или разрушить без какой-либо оценки. Вот пример:
ClearAll[myHold, makeInertCode];
SetAttributes[{myHold, makeInertCode}, HoldAll];
makeInertCode[code_] :=
MapAll[myHold, Unevaluated[code], Heads -> True]
Вот пример:
In[27]:=
icd = makeInertCode[
With[{x = RandomInteger[{1, 10}, 20]},
Extract[x, Position[x, _?OddQ]]]
]
Out[27]= myHold[myHold[With][myHold[myHold[List][myHold[myHold[Set][myHold[x],
myHold[myHold[RandomInteger][myHold[myHold[List][myHold[1],myHold[10]]],myHold[20]]]]]]],
myHold[myHold[Extract][myHold[x], myHold[myHold[Position][myHold[x], myHold[myHold[
PatternTest][myHold[myHold[Blank][]], myHold[OddQ]]]]]]]]]
Теперь мы можем использовать стандартные инструменты деструкции без опасности преждевременной оценки кода (чтобы быть уверенным, myHold
может быть задан HoldAllComplete
, а не HoldAll
):
In[28]:= Cases[icd, myHold[Extract][___], Infinity]
Out[28]= {myHold[Extract][myHold[x],
myHold[myHold[Position][myHold[x],
myHold[myHold[PatternTest][myHold[myHold[Blank][]],
myHold[OddQ]]]]]]}
как только код преобразуется/разрушается, его можно обернуть в Hold
или HoldComplete
, а затем обертки myHold
можно удалить, например, с помощью правила типа myHold[x___]:>x
, применяемого повторно. Но, как правило, добавленная ценность MapAll мне кажется довольно ограниченной, потому что, в частности, Map
с техникой уровня {0,Infinity}
эквивалентна ей. Я не думаю, что это часто используется.
Ответ 3
Я не использую его, но имеет какое-то забавное поведение для функций Listable. Например:
Если вы хотите, чтобы каждый элемент списка имел функцию, применяемую к нему несколько раз в зависимости от своей вложенной глубины в списке, я предполагаю, что это единственный раз, когда я видел его.
SetAttributes[f, Listable]
(f //@ {{a}, {{b}}, c}) // Flatten
{f[f[f[a]]], f[f[f[f[b]]]], f[f[c]]}
Вообще, хотя я бы предположил, что вы можете использовать ReplaceAll всякий раз, когда вы будете использовать это.
Ответ 4
Я бы использовал его как ленивый способ применить алгебраические выражения к объектам, с которыми не работают алгебраические функции:
In[13]:= ser = 1/(1 + x)^a + O[x]^4
Out[13]= SeriesData[x, 0, {
1, -a, Rational[1, 2] (a + a^2),
Rational[1, 6] ((-2) a - 3 a^2 - a^3)}, 0, 4, 1]
In[14]:= Factor[ser]
Out[14]= SeriesData[x, 0, {
1, -a, Rational[1, 2] (a + a^2),
Rational[1, 6] ((-2) a - 3 a^2 - a^3)}, 0, 4, 1]
In[15]:= MapAll[Factor, ser]
Out[15]= SeriesData[x, 0, {
1, -a, Rational[1, 2] a (1 + a),
Rational[-1, 6] a (1 + a) (2 + a)}, 0, 4, 1]