Юлия-Ланг сравнение выражения и коммутативности

ОК, мой заголовок не велик, но это легко объяснить на примере.

julia>a = :(1 + 2)
julia>b = :(2 + 1)

julia>a == b
false

У меня есть два выражения а и б. Я хотел бы знать, если они дадут мне такие же результаты без оценки.

Я думал, что коммутативные операторы, такие как + или *, могут сделать вывод, что результаты будут одинаковыми.

РЕДАКТИРОВАТЬ: Другой способ понять это - сравнить очень специфическое подмножество выражений, которые могут определить коммутативность функции: Expr(:call, +, a, b) <=> Expr(:call, +, b, a)

2 ответа

Решение

Мы можем написать довольно простую функцию, чтобы проверить, имеют ли два массива одинаковые элементы, по порядку упорядочения:

function eq_modulo_ordering!(xs, ys)  # note !, mutates xs and ys
    while !isempty(xs)
        i = findfirst(isequal(pop!(xs)), ys)
        i === nothing && return false
        deleteat!(ys, i)
    end
    isempty(ys)
end
eq_modulo_ordering(xs, ys) = eq_modulo_ordering!(copy(xs), copy(ys))

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

function expr_equiv(a::Expr, b::Expr, comm)
    a.head === b.head || return false
    a.head === :call || return a == b
    a.args[1] ∈ comm || return a == b

    eq_modulo_ordering(a.args, b.args)

end
expr_equiv(a, b, comm) = a == b
expr_equiv(a, b) = expr_equiv(a, b, [:+])

В случае, если мы хотим проверить, что два выражения полностью эквивалентны за пределами верхнего уровня, мы могли бы изменить наши функции, чтобы использовать взаимную рекурсию, чтобы проверить, являются ли подвыражения expr_equiv, скорее, чем isequal,

function eq_modulo_ordering!(xs, ys, comm)  # note !, mutates xs and ys
    while !isempty(xs)
        x = pop!(xs)
        i = findfirst(b -> expr_equiv(x, b, comm), ys)
        i === nothing && return false
        deleteat!(ys, i)
    end
    isempty(ys)
end
eq_modulo_ordering(xs, ys, comm) = eq_modulo_ordering!(copy(xs), copy(ys), comm)

function expr_equiv(a::Expr, b::Expr, comm)
    a.head === b.head || return false
    a.head === :call || return a == b
    a.args[1] ∈ comm || return all(expr_equiv.(a.args, b.args, Ref(comm)))

    eq_modulo_ordering(a.args, b.args, comm)
end
expr_equiv(a, b, comm) = a == b
expr_equiv(a, b) = expr_equiv(a, b, [:+])

Теперь мы можем использовать expr_equiv как и ожидалось, опционально предоставляя список функций, которые являются коммутативными.

julia> expr_equiv(:((a + b + b) * c), :((b + a + b) * c))
true

julia> expr_equiv(:((a + a + b) * c), :((b + a + b) * c))
false

julia> expr_equiv(:(c * (a + b + b)), :((b + a + b) * c), [:+, :*])
true

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

Невозможно вычислить, будут ли фрагменты кода иметь одинаковый результат.

Другие вопросы по тегам