Использует для MapAll (// @) - PullRequest
       16

Использует для MapAll (// @)

10 голосов
/ 21 апреля 2011

Функция MapAll была сочтена достаточно важной, чтобы гарантировать краткую форму //@, но я редко использую ее, особенно по сравнению с другими, такими как /@, /. и @@@, которые я использую почти везде.

  • Какие приложения лучше всего использовать MapAll?

  • Используется ли оно в основном в определенных областях или стилях программирования?

  • Как часто его можно использовать по сравнению с другими операторами?

Ответы [ 4 ]

13 голосов
/ 22 апреля 2011

//@ - это «обход дерева после заказа». Он посещает каждый узел в древовидной структуре, дочерние узлы которого посещаются до самого узла. Предоставленная функция вызывается с каждым узлом в качестве аргумента, дочерние узлы которого уже «расширены» предыдущим вызовом. Древовидные структуры данных являются общими, а также необходимость их обхода. Но я осмелюсь сказать, что основной вариант использования //@ в контексте 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

Постскриптум

В Mathematica всегда есть много способов добиться эффекта. Для этого игрушечного оценщика все предыдущее обсуждение является излишним:

In[13]:=
         $expr /. {p -> Plus, m -> Subtract}

Out[13]= 78

... если бы всегда было так легко проверить, дает ли оценщик правильные результаты:)

9 голосов
/ 22 апреля 2011

Я использовал его несколько раз, чтобы сделать инертное представление кода, который может выполняться и который вы хотите преобразовать или деструктурировать без какой-либо оценки. Вот пример:

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} эквивалентно ей. Я не думаю, что это часто используется.

7 голосов
/ 21 апреля 2011

Я не пользуюсь им, но немного забавен для Listable функций.Например:

Если вы хотите, чтобы к каждому элементу в списке была применена функция несколько раз, в зависимости от его вложенной глубины в списке, я думаю, это единственный раз, когда я видел его использование.

SetAttributes[f, Listable]

(f //@ {{a}, {{b}}, c}) // Flatten
{f[f[f[a]]], f[f[f[f[b]]]], f[f[c]]}

В общем, хотя я бы подумал, что вы можете использовать ReplaceAll всякий раз, когда будете использовать это.

4 голосов
/ 21 апреля 2011

Я бы использовал его как ленивый способ применять алгебраические выражения к объектам, с которыми алгебраические функции не работают:

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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...