coalesce
というわけで、下の :? 演算子、Rubyで出来ないか試してみた。
まずはcoalesce。
これは引数を順に評価し、最初にnullでなかったものを返す。
つまり、こうなる。
Owner@fam ~ $ ruby -e ' > class Object > def coalesce(*v) > self > end > end > class NilClass > def coalesce(*v) > v.select{|x|break x if x} > end > end > p nil.coalesce(nil, 2) > ' 2
いあ、単純に関数として定義しても問題は無い気がするけど…
しかし、これだと :? 演算子とは違う動きっぽい。
フツウの演算子として使うものだろうから、どちらかというとNULLIF関数(Postgres)が近そう。
実装してみる。
Owner@fam ~ $ ruby -e ' > class Object > def nullif(v) > self > end > end > class NilClass > def nullif(v) > v > end > end > p nil.nullif(2) > ' 2
そう。こういう動きだ。
これを :? 演算子として定義してみる。
Owner@fam ~ $ ruby -e ' > class NilClass > def :?(v) > v > end > end > ' -e:3: syntax error, unexpected tSYMBEG def :?(v) ^ -e:6: syntax error, unexpected kEND, expecting $end
…そういえばコロンははシンボルの開始ですね。
ここでは:?のシンボルを定義する必要がある。
単なる文字列として :? を定義するのは、比較的簡単なので、
Owner@fam ~ $ ruby -e 'p ":?".intern' :":?"
これをうまく使えないだろうか。
通常のdefだとsymbolは使えない。
となると、キホンdefine_methodでメタ的に操作するのがいいのだろう。
Owner@fam ~ $ ruby -e ' > class Object > define_method(":?".intern){|v| > self > } > end > class NilClass > define_method(":?".intern){|v| > v > } > end > '
うまくいった。
さて、うまくdefineされたところで、こんどは使う方法。
そのまま演算子で使おうとすると
Owner@fam ~ $ ruby -e ' > class NilClass > define_method(":?".intern){|v| > v > } > end > p (nil :? 1) > ' -e:7: syntax error, unexpected ':', expecting ')' p (nil :? 1) ^
と言われ、当然、文法違反。
.でメソッドだと強制的にパーサに教えようとしても、
Owner@fam ~ $ ruby -e ' > class NilClass > define_method(":?".intern){|v| > v > } > end > p (nil.:? 1) > ' -e:7: syntax error, unexpected tSYMBEG p (nil.:? 1) ^ -e:7: warning: invalid character syntax; use ?\s
理解してくれない。
結局パーサによる自然な解釈はあきらめ、他の方法を使うしかないのだろう。
というか、リフレクションで強制的に呼び出すしかない。
rubyでmethodを直接指定するには、methodメソッドがある。
methodメソッドを利用すると、Methodオブジェクトが生成されるので、ここに引数を渡せば呼び出しと同じとなる。
Owner@fam ~ $ ruby -e ' > class NilClass > define_method(":?".intern){|v| > v > } > end > p nil.method(":?".intern)[1] > ' 1
呼び出せた。
…って、やろうとした事と大分かけ離れてしまった…。
うう。