こんにちは、鈴木です。
先日(2014/9/18)に Ruby 2.2.0 preview1 が出ましたね!
いくつか新しい機能も増えていたので、気になったものをご紹介します!(詳細な変更内容はこのページで確認することができます。)
ついに来ました!恒等関数: Kernel#itself
いわゆる恒等関数と呼ばれる、自身 (self) をそのまま返すメソッド Kernel#itself が追加されました。
他の言語でも恒等関数が組み込みで存在する場合があり、id や identity などの名前で定義されていることが多いです。「名前重要!」の Ruby では恒等関数の導入案は以前からありましたが、名前がなかなか決まらなかったという経緯があります。今回は itself という名前に落ち着いたようで、ようやく導入されました。
Kernel#itself は以下のような動きをします。
1 2 3 4 5 |
1.itself # => 1 'Hello'.itself # => 'Hello' object = Object.new object.object_id == object.itself.object_id # => true |
要するに self を返してくれるということですね。
具体的な利用例ですが、以下のように配列の値自身で group_by したい場合に綺麗に書くことが出来ます。
1 2 3 4 5 6 7 |
values = [5, 7, 3, 7, 7, 5, 1, 1, 1, 0] # Ruby 2.2.0 未満 values.group_by{|x| x} # => {5=>[5, 5], 7=>[7, 7, 7], 3=>[3], 1=>[1, 1, 1], 0=>[0]} # Ruby 2.2.0 以降 values.group_by(&:itself) # => {5=>[5, 5], 7=>[7, 7, 7], 3=>[3], 1=>[1, 1, 1], 0=>[0]} |
他にも send や sort_by の引数が実行時にしか決まらない場合のデフォルト値として itself を使用することも考えられます。
1 2 3 4 5 6 7 8 |
values = %w(apple orange BANANA) # 実行時にソートの条件が決まる. sort_key = :itself sort_key = :length if sort_by_length? sort_key = :upcase if sort_by_upcase? p values.sort_by(&sort_key) |
Float#next_float, prev_float
数値計算系のプログラムでお世話になりそうなメソッドです。
次の or 前の float って?と思われるかもしれませんが、浮動小数点数には無限の精度があるわけではなく、表現できる値は飛び飛びになっています。
Float#next_float と Float#prev_float は、表現可能な次の or 前の値を返すメソッドです。以下のような結果になります。
1 2 |
123.456.next_float # => 123.45600000000002 123.456.prev_float # => 123.45599999999999 |
これを利用すれば機械イプシロン(1.0 より大きい最小の値と 1.0 との差)を求めることもできます。
1 2 |
# 機械イプシロン (1.0 より大きい最小の値と 1.0 との差) MACHINE_EPSILON = 1.0.next_float - 1.0 |
Binding#local_variables, receiver
メタプロの小道具になりそうなメソッドが追加されました。
Binding#local_variables はそのコンテキストで定義されているローカル変数の名前を返します。
以下のようにすると、呼び出し元で定義されているローカル変数にアクセスすることが出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def call_f taro = 123 jiro = 456 hanako = 789 f(binding) end def f(binding) binding.local_variables.each do |name| value = binding.local_variable_get(name) puts "#{name} = #{value}" end end call_f |
これを実行すると次のような出力が得られます。
1 2 3 |
taro = 123 jiro = 456 hanako = 789 |
Binding#receiver は Binding#eval('self') と同じ結果を返すメソッドです(良く利用されるので新しいメソッドとして追加されました)。
以下のように呼び出し元のオブジェクトにアクセスしたい場合に利用します。「Ruby: Procによる変数の隠蔽とbindng」の「Proc#bindingとmethod_missingの合わせ技」では DSL で利用する例を解説しているので、そちらも参考にしてみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Taro attr_accessor :name, :age def initialize self.name = 'taro' self.age = 10 end def call_f f(binding) end end def f(binding) p binding.receiver.name p binding.receiver.age end Taro.new.call_f |
これを実行すると、次のような結果が得られます。
1 2 |
"taro" 10 |
Enumerable#slice_after
Enumerable#each_slice(n) は n 個ずつにスライスしてくれるメソッドですが、特定の条件を満たす要素の直後でスライスするメソッド slice_after が新しく追加されました。ちなみに slice_before は前からありました。
slice_after は以下のように使います。
1 2 3 4 5 |
values = %w(しりとり リフティング ぐんまけん りんご ゴリラ ラッパ パン だいこん てんぷら らっきょう うどん) values.slice_after{|string| string =~ /(ん|ン)$/}.each do |values| p values end |
以下、実行結果です。
1 2 3 4 |
["しりとり", "リフティング", "ぐんまけん"] ["りんご", "ゴリラ", "ラッパ", "パン"] ["だいこん"] ["てんぷら", "らっきょう", "うどん"] |
Enumerable モジュールには chunk や each_cons など親戚のようなメソッドをはじめとして、便利なメソッドが数多く定義されているので、リファレンスを一読しておくと良いでしょう。Ruby のバージョンアップごとにメソッドが追加されることもあるので、たまに見直してみると「こんなメソッドがあったのか」ということも多々あります。
Method#curry
メソッドをカリー化する Method#curry が追加されました。
以下のような動作をします。
1 2 3 4 5 6 7 8 9 10 11 12 |
def f(a, b, c) p [a, b, c] end # Method オブジェクトを取得する. m = method(:f) # 普通に呼び出す. m.call(1, 2, 3) # カリー化してから呼び出す. m.curry.call(1).call(2).call(3) |
カリー化すると、複数の引数を取るメソッドを、一つの引数を取るメソッドの連鎖に分解することができます。例えば、二つの引数を取るメソッドをカリー化すると、引数が一つで戻り値が「ひとつの引数を取るメソッド」であるメソッドになります。これによって、以下のように引数を部分的に適用し、残りは他の処理に任せる、といったことが簡単に行えます。
1 2 3 4 5 6 7 8 |
def add(value, adder) adder.call(value) end adder = lambda{|a, b| a + b}.curry.call(10) (1..10).each do |x| puts add(x, adder) end |
Method#super_method
Method#super_method を使用すると、メソッドをオーバーライドしているときの親クラスのメソッドを取得することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A def f end end class B < A def f end end class C < B def f end end p C.new.method(:f).source_location p C.new.method(:f).super_method.source_location p C.new.method(:f).super_method.super_method.source_location |
デバッグで役に立つかもしれません。
まとめ
新機能をいくつか見てきましたが、便利そうなものが多いですね。他にもインクリメンタル GC やシンボル GC などの性能面の改善なども行われているので、正式リリースが楽しみです。