Enumerable#empty?は、汚染なしに可能か
Enumerableに対して、変数領域を汚さずに empty? みたいなメソッドが出来ないか考えてみた。
つまり、Enumerableなインスタンスeに対して、
i = 0 e.each{i+=1}
して、このiが1以上であればfalse。0であればtrueを戻すメソッドと同じ動きをする処理。
つまるはinstance_evalを使えばいいのだが、
Owner@fam /usr/local $ ruby-1.8.7-p22/bin/ruby -ve 'p [1].instance_eval{x=true;each{x=false;break};x};p local_variables' ruby 1.8.7 (2008-06-20 patchlevel 22) [i386-cygwin] false []
所謂一つのクロージャを使うので、
Owner@fam /usr/local $ ruby-1.8.7-p22/bin/ruby -ve 'x=1;[1].instance_eval{x=true;each{x=false;break};x};p x' ruby 1.8.7 (2008-06-20 patchlevel 22) [i386-cygwin] false
同じ名前の変数があると汚染してしまう。
1.9系なら、クロージャの引数を使って
Owner@fam /usr/local $ ruby-1.9.0-2/bin/ruby -ve 'x=1;[1].instance_eval{|x|x=true;each{x=false;break};x};p x' ruby 1.9.0 (2008-06-20 revision 17482) [i386-cygwin] -e:1: warning: shadowing outer local variable - x 1
とすれば、warningは出るものの、元の値を保護することは可能だ。
でもソレってなんだかなぁ…。
# いや、思いっきり使い方間違ってますよね。
ではローカルな変数領域を汚さずにジャンプするような構文といえば例外があるわけで、こっちを使ってみるとどうなるだろう。
Owner@fam /usr/local $ ruby-1.9.0-2/bin/ruby -ve 'p [1].instance_eval{begin;each{raise};true;rescue;false;end}' ruby 1.9.0 (2008-06-20 revision 17482) [i386-cygwin] false
意図通りに動いているけど、これはひどい。キタナイ。
でも、一応$!は汚してない…よね。
Owner@fam /usr/local $ ruby-1.9.0-2/bin/ruby -ve ' begin raise rescue p $! # => ? end p $! # => ? [1].instance_eval{ begin each{ raise } true rescue false end } p $! # => ?' ruby 1.9.0 (2008-06-20 revision 17482) [i386-cygwin] RuntimeError nil nil Owner@fam /usr/local $ ruby-1.8.7-p22/bin/ruby -ve ' begin raise rescue p $! # => ? end p $! # => ? [1].instance_eval{ begin each{ raise } true rescue false end } p $! # => ?' ruby 1.8.7 (2008-06-20 patchlevel 22) [i386-cygwin] RuntimeError nil nil
あれ?
$!ってrescueの中でしか有効じゃないのかな。スコープはどうだったっけ。
と、それは別件と言うことで、ココでは忘れよう。
で、美しく行くには、やはりEnumerable#empty? というのを実装してしまえばいいといえばいいのだけど。
Owner@fam /usr/local $ ruby-1.9.0-2/bin/ruby -ve ' module Enumerable if !method_defined?(:empty?) def empty?() empty = true each{ empty=false break } empty end end end p [1].empty? # => false p [].empty? # => true ' ruby 1.9.0 (2008-06-20 revision 17482) [i386-cygwin] false true
しかし、ユーザがこちらの意図するのと違うempty?を実装していたらどうしよう。
いや、そりゃどうしようもないかな。
こうなったら、Cで実装して提案してしまうというのも一つの手だろうか。
言語系の中に取り込まれれば、ユーザに悪影響を及ぼさないわけだし。
# って、目的変わっちゃってますね。反省。
いやまぁ、結局は過去バージョンの問題が出るから、同じ事ですね。
ん〜む。