Подтвердить что ты не робот

Карьера с Mathematica

В Mathematica можно использовать ограниченную форму Currying, используя следующую конструкцию:

f[a_][b_][c_] := (a^2 + b^2)/c^2

Позволяет сделать это, например:

f[4][3] /@ [email protected]
  {25, 25/4, 25/9, 25/16, 1}

Возникает проблема: Attributes применяется только к первому (множеству) аргументов. Рассмотрим:

ClearAll[f]
SetAttributes[f, HoldAllComplete]

f[a_][b_][c_] :=
  {[email protected]@a,
   [email protected]@b,
   [email protected]@c}

f[2 + 2][ 8/4 ][3 + 5]
   {"2 + 2", "2", "8"}  

Мое намерение состояло в возврате "8 / 4" и "3 + 5" в список.


Следовательно:

  • Есть ли способ расширить атрибуты этой конструкции?

  • Есть ли еще одна удобная конструкция для этого?

  • Существуют ли другие способы, помимо атрибутов, расширить Currying внутри Mathematica?

4b9b3361

Ответ 1

Извините за возможный несвязанный комментарий. Я просто искал "currying with Mathematica", и этот вопрос был первым в списке Google. Хотя, уже 1 год и уже получил ответ, я обнаружил, что представленные решения не очень элегантны. Простая модификация исходного кода должна быть следующей:

ClearAll[f]
SetAttributes[f, HoldAllComplete]
f[a_, b_, c_] := {[email protected]@a, [email protected]@b,
[email protected]@c}
f[a__] := Function[x, f[a, x], HoldAll]

Это приводит к желаемому переносу:

f[2+2][2+1] /@ [email protected]{1+1, 3+3}{{2+2, 2+1, 1+1}, {2+2, 2+1, 3+3}}

Он отлично работает для трех возможных разделов аргументов

f[1 + 1, 2 + 2, 6 + 1]
f[1 + 1, 2 + 2][6 + 1]
f[1 + 1][2 + 2][6 + 1]

и дает правильный результат: {"1+1", "2+2", "6+1"}}, но он не работает для f[1 + 1][2 + 2, 6 + 1]. Для этого можно использовать немного более продвинутую версию:

ClearAll[f, g]
SetAttributes[f, HoldAllComplete]
SetAttributes[g, HoldAllComplete]
f[a_, b_, c_] := (ClearAll[g]; SetAttributes[g, HoldAllComplete]; 
  Thread[Hold[{a, b, c}]] /. {Hold[e_] :> [email protected][e]})
f[a__] := (g[x__] := f[a, x]; g)

Ответ 2

Я не думаю, что есть какие-либо способы применения атрибутов к более поздним частям такого определения шаблона "upvalue".

Один из вариантов - использовать чистые функции с атрибутами. Не так удобно, как сопоставление с образцом, но когда вы оцениваете f[2+2][8/4], это фактически дает результат, который понравился бы Карри. ( "Функция" - это математика "лямбда", если вы знакомы с лямбда-исчислением.)

f = Function[a,Function[b,Function[c,[email protected]{a,b,c},HoldAll],HoldAll],HoldAll]

Я предполагаю, что вы хотите сделать что-то вроде следующего:

f[2+2][2/1] /@ [email protected]{1+1,3+3}{{2+2, 2/1, 1+1}, {2+2, 2/1, 3+3}}

Если вы собираетесь часто делать такие вещи, вы можете немного облегчить их ввод:

hf[args_,body_]:=Function[args,body,HoldAll]; SetAttributes[hf,HoldAll];

f = hf[a, hf[b, hf[c, [email protected]{a, b, c}]]]

Поваренная книга Mathematica представляет собой совершенно другой подход к Currying на страницах 73-77.

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

Ответ 3

Я не знаю, как расширить атрибуты во втором или более позднем списке аргументов в карри - хотя я бы хотел услышать об этом.

Вы можете реализовать определения для выражений с тем же внешним видом, что и выражение curries, используя чистые функции (хотя я бы с сомнением назвал его "удобным" ):

ClearAll[f, f1, f2]
SetAttributes[{f, f1, f2}, HoldAllComplete]
f[a_] := Function[b, f1[a, b], HoldAllComplete]
f1[a_, b_] := Function[c, f2[a, b, c], HoldAllComplete]
f2[a_, b_, c_] :=
  { [email protected]@a
  , [email protected]@b
  , [email protected]@c
  }

f[2+2][8/4][3+5]

Можно сопоставить шаблон с выражением curried, используя теперь недокументированный символ HeadCompose:

In[65]:= MatchQ[g[x][y][z], HeadCompose[g, x_, y_, z_]]
Out[65]= True

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

Ответ 4

Поздняя вечеринка - так что не прямой ответ на вопрос (на который хорошо ответили другие сообщения). Я просто хочу указать, что можно иметь форму нелокального контроля над оценкой с помощью Stack и исключений. Это немного уродливо, но я не думаю, что он был полностью изучен. Вот пример:

ClearAll[f];
f := With[{stack = Stack[_]},
   With[{fcallArgs = 
      Cases[stack, HoldForm[f[x_][y_][z_]] :> Hold[x, y, z]]},
      Throw[[email protected]] /; fcallArgs =!= {}]];


In[88]:= Catch[f[2+2][8/4][3+5]]

Out[88]= Hold[2+2,8/4,3+5]

Это использует тот факт, что головы оцениваются перед элементами, рекурсивно. То, что вы можете видеть здесь, состоит в том, что вы можете извлечь неоцененные аргументы таким образом и, возможно, использовать их в дальнейшей обработке. Однако вычисление прерывается. Также должно быть возможно извлечь достаточную информацию из Stack[_], чтобы возобновить вычисление. Я не уверен, можно ли реализовать продолжения в Mathematica, но если это так, это должно быть, вероятно, в этом направлении.

Ответ 5

Есть способ сделать это автоматически. Рассмотрим функцию

f[a_, b_, c_] := {a, b, c}

для которого мы хотим сделать его неявным "curryable", поэтому его можно было бы вызвать любым из следующих способов:

f[1, 2, 3]
f[1, 2][3]
f[1][2][3]

Это может быть достигнуто, если есть способ автоматически генерировать следующие определения (что мы делаем ниже):

f[a_, b_, c_] := {a, b, c}
f[a_, b_] := Function[c, f[a, b, c]]
f[a_] := Function[b, Function[c, f[a, b, c]]]

Как и в другом ответе, сделанном Мэттом, мы могли бы сделать только одно определение: f: = Funcion [a, Function [b, Function [c, BODY]]], но тогда мы не сможем вызвать f через f [a, b, c] или f [a, b], и ему придется называть это только как f [a] [b] или f [a] [b] [c]. С помощью нескольких определений мы можем выбрать либо стили.

Создание этих определений может быть выполнено с помощью функции (определенной ниже) CurryableSetDelayed, просто вызывая:

CurryableSetDelayed[f[a_, b_, c_], {a, b, c}]

Это будет работать, как и ожидалось, даже если какой-либо из этих символов определен, как работает SetDelayed.

Кроме того, с пакетом Notation вы можете заставить его отображаться как оператор присваивания; скажем f [a_, b_, c] # = {c, b, a}, но я не пробовал.

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

Полный код:

ClearAll[UnPattern];
ClearAll[MakeFunction]
ClearAll[CurriedDefinitions]
ClearAll[MyHold]
ClearAll[MyHold2]
ClearAll[CurryableSetDelayed]

SetAttributes[UnPattern,HoldAllComplete];
SetAttributes[MakeFunction,HoldAllComplete];
SetAttributes[CurriedDefinitions,HoldAllComplete]
SetAttributes[MyHold,HoldAllComplete]
SetAttributes[MyHold2,HoldAllComplete]
SetAttributes[CurryableSetDelayed,HoldAllComplete]

UnPattern[x_]:=Block[{pattern},MyHold[x]/. Pattern->pattern/. pattern[v_,_]:>v]

MakeFunction[param_,body_,attrs_]:=With[{p=UnPattern[param],b=UnPattern[body]},
  Block[{function},MyHold[function[p,b,attrs]]/. function->Function]]

CurriedDefinitions[fname_[args__],body_,attrs_]:=MapThread[MyHold2[#1:=#2]&,
  {Rest[(MyHold[fname]@@#1&)/@NestList[Drop[#1,-1]&,{args},Length[{args}]-1]],
   Rest[FoldList[MakeFunction[#2,MyHold[#1],Evaluate[attrs]]&,MyHold[fname[args]],
     Reverse[Drop[{args},1]]]]}]

CurryableSetDelayed[fname_[args__],body_]:={MyHold2[fname[args]:=body],
  [email protected]@CurriedDefinitions[fname[args],body,Attributes[fname]]}
  //. MyHold[x_]:>x/. MyHold2[x_]:>x

Update, теперь атрибуты (HoldAllComplete и т.д.) распространяются на все параметры, поэтому следующее работает так, как ожидалось, до тех пор, пока вы устанавливаете атрибуты до, вызывающие CurryableSetDelayed:

In[1185]:= ClearAll[f];
SetAttributes[f, {HoldAllComplete}]
CurryableSetDelayed[
  f[a_, b_, c_], {[email protected]@a, [email protected]@b, 
   [email protected], [email protected]}];
f[1 + 1, 2 + 2, c + 1]
f[1 + 1, 2 + 2][c + 1]
f[1 + 1][2 + 2][c + 1]

Out[1188]= {"1 + 1", "2 + 2", Unevaluated[c + 1], Hold[c + 1]}

Out[1189]= {"1 + 1", "2 + 2", Unevaluated[c + 1], Hold[c + 1]}

Out[1190]= {"1 + 1", "2 + 2", Unevaluated[c + 1], Hold[c + 1]}