Ruby Freaks Lounge

第3回 Ruby1.9の新機能ひとめぐり(中編):洗練された文法と意味論

この記事を読むのに必要な時間:およそ 2.5 分

第1回では,YARVやFiberなど有名な機能と,配列処理の強化について紹介しました。

今回は,Ruby1.9で導入された新しい文法や意味論を中心に紹介します。

ブロックパラメータ(仮引数)に関する変更

ブロックパラメータのスコープがブロックローカルに

ブロック中の変数はスコープがやや曖昧でした。初出の変数の場合はブロックローカル(ブロックの終了と共に変数も消える)で,ブロックの外ですでに宣言されていた変数の場合はその変数を指す,というルールで,ブロックの外まで見ないと変数のスコープが判断できませんでした。

この問題を軽減するため,1.9ではブロックパラメータは常にブロックローカルであると改定されました。

コード1 ブロックパラメータがブロックローカルに

# ローカル変数xを定義する
x = "bear"

# ブロックローカル変数xを受け取るブロック(ローカル変数のxとは別!)
["dog", "cat", "panda"].each do |x|
  # このブロックの中でxの参照はブロックローカル変数の方を指す
  p x
  break if x == "cat"
end

# ブロックを出るとxはローカル変数の方を指すようになる
p x

図1 コード1の実行結果

# 1.8で実行した場合
$ ruby18 block-param-scope.rb
"dog"
"cat"
"cat"

# 1.9で実行した場合
$ ruby19 block-param-scope.rb
"dog"
"cat"
"bear"

これによって,ary.map{|x| ... } のような簡単なブロックを定義する際に,いちいちブロックパラメータ名の衝突を気にする必要がなくなりました。

ただしこの変更には互換性の問題があります。つまり,変数名を意図的に衝突させていたようなコードが動かなくなります。心配な人は,rubyに-wオプションを与えて実行することでwarningを出してくれますので,チェックするといいでしょう。

図2 -wつきでコード1を実行した結果

$ ruby19 -w block-param-scope.rb
block-param-scope.rb:5: warning: shadowing outer local variable - x
"dog"
"cat"
"bear"

また,ブロックパラメータの後にセミコロンと変数名の列を書くことで,ブロックパラメータ以外にブロックローカルな変数を宣言する機能もあります。

コード2 ブロックローカル変数の宣言

foo = "bear"

# ブロックローカル変数fooを宣言する
3.times do |x; foo|
  # ブロックローカル変数fooは毎回nilで初期化される
  p foo #=> nil
  foo = "dog"
  # ブロックの終了毎にブロックローカル変数fooは消える
end

# 外側のローカル変数は変更されない
p foo  #=> "bear"

ブロックとメソッドの仮引数のルールが統一

ブロックの仮引数にオプショナル引数やrest引数(*つきの引数)などが書けるようになりました。これによってブロックとメソッドの仮引数が同じルールになり,シンプルになりました。

コード3 ブロック仮引数中でオプショナル引数が使える

def foo
  yield 1, 2
  yield 1
end

foo do |x, y = :default|
  p [x, y]
end

図3 コード3の実行結果

[1, 2]
[1, :default]

この機能はAPI設計の幅を広げると思います。また,Module#define_methodによってオプショナル引数やブロックを受け取るメソッドが定義できるようになりました。

ただし,|@foo|のように,ブロックパラメータにインスタンス変数やクラス変数,アクセサメソッドなどを書くことはできなくなったことには注意が必要かもしれません。

著者プロフィール

遠藤侑介(えんどうゆうすけ)

某電機メーカーで研究職勤務。個人活動として,2008年1月よりRubyの開発に参加。テストを書いたり,バグをとったり,バグを入れたりしています。好きな言語はRubyとHaskellとOCaml 。使う言語はRubyとC 。

URLhttp://d.hatena.ne.jp/ku-ma-me/

コメント

コメントの記入