想象一下,我在Mathematica中定义了一个递归因子,如下所示:
Clear[fact]
fact[0] = 1
fact[n_] := n fact[n - 1]
评估事实[10]证实该功能有效并终止。
这是一个主要的例子,但它在这个问题中起到了作用。实际上,我的问题一般都与递归函数定义有关。
我期望评估以下替换以终止:
x fact[x-1] /. x -> 2
唉,它运行到递归深度限制:
$RecursionLimit::reclim: Recursion depth of 256 exceeded.
我希望看到类似的内容:
2 fact[2-1]
或只是值
2
更新:事实 的替代递归定义按预期工作:
Clear[fact]
fact[n_] := If[n < 1, 1, n fact[n - 1]]
但是这个事实(双关语意图;-)让我更加神秘:它为什么表现得那么不同?
我的问题有两个:
即使有内置的帮助并在网上寻找线索,我也无法解释为什么Mathematica坚持,显然,保持符号结果,而不是评估中间人&#39; ;结果并很好地终止。谁冒险进行了可行的解释?
我如何说服Mathematica根据我的预期执行(除了使用如果 []使用替代方案)?
我真的对这个感到困惑,我真的希望有人可以帮助我。
/ M炼
答案 0 :(得分:6)
尝试u[x_] := x; Trace[x*u[x] /. x -> 2]
,首先评估x
和u[x]
。在您的情况下,它首先尝试在将fact[x-1]
替换为2之前评估x
,并达到递归限制。
Attributes[ReplaceAll]
表明它没有属性HoldFirst
,所以它首先评估它的第一个参数。例如,
ReleaseHold@ReplaceAll[Hold[x*fact[x - 1]], x -> 2]
给出预期的2
,因为它拥有第一个参数,替换,然后按预期释放保持。
另一种方法是
Unprotect[ReplaceAll]
SetAttributes[ReplaceAll, HoldFirst]
ReplaceAll[x*fact[x - 1], x -> 2]
但不要这样做。
不过有人会尽快给出更好的解释。
编辑:回答关于原因的补充问题
Clear[factif]
factif[n_] := If[n < 1, 1, n factif[n - 1]]
不会导致无限递归:以这种方式定义,factif[x]
求值为If[x < 1, 1, x factif[x - 1]]
,因为x<1
无法求值。因此,在尝试评估ReplaceAll
的第一个参数后,它仍然保持这种形式,然后发生替换等。
答案 1 :(得分:5)
这是因为你正在评估这个:
fact[x-1]
在更换之前发生。只需fact[x-1]
即可得到错误。
您可以像这样修复fact
功能:
Clear[fact]
fact[0] = 1
fact[n_Integer?Positive] := n fact[n - 1]
然后x fact[x - 1] /. x -> 2
返回2
,这似乎是正确的。
请记住,您的函数参数模式fact[n_]
非常一般。例如,它允许评估fact[Integrate[Sin[x], x]]
之类的内容,这可能不是您想要的。使用fact[n_Integer]
更精确,并允许fact
函数按您希望的方式运行。
如果您想更好地定义此功能,您可以执行以下操作:
Clear[fact]
fact::nicetrybuddy = "fact[``] is not defined.";
fact[0] = 1
fact[n_Integer?Positive] := n fact[n - 1]
fact[n_] := (Message[fact::nicetrybuddy, n]; $Failed)
因此fact["x"]
之类的内容会失败并显示一条消息:
fact::nicetrybuddy: fact[x] is not defined.
答案 2 :(得分:1)
其他答案是正确的:fact
在其参数被替换之前进行评估。关键问题是您已经为fact
定义了整数输入,并且没有为非整数输入提供终端条件。如果你做了
Clear[fact]
fact[0] = 1
fact[n_Integer?Positive] := n fact[n - 1]
然后fact
将被取消评估,直到它与正整数相匹配。
您可能需要将替换语句包装在Evaluate
中,然后在替换其参数后触发fact
的定义。
另一种方法可能是使用纯函数:
# fact[#-1]& @ 2
这不应该过早评估。