Julia - ドット . を使って method を呼ぶ

約1年前に全く Julia っぽくない邪道な遊びをしていたなということをふと思い出した。
微妙なメモだけは残っていたので供養しておく。

これから紹介する方法は遊びであって本気にされると困ります。

環境

  • Julia 1.3.0

getproperty

v.method という呼び方は getproperty が呼ばれているので、この関数をオーバーロードすると v.length() のような他の言語では一般的だけれども、全然 Julia っぽくない書き方を一応実現することはできる。

https://docs.julialang.org/en/v1/base/base/#Base.getproperty

julia> function Base.getproperty(v::Vector, s::Symbol)
           s == :length && return () -> length(v)
           s == :reshape && return function (a,b)
               return reshape(v, a,b)
           end
       end

julia> v = rand(10)
10-element Array{Float64,1}:
 0.8267378196342272 
 0.6176403952129186 
 0.5046041927146574 
 0.8758407174025733 
 0.6085707115128745 
 0.9317736835188135 
 0.40242234842524804
 0.9733691132606714 
 0.5724142410241722 
 0.568618356534722  

julia> v.length()
10

julia> v.reshape(2,5)
2×5 Array{Float64,2}:
 0.826738  0.504604  0.608571  0.402422  0.572414
 0.61764   0.875841  0.931774  0.973369  0.568618

PyCall.jlでPyTorch使ってDeep Learningする - Qiita の記事の方法を使うとより汎用的になる。(一部走るように修正した) ただし、全体的にものすごく動作がもっさりする。

julia> function Base.getproperty(obj, s::Symbol)
           if isdefined(obj, s)
               getfield(obj, s)
           else
               eval(quote (args...) ->  $(s)($obj, args...) end)
           end
       end
WARNING: Method definition getproperty(Any, Symbol) in module Base at Base.jl:20 overwritten in module Main at REPL[1]:2.

julia> v = rand(6)
6-element Array{Float64,1}:
 0.06120994308448946 
 0.5256925985282832  
 0.9023581914503764  
 0.8468689107200875  
 0.019238205610359937
 0.6605465949011493  

julia> function myfunc(v::Vector, a)
           return v .+ a
       end
myfunc (generic function with 1 method)

julia> v.myfunc(10)
6-element Array{Float64,1}:
 10.061209943084489
 10.525692598528284
 10.902358191450377
 10.846868910720087
 10.01923820561036 
 10.660546594901149

julia> v.size()
(6,)

struct

class のある他言語のように、struct 定義時に method を生やそうと思えば一応生やせる。

julia> mutable struct Hoge
           v::Array
           length::Function
           size::Function
           reshape::Function
           
           function Hoge(v)
               x = new()
               x.v = v
               x.length = () -> length(x.v)
               x.size = () -> size(x.v)
               x.reshape = (a, b) -> reshape(x.v, a, b)
               return x
           end
       end

julia> x = Hoge([1, 2, 3])
Hoge([1, 2, 3], var"#3#5"{Hoge}(Hoge(#= circular reference @-2 =#)), var"#4#6"{Hoge}(Hoge(#= circular reference @-2 =#)))

julia> x.length()
3

julia> x.size()
(3,)

julia> x.v = rand(10, 5)
10×5 Array{Float64,2}:
 0.0844862  0.842631  0.339725    0.954259   0.194565  
 0.470892   0.521036  0.00714723  0.846754   0.524923  
 0.870488   0.545857  0.545821    0.827207   0.974655  
 0.29028    0.876719  0.637267    0.997776   0.462824  
 0.925254   0.858045  0.53476     0.794868   0.330684  
 0.711127   0.318063  0.0518006   0.0385135  0.00460019
 0.930399   0.979754  0.0512777   0.0573049  0.412405  
 0.627587   0.281032  0.115321    0.194771   0.87389   
 0.646853   0.140648  0.906595    0.0133067  0.158983  
 0.539785   0.133379  0.0629104   0.143179   0.659137

julia> x.length()
50

julia> x.size()
(10, 5)

julia> x.reshape(5, 10)
5×10 Array{Float64,2}:
 0.0844862  0.711127  0.842631  0.318063  0.339725    0.0518006  0.954259  0.0385135  0.194565  0.00460019
 0.470892   0.930399  0.521036  0.979754  0.00714723  0.0512777  0.846754  0.0573049  0.524923  0.412405  
 0.870488   0.627587  0.545857  0.281032  0.545821    0.115321   0.827207  0.194771   0.974655  0.87389   
 0.29028    0.646853  0.876719  0.140648  0.637267    0.906595   0.997776  0.0133067  0.462824  0.158983  
 0.925254   0.539785  0.858045  0.133379  0.53476     0.0629104  0.794868  0.143179   0.330684  0.659137