在Mathematica中,为什么递归函数中的替换不会终止?

nan*_*ous 7 recursion replace wolfram-mathematica

想象一下,我在Mathematica中定义了一个递归因子,如下所示:

Clear[fact]
fact[0] = 1
fact[n_] := n fact[n - 1]
Run Code Online (Sandbox Code Playgroud)

评估事实[10]证实该功能有效并终止.

这是一个主要的例子,但它在这个问题中起到了作用.实际上,无论如何,我的问题一般都与递归函数定义有关.

我期望评估以下替换以终止:

x fact[x-1] /. x -> 2
Run Code Online (Sandbox Code Playgroud)

唉,它运行到递归深度限制:

$RecursionLimit::reclim: Recursion depth of 256 exceeded.
Run Code Online (Sandbox Code Playgroud)

我希望看到类似的东西:

2 fact[2-1]
Run Code Online (Sandbox Code Playgroud)

或只是价值

2
Run Code Online (Sandbox Code Playgroud)

更新:事实 的另一种递归定义确实按预期工作:

Clear[fact]
fact[n_] := If[n < 1, 1, n fact[n - 1]]
Run Code Online (Sandbox Code Playgroud)

但是这个事实(双关语意图;-)让我更加神秘:它为什么表现得如此不同?

我的问题是双重的:

  1. 即使有内置的帮助和搜索网络的线索,我也无法解释为什么Mathematica坚持,显然,保持符号结果,而不是评估"中间"结果并很好地终止.谁冒险进行了可行的解释?

  2. 我如何说服Mathematica根据我的预期执行(除了使用If [] 之外的替代方案)?

我真的对这个感到困惑,我真的希望有人可以帮助我.

/ M炼

acl*_*acl 6

尝试u[x_] := x; Trace[x*u[x] /. x -> 2],它首先评估xu[x].在您的情况下,它首先尝试fact[x-1]在替换x为2 之前进行求值,并达到递归限制.

Attributes[ReplaceAll]表明它没有属性HoldFirst,所以它首先评估它的第一个参数.例如,

ReleaseHold@ReplaceAll[Hold[x*fact[x - 1]], x -> 2]
Run Code Online (Sandbox Code Playgroud)

给出预期的2,因为它拥有第一个参数,替换,然后按预期释放保持.

另一种方法是

Unprotect[ReplaceAll]
SetAttributes[ReplaceAll, HoldFirst]
ReplaceAll[x*fact[x - 1], x -> 2]
Run Code Online (Sandbox Code Playgroud)

但不要这样做.

当然有人会尽快给出更好的解释.

编辑:回答关于原因的补充问题

Clear[factif]
factif[n_] := If[n < 1, 1, n factif[n - 1]]
Run Code Online (Sandbox Code Playgroud)

不会导致无限递归:以这种方式定义,factif[x]求值为If[x < 1, 1, x factif[x - 1]],因为x<1无法求值.因此,在尝试评估第一个参数后ReplaceAll,它仍然保持这种形式,然后更换发生等.


Arn*_*ing 5

这是因为你正在评估这个:

fact[x-1]
Run Code Online (Sandbox Code Playgroud)

在更换之前.只是做fact[x-1],你得到错误.

您可以fact像这样修复您的功能:

Clear[fact]
fact[0] = 1
fact[n_Integer?Positive] := n fact[n - 1]
Run Code Online (Sandbox Code Playgroud)

然后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)
Run Code Online (Sandbox Code Playgroud)

所以类似的东西fact["x"]会失败并带有消息:

fact::nicetrybuddy: fact[x] is not defined.
Run Code Online (Sandbox Code Playgroud)