OpenStructで動的構造体を作る
RubyのOpenStruct(ostruct)を使うと、属性が自動生成される構造体を作ることができます。
require 'ostruct' alice = OpenStruct.new alice.name = 'Alice' alice.mail = 'alice@example.com' p alice #=> #<OpenStruct name="Alice", mail="alice@example.com">
Rubyの{ }と戯れて
1.
「ブロックを表す { } と、ハッシュリテラルの { } が混同されることはあるか?」というのが最初に抱いた疑問でした。それについては自分で「tASSOC(つまり=>)の有無でわかるじゃん」と思いました。つまり、以下の二つのメソッドmethod1, method2の呼び出しで何が渡されるかは自明であると思いました。
method1 { puts 'alice@example.com' } # ブロックが渡されるのだろう method2 { :alice => 'alice@example.com' } # ハッシュが渡されるのだろう
実際にmethod1, method2を書いて試そうとしたところ、以下のようなエラーになりました。
def method1(&block) block.call end def method2(arg) p arg end method1 { puts 'alice@example.com' } method2 { :alice => 'alice@example.com' } # syntax error, unexpected tASSOC, expecting '}'
2.
上でエラーになった理由は、きっとmethod2の引数をインタプリタが誤解するからだと思い、かっこ ( ) を付けてみました。すると以下のように期待通りの動きになりました。これでブロックとハッシュリテラルに関しては何となく納得。
def method1(&block) block.call end def method2(arg) p arg end method1 { puts 'alice@example.com' } #=> alice@example.com method2({ :alice => 'alice@example.com' }) #=> {:alice=>"alice@example.com"}
3.
「あれ?でも、メソッドの引数ってかっこがなくてもよかったはずだよな。だからこそDSL(Domain Specific Language)が楽に作れるわけだし」と思って、以下のようなプログラムで試してみました。あれれ?今度は括弧がなくてもちゃんと表示されます。
def print_args(*args) p args end print_args 1, 2, 3 #=> [1, 2, 3] print_args 1, 2, { :R => 'Rocket' } #=> [1, 2, {:R=>"Rocket"}]
4.
今度は引数を1個だけにしてみました。するとやはりエラーになります。
def print_args(*args) p args end print_args 1 print_args { :R => 'Rocket' } # syntax error, unexpected tASSOC, expecting '}'
5.
なるほど。メソッド名の直後が { だったら「これはブロック」と判断しているのか、と推測しました。以下実験。確かに { } のほうはブロックだと判断され、引数だとは思われていません。
def print_it(arg) p arg end print_it 1 #=> 1 print_it { puts 'Rocket' } #=> wrong number of arguments (0 for 1) (ArgumentError)
6.
ここまでで現象的には納得しました。また、RubyのFAQに4.6 p {}で何も表示されませんとあるのを見つけました(^_^;
7. 追記
bellbindさんから、{ }を取れば通りますねとコメントいただきました。ふむふむ。=> によってちゃんとハッシュにしてくれるんですね。
def print_args(*args) p args end print_args :R => 'Rocket' print_args :R => 'Rocket', :S => 'Space'
実行結果です。ちなみにこの例はR is for RocketとS is for Spaceより。
[{:R=>"Rocket"}] [{:R=>"Rocket", :S=>"Space"}]