Recursion 在Mathematica中,为什么递归函数中的替换不';不要终止?

Recursion 在Mathematica中,为什么递归函数中的替换不';不要终止?,recursion,replace,wolfram-mathematica,Recursion,Replace,Wolfram Mathematica,假设我在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 e

假设我在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]
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)
或者只是价值

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

Clear[fact]
fact[n_] := If[n < 1, 1, n fact[n - 1]]
Clear[事实]
事实[n_]:=如果[n<1,1,n事实[n-1]]
但这个事实(双关语;-)让我更加神秘:为什么它的行为如此不同

我的问题有两个:

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

  • 我如何说服Mathematica按照我的期望执行(而不是使用If[])的替代方案)

  • 我真的被这件事弄糊涂了,我真的希望有人能帮我


    /Twan尝试
    u[x]:=x;跟踪[x*u[x]/.x->2]
    ,它首先计算
    x
    u[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]]
    
    Clear[factif]
    如果[n<1,1,n如果[n-1]]
    

    不会导致无限递归:这样定义,
    factif[x]
    的计算结果为
    If[x<1,1,x factif[x-1]]
    ,因为
    x这是因为您在计算:

    fact[x-1]
    
    在更换之前。只要做
    fact[x-1]
    你就会得到错误

    您可以这样修复
    事实
    函数:

    Clear[fact]
    fact[0] = 1
    fact[n_Integer?Positive] := n fact[n - 1]
    
    然后
    x事实[x-1]/。x->2
    返回似乎正确的
    2

    请记住,函数参数模式
    fact[n.]
    非常通用。例如,它允许像
    fact[Integrate[Sin[x],x]]
    这样的东西进行计算,这可能不是您想要的。使用
    fact[n_Integer]
    更精确,并允许
    fact
    函数按您希望的方式运行

    如果您想更好地定义此函数,可以执行以下操作:

    2 fact[2-1]
    
    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.
    

    其他答案是正确的:
    fact
    在替换其参数之前进行计算。关键问题是,您已经定义了
    fact
    ,并考虑了整数输入,而没有为非整数输入提供终端条件。如果你真的这么做了

    Clear[fact]
    fact[0] = 1
    fact[n_Integer?Positive] := n fact[n - 1]
    
    然后,
    fact
    将保持未计算状态,直到它具有匹配正整数的内容

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

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

    # fact[#-1]& @ 2
    

    这不应该过早地进行评估。

    我现在正在运行一个大型模拟(400000个Hodrick-Prescott过滤器!),所以我没有用纯函数检查最后一个建议。我考虑过这一点。但我已经尝试过不同的递归函数,例如列表。我也遇到了同样的行为。所以我尽可能地把这个问题贴出来。但一个例子确实使事情再次具体化。抱歉。啊哈,这是有道理的:Mathematica首先计算/的LHS。然后执行替换。使用Hold[]可以推迟“急切”的评估。谢谢你的回答:有效、相关、清晰、简洁!我的compliments@nanitous干杯如果三个答案中有一个确实回答了您的问题,您可以将其标记为已接受的答案,这样它就会出现在顶部(这会提高回答者的声誉)。添加一个全面条款是个好主意!我得记住那个。