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

发布于 2024-12-17 02:31:09 字数 921 浏览 1 评论 0原文

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

Clear[fact]
fact[0] = 1
fact[n_] := n fact[n - 1]

评估fact[10] 确认该函数有效并终止。

这是一个主要的例子,但它在这个问题中达到了它的目的。实际上,我的问题总体上与递归函数定义有关。

我预计评估以下替换也将终止:

x fact[x-1] /. x -> 2

唉,它遇到了递归深度限制:

$RecursionLimit::reclim: Recursion depth of 256 exceeded.

我预计会看到类似的内容:

2 fact[2-1]

或只是值

2

UPDATE:fact 的替代递归定义 确实按预期工作:

Clear[fact]
fact[n_] := If[n < 1, 1, n fact[n - 1]]

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

我的问题是双重的:

  1. 即使有内置的帮助并在网上搜索线索,我也无法解释为什么 Mathematica 显然坚持保留符号结果,而不是评估“中间”结果并很好地终止。谁敢提出可行的解释?

  2. 我如何说服 Mathematica 按照我的期望执行(除了使用 If[] 的替代方案)?

我真的很困惑这个问题,我真的希望有人能帮助我。

/吐温

Imagine I've defined a recursive factorial in Mathematica, like this:

Clear[fact]
fact[0] = 1
fact[n_] := n fact[n - 1]

Evaluating fact[10] confirms that the function works and terminates.

A bit of a staple example, but it serves its purpose in this question. Actually, my question pertains to recursive function definitions in general anyway.

I expected evaluating the following replacement to terminate as well:

x fact[x-1] /. x -> 2

Alas, it runs in to a recursion depth limit:

$RecursionLimit::reclim: Recursion depth of 256 exceeded.

I expected to see something like:

2 fact[2-1]

or just the value

2

UPDATE: An alternative recursive definition of fact does work as expected:

Clear[fact]
fact[n_] := If[n < 1, 1, n fact[n - 1]]

But this fact (pun intended ;-) makes it even more mysterious to me: Why does it behave so much differently?

My question is twofold:

  1. Even with the built-in help and searching the net for clues, I can't explain why Mathematica insists in, apparently, keeping the symbolic result, rather than evaluating the 'intermediate' results and terminating nicely. Who ventures a viable explaination?

  2. How can I convince Mathematica to perform according to my expections (Other than using the alternative using If[])?

I'm really puzzled by this one, and I really hope someone out there can help me out.

/Twan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

时光磨忆 2024-12-24 02:31:09

尝试 u[x_] := x;跟踪[x*u[x] /。 x-> 2],它首先评估 xu[x]。那么,在您的情况下,它首先尝试评估 fact[x-1],然后将 x 替换为 2,并达到递归限制。

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 的第一个参数之后,它仍保持这种形式,然后发生替换等。

Trying u[x_] := x; Trace[x*u[x] /. x -> 2], it first evaluates x and u[x]. In your case, then, it first tries to evaluate fact[x-1] before replacing x by 2, and hits the recursion limit.

Attributes[ReplaceAll] shows that it does not have attribute HoldFirst, so it starts by evaluating its first argument. For instance,

ReleaseHold@ReplaceAll[Hold[x*fact[x - 1]], x -> 2]

gives the expected 2, as it holds the first argument, replaces, then releases the hold, as you intended.

Another way to do it is

Unprotect[ReplaceAll]
SetAttributes[ReplaceAll, HoldFirst]
ReplaceAll[x*fact[x - 1], x -> 2]

but don't do this.

Surely someone will give a better explanation soon, though.

EDIT: In response to the added question as to why

Clear[factif]
factif[n_] := If[n < 1, 1, n factif[n - 1]]

does not result in infinite recursion: defined this way, factif[x] evaluates to If[x < 1, 1, x factif[x - 1]], since x<1 cannot be evaluated. So it remains in this form after the attempted evaluation of the first argument of ReplaceAll, then the replacement occurs etc.

动次打次papapa 2024-12-24 02:31:09

这是因为您正在评估这一点:

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.

This is because you're evaluating this:

fact[x-1]

before the replacement happens. Just do fact[x-1] and you get the error.

You can fix your fact function like so:

Clear[fact]
fact[0] = 1
fact[n_Integer?Positive] := n fact[n - 1]

Then x fact[x - 1] /. x -> 2 returns 2 which seems correct.

Remember that your function argument pattern fact[n_] is extremely general. For example it allows for something like fact[Integrate[Sin[x], x]] to evaluate, which is probably not something you intended. Using fact[n_Integer] is much more precise, and will allow the fact function to act the way you want it to.

If you want to define this function even better, you can do something like:

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)

So that something like fact["x"] will fail with a message:

fact::nicetrybuddy: fact[x] is not defined.
有木有妳兜一样 2024-12-24 02:31:09

其他答案是正确的:fact 在其参数被替换之前评估。根本问题是您已经定义了事实并考虑了整数输入,并且没有为非整数输入提供终止条件。如果您这样做,

Clear[fact]
fact[0] = 1
fact[n_Integer?Positive] := n fact[n - 1]

fact 将保持未计算状态,直到它具有与正整数匹配的内容为止。

您可能需要将替换语句包装在 Evaluate 中,然后在替换其参数后触发 fact 的定义。

另一种方法可能是使用纯函数:

# fact[#-1]& @ 2

不应该过早地求值。

The other answers are correct: fact evaluates before its argument is replaced. The essential issue is that you have defined fact with integer inputs in mind, and haven't provided a terminal condition for non-integer inputs. If you instead did

Clear[fact]
fact[0] = 1
fact[n_Integer?Positive] := n fact[n - 1]

Then fact would be left unevaluated until it had something that matched a positive integer.

You might need to wrap your replacement statement in Evaluate to then fire the definition for fact after replacing its argument.

An alternative approach might be to use a pure function:

# fact[#-1]& @ 2

That shouldn't evaluate prematurely.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文