Использует для 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]