代码之家  ›  专栏  ›  技术社区  ›  largecats

在Julia函数中重新定义函数会产生奇怪的行为

  •  1
  • largecats  · 技术社区  · 7 年前

    function foo1()
        x = 1
        if x != 1
            error("Wrong")
        end
        x = 2
    end
    
    function foo()
        function p(t) return t + 1 end
        if p(1) != 2
            error("Wrong")
        end
        function p(t) return 1 end
    end
    

    foo1() 运行时没有错误,但是 foo() 给出错误 Wrong . 我认为这可能与Julia一般不支持重新定义函数有关,但我不确定。为什么会这样?

    1 回复  |  直到 7 年前
        1
  •  3
  •   Bogumił Kamiński    7 年前

    我想说,这属于一个更普遍的已知问题 https://github.com/JuliaLang/julia/issues/15602 .

    在您的情况下,考虑一个更简单的函数:

    function f()
        p() = "before"
        println(p())
        p() = "after"
        nothing
    end
    

    打电话 f() 将打印 "after" .

    foo 按以下方式:

    julia> @code_typed foo()
    CodeInfo(
    4 1 ─     invoke Main.error("Wrong"::String)::Union{}                                                                                │
      │       $(Expr(:unreachable))::Union{}                                                                                             │
      └──     $(Expr(:unreachable))::Union{}                                                                                             │
    ) => Union{}
    

    你可以看到Julia优化了所有的内部逻辑,只调用 error .

    如果您提前一步检查,您可以看到:

    julia> @code_lowered foo()
    CodeInfo(
    2 1 ─      p = %new(Main.:(#p#7))                                                                                                    │
    3 │   %2 = (p)(1)                                                                                                                    │
      │   %3 = %2 != 2                                                                                                                   │
      └──      goto #3 if not %3                                                                                                         │
    4 2 ─      (Main.error)("Wrong")                                                                                                     │
    6 3 ─      return p                                                                                                                  │
    )
    

    你看到了吗 p 在顶行中只指定一次。实际上使用了第二个定义(此处不可见,但可以在上面看到)。

    要解决您的问题,请使用以下匿名函数:

    function foo2()
        p = t -> t + 1
        if p(1) != 2
            error("Wrong")
        end
        p = t -> 1
    end
    

    一切都会如期进行。这种方法的局限性在于,您不会在name上获得多个dispatch (它绑定到一个具体的匿名函数,但我猜在您的示例中不需要多次分派)。

    推荐文章