使用 Mathematica 进行柯里化

发布于 2024-11-01 14:47:01 字数 825 浏览 0 评论 0原文

使用此构造,可以在 Mathematica 中实现有限形式的 Currying

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

允许一个人做,例如:

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

有一个问题:属性仅适用于第一个(一组)参数。考虑一下:

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]
   {"2 + 2", "2", "8"}  

我的意图是在列表中返回 "8 / 4""3 + 5"


因此:

  • 有没有办法将属性扩展到此构造?

  • 是否有另一个方便的构造来实现此目的?

  • 除了属性之外,还有其他方法可以在 Mathematica 中扩展 Currying 吗?

One may implement a limited form of Currying in Mathematica, using this construct:

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

Allowing one to do, for example:

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

There is a problem: Attributes only apply to the first (set of) argument(s). Consider:

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]
   {"2 + 2", "2", "8"}  

My intent was to return "8 / 4" and "3 + 5" in the list.


Consequently:

  • Is there a way to extend attributes to this construct?

  • Is there another convenient construct to achieve this?

  • Are there other ways, besides attributes, to extend Currying within Mathematica?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

孤千羽 2024-11-08 14:47:02

我认为没有任何方法可以将属性应用于此类“upvalue”模式定义的后续部分。

一种替代方法是使用带有属性的纯函数。不像模式匹配那么方便,但是当你计算 f[2+2][8/4] 时,它实际上给出了 Curry 想要的结果。 (如果您熟悉 lambda 演算,“Function” 就是 Mathematica 的“lambda”。)

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

我想您希望能够执行如下操作:

f[2+2][2/1] /@未计算@{1+1,3+3}    →    {{2+2, 2/1, 1+1}, {2+2, 2/1, 3+3}}

如果您要经常执行此类操作,则可以稍微简化输入操作在:

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

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

Mathematica 食谱 在第 73-77 页上介绍了一种相当不同的柯里化方法。

作为一般准则,如果您尝试控制 Mathematica 计算表达式的时间,您会让自己很痛苦。在许多情况下,更好的方法是使用符号作为您还不想计算的表达式的占位符,然后当需要计算时,您可以用所需的表达式替换该符号。

I don't think there is any way to have attributes apply to the later parts of such an "upvalue" pattern definition.

One alternative is to use pure functions with attributes. Not as convenient as pattern matching, but when you evaluate f[2+2][8/4], it actually gives a result Curry would have liked. ("Function" is Mathematica's "lambda", if you're familiar with lambda calculus.)

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

I presume you want to be able to do something like the following:

f[2+2][2/1] /@ Unevaluated@{1+1,3+3}     →    {{2+2, 2/1, 1+1}, {2+2, 2/1, 3+3}}

If you're going to do this sort of thing often, you can make it slightly easier to type in:

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

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

The Mathematica Cookbook presents a rather different approach to Currying on pages 73-77.

As a general guideline, if you try to control when Mathematica evaluates expressions, you will make yourself miserable. A better approach in many situations is to use symbols as placeholders for the expressions you don't want evaluated yet, and then when the time comes to evaluate one, you can substitute the desired expression for the symbol.

古镇旧梦 2024-11-08 14:47:02

抱歉,可能有不相关的评论。我刚刚搜索了“currying 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)

Sorry for a probably unrelated comment. I just searched «currying with Mathematica» and this question was the first in Google list. Although, it is 1 year old and already got answer, I found that the solutions presented are not quite elegant imho. The simple modification of the initial code should be as follows:

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]

It results in the desired carrying:

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

It works fine for three possible partitions of arguments

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

and gives the correct result:
{"1+1", "2+2", "6+1"}}, but it fails for f[1 + 1][2 + 2, 6 + 1]. For this one, one can use a little bit more advanced version:

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)
上课铃就是安魂曲 2024-11-08 14:47:02

我不知道有什么方法可以将属性扩展到第二个或后面的柯里化参数列表——尽管我很想听听。

您可以使用纯函数来实现与柯里化表达式具有相同外观的表达式定义(尽管我不愿意称其为“方便”):

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 系统中,并且可能会发生变化。

I'm not aware of any way to extend attributes to the second or later curried argument lists -- although I'd love to hear about one.

You can implement definitions for expressions with the same appearance as the curried expression by using pure functions (although I would hesitate to call it "convenient"):

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]

It is possible to pattern match a curried expression using the now undocumented symbol HeadCompose:

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

... although this capability does not help in the matter at hand. HeadCompose was deprecated a few versions ago, to the point of it finally being removed from the documentation. But I am not aware of any other way to pattern-match curried expressions. I speculate that it was deprecated precisely because one cannot effectively attach attributes and definitions to it, giving it that dreaded status: This symbol has not been fully integrated into the long-term Mathematica system, and is subject to change.

孤寂小茶 2024-11-08 14:47:02

参加聚会迟到了 - 所以不是问题的直接答案(其他帖子已经很好地回答了)。我只是想指出,可以通过使用 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 中实现延续,但如果可以的话,应该是这样的。

Coming late to the party - so not a direct answer to the question (which has been answered by other posts quite well). I just want to point out that one can have a form of non-local control over the evaluation by using Stack and exceptions. It is a bit ugly, but I don't think it has been fully explored. Here is an example:

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]

This uses the fact that heads are evaluated before elements, recursively. What you can see from here is that one is able to extract the unevaluated arguments in this way, and can, perhaps, use them in further processing. The computation is interrupted though. It should also be possible to extract enough information from the Stack[_] to resume the computation. I am not sure whether one can implement continuations in Mathematica, but if so, that should be probably along these lines.

锦爱 2024-11-08 14:47:02

有一种方法可以自动完成。 函数

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

考虑我们想要使其隐式“可柯里化”的

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]]]

,因此可以通过以下任何方式调用它: 如果有一种方法可以自动生成以下定义(我们在 根据 Matt 上面的另一个答案,我们可以只做一个定义: f:=Function[a,Function[b,Function[c, BODY]]],但是这样我们将无法通过 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],
  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]}

There is a way to do it automatically. Consider the function

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

for which we want to make it implicitly "curryable", so it could be called in any of these ways:

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

This could be achieved if there is a way to generate the following definitions automatically (which we do below):

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]]]

As in the other answer above by Matt, we could have done only one definition: f:=Funcion[a,Function[b,Function[c, BODY]]], but then we will not be able to call f via f[a,b,c] or f[a,b], and will have to call it only as f[a][b] or f[a][b][c]. With multiple definitions we can choose either styles.

Generating these definitions could be done by the function (defined below) CurryableSetDelayed, simply by calling:

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

This will work as expected even if any of these symbols is defined, just like SetDelayed would work.

Also, with Notation package you could make it appear as an assignment operator; say f[a_,b_,c]#={c,b,a}, but I didn't try it.

In the source below I use some ad hoc symbols which may conflict with the session, so if you are going to use this, enclose it in a package namespace.

The full code:

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

Update, now Attributes (HoldAllComplete,etc.) extends to all parameters, so the following works as expected, as long as you set the attributes before calling 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]}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文