Mathematica:获取传递给函数的参数数量?

时间:2010-06-15 17:32:55

标签: function count arguments wolfram-mathematica

如何获取传递给函数的参数数量,例如Plus[2,3,4,5]传递给它的4个参数。我在想它可能涉及使用函数Length并将参数放入列表中。目的是基于函数的参数数量迭代操作。可能有一个简单的解决方案或功能,但我还没有遇到它。还欢迎任何其他方式或建议吗?

6 个答案:

答案 0 :(得分:7)

这是一种方式:

In[1]:= foo[args___] := Length[{args}]

In[2]:= foo[1,2,3,4]
Out[2]= 4

当你定义这样的函数时,模式args___(带有3个尾部下划线)将匹配0或更多事物的Sequence。您无法在Length上使用Sequence并且发生任何明智的事情,因此您应首先将args包裹在List{})中。< / p>

然而,belisarius是正确的。对于大量迭代操作,使用内置高阶函数(如MapFold)会更容易,更有效。

要添加的EDIT:由于Mathematica表达式构建在边界检查数组之上,Length及时 O (1)。这可能会让您相信foo 具有 O (1)复杂性,但您会错。由于模式匹配的工作方式,args匹配的所有元素都将复制到新的List中,然后传递给Length,复杂性 O N )。这不一定是个大问题,因为使用带有函数的非常大的参数列表几乎总是意味着使用Apply,它执行 O N )副本无论如何,但这是你应该知道的。

再次编辑添加:还有另一种方法可以直接在正在评估的表达式上使用Length(就像Mathematica的大多数面向列表的函数一样,Length可以用于任何头部的表达式,而不仅仅是列表)。没有任何东西被复制,因为没有序列匹配并给出新的头部,并且计算其参数的函数不需要具有任何特殊属性,如HoldAll。尽管如此,通过引入副作用实际上不属于副作用的方式来利用模式匹配机制中的怪癖是一种狡猾的黑客行为,所以如果有的话,我会非常谨慎地使用它:

Module[{n},
 expr : foo[___] /; (n = Length[Unevaluated[expr]]; True) :=
  n]

变量n可能是全局变量,但Module会创建(或至少做一个伪造的)词法闭包,因此您至少可以将变量保持在本地。

答案 1 :(得分:2)

我认为您将不得不开始使用 Mathematica 的评估序列,或者可能更简单地干扰其内在函数的属性。你遇到的一个问题是 Mathematica 非常贪婪地评估,所以当你输入Plus[2,3,4,5]之后按下返回它已经完成它的东西并返回14。

你可以摆弄$Pre以达到你想要的效果。但是你可能必须Unprotect[Plus]并强制它Hold它的论点,直到你有机会计算它有多少。

当然,如果您只是以Plus为例并且真的想要定义自己的函数,那么您的任务可能会轻松得多。这是我写的一个函数,它只返回它得到的参数数量:

fun[y___]:=Length[{y}]

我在一些简单的案例中对此进行了测试。尝试以下方式对您有所帮助:

fun[1,{2,3}]

我倾向于同意已经发表的评论,你建议做的事情不是很好 Mathematica -al

答案 2 :(得分:1)

您始终可以使用列表:

f[list_]:= (len = Length[list];
            While [....
                   do whatever
                 ];
            Return [ ..];
           );

 myOut= f[{a,b,c}];

这种方式适合与Mathematica一起使用,因为列表管理非常强大。

如果使用f [a,b,c],则参数个数是硬编码的

但是再次尝试功能性的方式。

答案 3 :(得分:1)

根据我在另一个答案中的评论,通常的惯用方法是:

Length[Unevaluated[expr]]

E.g:

In[1]:= Length[Unevaluated[Plus[1, 2, 3, 4]]]

Out[1]= 4

Unevaluated的使用阻止了参数的评估,避免了Length(没有任何Hold*属性)的参数将被评估为原子值的情况(如数字)没有长度,Length在这种情况下返回0

In[2]:= Length[Plus[1, 2, 3, 4]]

Out[2]= 0

答案 4 :(得分:0)

不确定您需要什么样的递归,但根据我的经验,您的函数定义中的(Haskel启发?)first,rest模式可能非常强大:

f[onearg_]:=onearg
f[first_,rest__]:=first+2 f[rest]

In[148]= Trace@f[2,3,4]
Out[148]= {f[2,3,4],2+2 f[3,4],{{f[3,4],3+2 f[4],{{f[4],4},2 4,8},3+8,11},2 11,22},2+22,24}

当然,如果您需要,您可以访问Length[{rest}]

编辑2 :( Pilsy指出旧的情节不正确)Mathematica实际上复制了'rest'部分,因此如果实际函数的成本可以忽略不计,则缩放变为二次曲线。

f[] := 0;
f[onearg_] := onearg[[1, 1]];
f[first_, rest__] := f[first] + f[rest];
ListLogLogPlot[Part[#, -1, 1],
   Joined -> True, PlotRange -> {{100, All}, Automatic}, 
   AxesLabel -> {"#Arguments", "Runtime"}] &@
 Reap@Nest[
   Function[all, 
    Sow[{Length[all], First@Timing[Table[f @@ all, {10}]]}];
    Join[all, RandomReal[{-1, 1}, {10, 10, 10}]]], {}, 100]

下图显示了如上所述的廉价内部函数f[onearg_]:=onearg[[1,1]]的输出,其中缩放的参数数量确实是二次的,并且对于昂贵的内部函数f[onearg_]:=SingularValueList[onearg,1],其中缩放更接近于线性的。

output of above code

答案 5 :(得分:0)

上面的一些解决方案要求您将参数显式输入到将其放入列表的函数中。通过我自己的研究,我发现有办法计算答案。给定表达式3 + 2*2*2*4 + 5,找出传递给Times函数的参数数量。通过使用TreeForm函数对其进行可视化的一些帮助,我将一些mathematica的内置函数放在一起来评估答案。

步骤:
1 /获取功能的位置。
2 /它返回一个嵌套列表 3 /展平清单。
4 /获取列表的长度,该长度将是函数的参数所在的级别 5 / Level返回一个参数列表,然后你可以得到它的长度。

示例:

In[89]:= Position[Hold[3 + 2*2*2*4 + 5], Times]

Out[89]= (1    2    0)

In[90]:= FullForm[%]

Out[90]= List[List[1,2,0]]

In[91]:= Flatten[%]

Out[91]= {1,2,0}

In[92]:= FullForm[%]

Out[92]= List[1,2,0]

In[93]:= Length[%]

Out[93]= 3

In[94]:= Level[Hold[3 + 2*2*2*4 + 5], {%}]

Out[94]= {2,2,2,4}

In[95]:= Length[%]

Out[95]= 4

这可以全部放在一个功能中。 虽然在表达式中以自动方式存在两个相同功能的实例时,它可能无法处理。这可能需要用户设置或输入一些条件。