Edited at
移行Day 11

AdventCalendar Day11 [超Ruby入門]


はじめに

本記事は、超Ruby入門11日目の記事です。

コメント頂ける方は、ガイドラインを読んで頂けると幸いです。


本題

%w[Ruby, Python, PHP].map {|x| x.length }

上記のコードがどのように動くかはもう知っているね。

では、下記コードを実行してみよう。

%w[Ruby, Python, PHP].map(&:length)

何と二つのコードは同じ結果をもたらす。

秘密を知るために、シンボルとProcを学んでいこう。


シンボル

シンボルとは、ある文字列に対して一つだけ割り当てられる整数だ。

下記コードを実行してシンボルが一意なオブジェクトIDを持つことを確かめてみよう。

symbol = "symbol"

copy_symbol = "symbol"

# 各変数のオブジェクトid
symbol.object_id
copy_symbol.object_id

# シンボル化
symbol.intern
copy_symbol.intern

symbol.intern.object_id
copy_symbol.intern.object_id

%w[Ruby, Python, PHP].map(&:length)

上記のコードでは:lengthがシンボルだ。

疑い深い人は確かめてみよう

Symbol.all_symbols

Symbol.all_symbols.grep(/\Alength\z/)


Proc

Procとは、ブロックを関連データと共に持ち運べるようにするクラスだ。Procは常にブロックと共にある。

Proc_squeeze = { | x | x.squeeze } # エラー

Proc_squeeze = Proc.new { | x | x.squeeze }
Proc_squeeze.class
Proc_squeeze.call("PPPHHHPPP")

Procオブジェクトを使うことで、ブロックを変数として定義できる。

Proc.callメソッドを呼び出すことで、Procオブジェクトを実行できるのだ。


本題

さて、ブロックの説明でイテレータには複数の文法があることを覚えているだろうか?

method(arg1, arg2, ..., `&' proc_object)

例えば、下記のような使い方ができる。

%w[Ruby, Python, PHP].map(& Proc.new { | x | x.length } )

もう少しで、map(&:length)の謎が解けそうだ。

"&"でオブジェクトを渡すと暗黙的にto_procメソッドを呼び出す。to_procメソッドはProcオブジェクトを返すメソッドだ。


to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。デフォルトで Proc、Method オブジェ クトは共に to_proc メソッドを持ちます。to_proc はメソッド呼び出し時に実 行され、Proc オブジェクトを返すことが期待されます。


シンボルには、to_procメソッドを持つ。

もうわかっただろう。

① %w[Ruby, Python, PHP].map(&:length)

② %w[Ruby, Python, PHP].map(& Proc.new { | x | x.length } )

①では、&:lengthからto_procメソッドが呼び出され、Procオブジェクトを返すことで②のような形で実行されるのだ。


利用例

さて、Procオブジェクトを紹介したが実際にはいつ利用するのだろうか?

require "rspec"

class Person
end

describe Person do
it "should be a instance" do
expect{ Person.new }.to be_a(Person)
end

it "should be a instance" do
expect( Person.new ).to be_a(Person)
end
end

MUGENUP技術ブログさん

Procは、Railsのテストなどで利用される。

上記のテストでは、{ }がProcクラスであるため最初のテストをパスすることができない。

*Procの使い所をコメントしてくださるとありがたいです


参照

徳丸浩の日記