作者: rubikitch
日時: 2008/1/21(17:28)
From: kikwai@... (機械伯爵)
Subject: [TSfree:2305] ブロックの実例
Date: Sun, 20 Jan 2008 12:50:28 +0900 (JST)

るびきちです。

>  で、ご存知の方はご存知の通り(……日本語になってねーよ)Pythonでは、
> 式として表現できるものに関しては、上言語と同様に手続き(関数)を直接
> lambda文(関数リテラル?)として渡すことができるのですが、Pythonでlamb
> daで表記できるのは式文だけであり、他の文構造や、代入文の混じったもの
> は一切記述できません。これが、Pythonの記述上の欠陥であるとよく言われ
> てきました。

昔Pythonに手を出そうとしましたが、それを知って愕然としました。

>  どうぞ、RubyやSmalltakやLispを愛用している方々、「ブロックってこん
> なに便利なんだぞ」的な例をご教示いただけたら、と思います。

Rubyのブロックは美化された高階関数です。
関数を引数に取るLisp関数のうち、圧倒的多数がひとつの関数しか取らないため、
Rubyのブロックはひとつだけという設計上の決定に至ったとどこかで聞きました。

ブロックに複文が記述できる利点はやっぱり制御構造の抽象化です。
自分で制御構造の構文を作れる感覚です。

たとえばHash#fetchはハッシュのキーに対応する値を得るメソッドですが、
ブロックを持つことができます。
ブロックはキーが存在しない場合のみに評価されます。
だからブロック中にキーが存在しない場合の処理をごにょごにょ書けるのです。

hash = {:one=>1, :two=>2}
hash.fetch(:two)                # => 2
hash.fetch(:three) {|key| "key(#{key.inspect}) is not found" } # => "key(:three) is not found"

次の例は二次元配列のインデックスつきループを定義します。

class Array
  def each_with_index_2dim
    each_with_index do |inner, i|
      inner.each_with_index do |elem, j|
        yield(elem, i, j)
      end
    end
  end
end

ary2 = [[1,2],[3,4],[5,6]]
ary2.each_with_index_2dim{|e, i, j| puts "ary2[#{i}, #{j}]=#{e}"}
# >> ary2[0, 0]=1
# >> ary2[0, 1]=2
# >> ary2[1, 0]=3
# >> ary2[1, 1]=4
# >> ary2[2, 0]=5
# >> ary2[2, 1]=6

例ではブロックの中身は1つの式ですが、当然複数の式が書けます。
if等の条件分岐が式として値を持つのが地味に便利です。

他にもソートの条件とかもブロックで指定します。

--
rubikitch
Blog: http://d.hatena.ne.jp/rubikitch/
Site: http://www.rubyist.net/~rubikitch/