環境
Julia 1.0
無駄な method を消す
Julia といえば多重ディスパッチが有名ですよね*1。
julia> f(x::Int64) = println("Intだよ") f (generic function with 1 method) julia> f(x::Float64) = println("Floatだよ") f (generic function with 2 methods) julia> f(Int64(1)) Intだよ julia> f(Float64(1)) Floatだよ
こんな感じに同じ関数でも引数の型を指定して定義することで、型に応じて動作を変えることができるので便利です。
しかし、ここで一つ問題があります。
上記の例では引数が Int64
だったら Intだよ
と出力するようにしましたが、後々になって Integer
の subtype 全体に対して 整数だよ
と出力したくなったとしましょう。
素直に考えたら次のように定義すると思います。
julia> f(x::Integer) = println("整数だよ") f (generic function with 3 methods)
しかし、この定義の仕方だと Int64
の数値を入れると一番初めに定義した method が呼び出されてしまいます。
julia> f(Int32(1)) 整数だよ julia> f(Int64(1)) Intだよ julia> methods(f) # 3 methods for generic function "f": [1] f(x::Float64) in Main at REPL[2]:1 [2] f(x::Int64) in Main at REPL[1]:1 [3] f(x::Integer) in Main at REPL[5]:1
単純な対処法は f(x::Int64)
を上書きしてしまうことでしょう。
f(x::Int64) = println("整数だよ")
しかし、また気が変わって Integer
の subtype 全体に対して Intだよ
と出力したくなった場合、f(x::Int64)
と f(x::Integer)
の2つを上書きしなければなりません。これはどう考えても面倒です。
f(x::Int64) = println("Intだよ") f(x::Integer) = println("Intだよ")
今は Integer
の subtype ならば全て同じ動作にしたいので f(x::Int64)
が邪魔です。いっそなかったことにしたいです。
そんな時に便利なのが Base.delete_method
です。2018/9/24 現在公式ドキュメントに記述は一切ありません。
which
と組み合わせて以下のように使います。
julia> f(Int64(1)) Intだよ julia> Base.delete_method(@which f(Int64(1))) julia> f(Int64(1)) 整数だよ julia> methods(f) # 2 methods for generic function "f": [1] f(x::Float64) in Main at REPL[2]:1 [2] f(x::Integer) in Main at REPL[5]:1
見事に f(x::Int64)
の method が消えました。@which
マクロを使う場合は消したい method の具体的な値を入れてください。which
を使う場合、
Base.delete_method( which(f, (Int64, ) ) )
と書きます。具体的な値を入れてしまう方が楽だとは思いますが、「この型の method を消してるんだぞ!」と強調したいときは which
使うと良いのではないでしょうか。
とまぁ、無事に不必要な method を消すことができた訳ですが、正直に言ってこの程度のことだったら一度プロセスを切ってもう一度 Julia を起動し直したほうが断然楽ですね。
ものすごく読み込みが遅いパッケージを使っている最中ならば Base.delete_method
を使ったほうが楽かもしれませんが、大抵の場合は再起動したほうが早いでしょう。
上記の例ではあまりメリットを感じないので、もう少し実用的なものを考えてみましょう。私は pretty-print 好きなので pretty-print に応用しましょう*2。
pretty-print
julia> 1.0 + im 1.0 + 1.0im julia> function Base.show(io::IO, z::Complex{Float64}) r = round(abs(z), digits=3) θ = round(atan(z.im, z.re), digits=3) print(io, r, " * ( cos( ", θ, " ), sin( ", θ, " ) )") end julia> 1.0 + im 1.414 * ( cos( 0.785 ), sin( 0.785 ) )
定義した method を消します。
julia> Base.delete_method(which(Base.show, (IO, Complex{Float64}))) julia> 1.0 + im 1.0 + 1.0im
どうですか!今までは pretty-print を自分で定義した場合、元の書式に戻すには再起動するしかなかったのに Base.delete_method
を使うことで再起動を回避できたではありませんか!!!
とはいえ、これでは切り替えが面倒なので簡単に切り替えできるように関数を定義しましょう。
julia> 1.0 + im 1.0 + 1.0im julia> POLAR = false false julia> function polarform(x::Bool) if !POLAR && x eval(quote function Base.show(io::IO, z::Complex{Float64}) r = round(abs(z), digits=3) θ = round(atan(z.im, z.re), digits=3) print(io, r, " * ( cos( ", θ, " ), sin( ", θ, " ) )") end end) global POLAR = true return end if POLAR && !x Base.delete_method(which(Base.show, (IO, Complex{Float64}))) global POLAR = false return end end polarform (generic function with 1 method) julia> polarform(true) julia> 1.0 + im 1.414 * ( cos( 0.785 ), sin( 0.785 ) ) julia> polarform(false) julia> 1.0 + im 1.0 + 1.0im
polarform(true)
にすると極形式で、polarform(false)
にすると Cartesian Form*3 で表示することに成功しました!
ついに私は pretty-print の切替方法を確立してしまった。 今後のパッケージ作りに役に立ちそうだ。
落とし穴
先程の pretty-print の例では Base.delete_method
を使えばいつでもデフォルトの pretty-print に戻せるように感じたかもしれませんが、そんなことは全くもってありません。
複素数の pretty-print に関しては Complex{Float64}
の supertype である Complex
にのみ定義されているから先程の例は上手くいっただけで、普通はこんなに上手くいかないので注意してください。
export
されてもいなければ、公式ドキュメントにすら書かれていない、エンドユーザーが使うことを想定してない関数がそんなにおいしいわけないですね。
julia> 1.0+im 1.0 + 1.0im julia> Complex{Float64} <: Complex true julia> @which show(stdout, 1.0+im) show(io::IO, z::Complex) in Base at complex.jl:183 julia> Base.delete_method(@which Base.show(stdout, 1.0+im)) julia> 1.0+im Complex{Float64}(1.0, 1.0) julia> @which show(stdout, 1.0+im) show(io::IO, x) in Base at show.jl:315 julia> Base.delete_method(@which Base.show(stdout, 1.0+im)) julia> @which show(stdout, 1.0+im) ERROR: no unique matching method found for the specified argument types Stacktrace: [1] which(::Any, ::Any) at ./reflection.jl:922 [2] top-level scope at none:0 julia> 1.0+im Error showing value of type Complex{Float64}: ERROR: MethodError: no method matching display(::Complex{Float64}) Closest candidates are: display(::Any) at multimedia.jl:284 display(::AbstractDisplay, ::AbstractString, ::Any) at multimedia.jl:178 display(::AbstractString, ::Any) at multimedia.jl:179 ... Stacktrace: [1] display(::Complex{Float64}) at ./multimedia.jl:294
参考
LICENSE
- Copyright 2018 goropikari
- Licensed under MIT: