Currying with Mathematica

后端 未结 5 1134
無奈伤痛
無奈伤痛 2020-12-23 07:30

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

5条回答
  •  半阙折子戏
    2020-12-23 08:12

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

提交回复
热议问题