🍡
Julia で列挙体を使うときのパターン
Julia で列挙体 (enum) を使うときは以下の様なパターンで書くと良いことが多い。
module Seasons
export Season, spring, summer, autumn, winter
export next, prev, ismild, ishot, iscold
never()::Union{} = throw(ErrorException("something wrong"))
@enum Season::UInt8 begin
spring
summer
autumn
winter
end
Base.repr(season::Season)::String =
season == spring ? "⟨spring⟩" :
season == summer ? "⟨summer⟩" :
season == autumn ? "⟨autumn⟩" :
season == winter ? "⟨winter⟩" :
never()
Base.show(io::IO, season::Season)::Nothing = print(io, repr(season))
Base.show(io::IO, ::MIME"text/plain", season::Season)::Nothing = show(io, season)
Base.string(season::Season)::String =
season == spring ? "spring" :
season == summer ? "summer" :
season == autumn ? "autumn" :
season == winter ? "winter" :
never()
Base.print(io::IO, season::Season)::Nothing = print(io, string(season))
Base.tryparse(::Type{Season}, str::AbstractString)::Union{Season, Nothing} =
str == "spring" ? spring :
str == "summer" ? summer :
str == "autumn" ? autumn :
str == "winter" ? winter :
nothing
function Base.parse(::Type{Season}, str::AbstractString)::Season
season = tryparse(Season, str)
isnothing(season) && throw(ArgumentError("cannot parse $(repr(str)) as Season"))
season
end
next(season::Season) = Season((UInt8(season) + 0x01) % 0x04)
prev(season::Season) = Season((UInt8(season) + 0x03) % 0x04)
ismild(season::Season) = UInt8(season) % 0x02 == 0x00
ishot(season::Season) = season == summer
iscold(season::Season) = season == winter
end # module Seasons
列挙体を使うときに要素数がそんなバカでかい数になることは少ないハズなので内部表現が UInt8
か Int8
になるようにする。
デバッグでの表現のために以下の函数を実装する。
Base.repr(x::T)::String
Base.show(io::IO, x::T)::Nothing
Base.show(io::IO, ::MIME"text/plain", x::T)::Nothing
Base.show(io::IO, ::MIME"text/plain", x::T)::Nothing
を定義すれば Base.repr(x::T)::String
も実装されるが、列挙体の場合は自分で定義した方が速くなる場合が多いと思われる。
文字列との変換のために以下の函数を実装する。
Base.string(x::T)::String
Base.print(io::IO, x::T)::Nothing
Base.tryparse(::Type{T}, str::AbstractString)::Union{T, Nothing}
Base.parse(::Type{T}, str::AbstractString)::T
Base.print(io::IO, x::T)::Nothing
を定義すれば Base.string(x::T)::String
も実装されるが、列挙体の場合は自分で定義した方が速くなる場合が多いと思われる。
tryparse
は成功すればパース結果、失敗すれば nothing
を返す。parse
は成功すればパース結果、失敗すればエラーを投げる。
ML 系の言語のような条件分岐はないため、if ...; elseif ...; else ...; end
や ... ? ... : ...
や ... && ... return; ... && ... return; ...
で処理し、最後に Union{}
型の函数を呼び出す。つまりはエラーを投げる。
その他の intrinsic な函数を実装する。
Discussion