Карьера с 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?
Ответы
Ответ 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]}