Карри с Mathematica - PullRequest
       7

Карри с Mathematica

30 голосов
/ 16 апреля 2011

Можно реализовать ограниченную форму Curry в Mathematica, используя эту конструкцию:

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

Разрешение сделать, например:

f[4][3] /@ Range@5
  <b>{25, 25/4, 25/9, 25/16, 1}</b>

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

ClearAll[f]
SetAttributes[f, HoldAllComplete]

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

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

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


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

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

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

  • Есть ли другие способы, кроме атрибутов, расширить карринг в Mathematica?

Ответы [ 5 ]

18 голосов
/ 16 апреля 2011

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

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

f = Function[a,Function[b,Function[c,HoldForm@{a,b,c},HoldAll],HoldAll],HoldAll]

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

f[2+2][2/1] /@ Unevaluated@{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, HoldForm@{a, b, c}]]]

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

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

13 голосов
/ 19 июля 2012

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

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

Результатом является желаемое ношение:

f[2+2][2+1] /@ Unevaluated@{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_] :> ToString@Unevaluated[e]})
f[a__] := (g[x__] := f[a, x]; g)
10 голосов
/ 16 апреля 2011

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

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

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_] :=
  { ToString@Unevaluated@a
  , ToString@Unevaluated@b
  , ToString@Unevaluated@c
  }

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

В можно сопоставить шаблонное выражение с использованием теперь недокументированного символа HeadCompose:

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

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

6 голосов
/ 16 ноября 2011

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

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 будет работать.

Кроме того, с пакетом Нотации вы можете сделать так, чтобы он отображался как оператор присваивания;скажем 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],
  Sequence@@CurriedDefinitions[fname[args],body,Attributes[fname]]}
  //. MyHold[x_]:>x/. MyHold2[x_]:>x

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

In[1185]:= ClearAll[f];
SetAttributes[f, {HoldAllComplete}]
CurryableSetDelayed[
  f[a_, b_, c_], {ToString@Unevaluated@a, ToString@Unevaluated@b, 
   Unevaluated@c, Hold@c}];
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]}
6 голосов
/ 18 апреля 2011

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

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


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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...