Переопределение функций в функции Джулии дает странное поведение - PullRequest
0 голосов
/ 06 октября 2018

Рассмотрим следующие функции, определения которых содержат переопределения функций.

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.Я думал, что это как-то связано с тем, что Джулия вообще не поддерживает переопределение функций, но я не уверен.Почему это происходит?

1 Ответ

0 голосов
/ 06 октября 2018

Я бы сказал, что это относится к более общеизвестной проблеме 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{}

и вы увидите, что Джулия оптимизирует всю внутреннюю логику и просто вызывает 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

и все будут работать какожидается.Ограничением этого подхода является то, что вы не получаете многократную диспетчеризацию для имени p (это связано с конкретной анонимной функцией, но я полагаю, вам не нужно многократная диспетчеризация в вашем примере).

...