Я написал простой макрос переключения специально для Enum
с.Код в значительной степени вдохновлен Match.jl , и ему не хватает универсальности и обработки ошибок Match.@match
.Мой макрос @enum_switch
реализован следующим образом:
import MacroTools.rmlines
# Assume the correct number of switches are provided for the Enum.
macro enum_switch(v, block_ex)
block_ex = rmlines(block_ex) # Remove `LineNumberNode`s from block quote
pairs = block_ex.args
ex = nothing
for p in reverse(pairs)
if isnothing(ex)
ex = p.args[3]
else
ex = Expr(:if, Expr(:call, :(==), esc(v), p.args[2]), p.args[3], ex)
end
end
ex
end
Он может использоваться для определения talk_switch
следующим образом:
@enum Fruit apple=1 orange=2 kiwi=3
function talk_switch(fruit::Fruit)
@enum_switch fruit begin
apple => "I like apples."
orange => "I like oranges."
kiwi => "I like kiwis."
end
end
И мы можем видеть, что он работает как задумано:
julia> talk_switch(apple)
"I like apples."
julia> talk_switch(orange)
"I like oranges."
julia> talk_switch(kiwi)
"I like kiwis."
Теперь давайте сравним talk_switch
с другими предлагаемыми подходами.
function talk_ifelse(fruit::Fruit)
if fruit == apple
"I like apples."
elseif fruit == orange
"I like oranges."
else
"I like kiwis."
end
end
function talk_array(fruit::Fruit)
say = ["I like apples.", "I like oranges.", "I like kiwis."]
say[Int(fruit)]
end
function talk_dict(fruit::Fruit)
phrases = Dict{Fruit, String}(
apple => "I like apples.",
orange => "I like oranges.",
kiwi => "I like kiwis."
)
phrases[fruit]
end
abstract type AbstractFruit end
struct Apple <: AbstractFruit end
struct Orange <: AbstractFruit end
struct Kiwi <: AbstractFruit end
const APPLE = Apple()
const ORANGE = Orange()
const KIWI = Kiwi()
talk_type(fruit::Apple) = "I like apples."
talk_type(fruit::Orange) = "I like oranges."
talk_type(fruit::AbstractFruit) = "I like kiwis."
Как и предполагалось, talk_switch
и talk_ifelse
выдают одинаковый пониженный код:
julia> @code_lowered talk_switch(kiwi)
CodeInfo(
1 ─ %1 = fruit == Main.apple
└── goto #3 if not %1
2 ─ return "I like apples."
3 ─ %4 = fruit == Main.orange
└── goto #5 if not %4
4 ─ return "I like oranges."
5 ─ return "I like kiwis."
)
julia> @code_lowered talk_ifelse(kiwi)
CodeInfo(
1 ─ %1 = fruit == Main.apple
└── goto #3 if not %1
2 ─ return "I like apples."
3 ─ %4 = fruit == Main.orange
└── goto #5 if not %4
4 ─ return "I like oranges."
5 ─ return "I like kiwis."
)
Наконец, мы можем сравнить производительность различных решений:
julia> using BenchmarkTools
julia> @btime talk_switch(kiwi);
6.348 ns (0 allocations: 0 bytes)
julia> @btime talk_ifelse(kiwi);
6.349 ns (0 allocations: 0 bytes)
julia> @btime talk_type(KIWI);
6.353 ns (0 allocations: 0 bytes)
julia> @btime talk_array(kiwi);
103.447 ns (1 allocation: 112 bytes)
julia> @btime talk_dict(kiwi);
861.712 ns (11 allocations: 704 bytes)
Как и ожидалось, talk_switch
и talk_ifelse
имеют одинаковую производительность, так как они производят одинаковый пониженный код.Интересно, что talk_type
также выполняет те же функции, что и talk_switch
и talk_ifelse
.Наконец, мы видим, что talk_array
и talk_dict
сильно отстают от трех лучших исполнителей.