如何获取传递给函数的参数数量,例如Plus[2,3,4,5]
有4个参数传递给它。我认为这可能涉及使用函数Length并将参数放入列表中。目的是根据函数的参数数量迭代操作。可能存在简单的解决方案或函数,但我尚未遇到它。欢迎提出任何其他方法或建议。
如何获取传递给函数的参数数量,例如Plus[2,3,4,5]
有4个参数传递给它。我认为这可能涉及使用函数Length并将参数放入列表中。目的是根据函数的参数数量迭代操作。可能存在简单的解决方案或函数,但我尚未遇到它。欢迎提出任何其他方法或建议。
这里有一种方法:
In[1]:= foo[args___] := Length[{args}]
In[2]:= foo[1,2,3,4]
Out[2]= 4
args___
(带有3个下划线)将匹配0个或多个东西的Sequence
。您无法在Sequence
上使用Length
并获得任何明智的结果,因此您应该首先将args
包装在List
(即{}
)中。Map
和Fold
会更容易和更有效。
编辑以添加: 由于 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
将创建(或至少伪造)词法闭包,因此您至少可以保持变量的局部性。fun[y___]:=Length[{y}]
我已经在一些简单的情况下进行了测试。你可以尝试以下操作,这将对你有所帮助:
fun[1,{2,3}]
Flat
属性时,事情变得更有趣了。如果 f
是 Flat
(SetAttributes[f, Flat]
),那么 f[1, f[2, 3]]
就会变成 f[1, 2, 3]
。这取决于你想如何计算。=) - Michael PilatLength[Unevaluated[Plus[1,2,3,4]]]
会输出4
。 - Michael PilatUnevaluated
只有在作为另一个函数的参数时才防止其参数的求值,但 Hold
则始终防止其内容的求值。因此,Length[Unevaluated[Plus[1,2,3,4]]]
实际上计算的是 Length[Plus[1,2,3,4]]
,而不是如上所述的 Length[10]
。为了更好地理解,建议参考 非标准求值 教程。Hold[Plus[1,2,3,4]]
的长度为 1
,因为 Hold 表达式只有一个参数。 - Michael Pilat你总是可以使用列表:
f[list_]:= (len = Length[list];
While [....
do whatever
];
Return [ ..];
);
myOut= f[{a,b,c}];
这种方法适用于Mathematica,因为列表管理非常强大。
如果您使用f [a,b,c],则参数数量是硬编码的
但再次尝试函数式方法。
根据我在另一个答案中的评论,通常做法是使用惯用语:
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
不确定您需要哪种递归,但根据我的经验,在函数定义中使用(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}]
。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]
,其中缩放更接近于线性。
f
——如果我有一个退出内核的键盘快捷键就好了。
新的(希望正确的)时间与您在二次缩放方面达成共识,正如您所见。尽管如此,如果主要成本在内部函数中(如图的右侧所示),总计时仍然是线性的,因此我不会停止使用这种模式。 - Janus以上一些解决方案需要您将参数明确输入到一个函数中,然后将其放入列表中。通过自己的研究,我发现有一种方法可以计算答案。给定表达式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
这些内容都可以放在一个函数中。 虽然它可能无法自动处理表达式中存在两个相同函数实例的情况。这可能需要一些条件设置或用户输入。