Rubyのyieldを使った例を教えてください



yieldをどんな時に使うと便利なのか今一つピンとこないので
「こんな時に使うと便利」というサンプルコードがあれば教えてください。

回答の条件
  • URL必須
  • 1人10回まで
  • 登録:
  • 終了:2008/08/08 16:00:03
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答3件)

id:rubikitch No.1

回答回数120ベストアンサー獲得回数22

ポイント35pt

yieldはブロック呼び出しに使います。ブロックはRuby流無名関数(高階関数)です。yieldはブロックを評価した結果を返します。この用法はブロックの評価結果を使用するタイプです。

例えば、コレクションの値の総和や二乗和を求めるようなコードはこんなのです。


module Enumerable
  def sum
    if block_given?
      inject(0) {|s, x| s + yield(x) }
    else
      inject(:+)
    end
  end
end

ary = [1, 5, 3]
# 総和
ary.sum            # => 9
# 二乗和
ary.sum {|x| x*x}  # => 35

他にもループを抽象化(自前イテレータを定義)したり、有効範囲を指定(open関数など)したり、条件を指定したり(Enumerable#find)、文脈を変更したり(Object#instance_eval)などです。

http://www.ruby-lang.org/ja/man/html/index.html

id:garyo

rubikitchさん、いつもありがとうございます。

サンプルの例もすごくいいですが、

>他にもループを抽象化(自前イテレータを定義)したり、有効範囲を指定(open関数など)したり、条件を指定したり(Enumerable#find)、文脈を変更したり(Object#instance_eval)などです。

もしこれらの例もありましたら教えてくださいm(__)m

※あと、サンプルを動かすと以下のエラーになりました

C:/DOCUME~1/mec/LOCALS~1/Temp/rb20A.tmp:15:in `inject': no block given (LocalJumpError)

2008/08/01 19:23:23
id:rubikitch No.2

回答回数120ベストアンサー獲得回数22

ポイント23pt

inject(:+)はRuby 1.8.7以降で。1.8,6以前ならば inject{|s,x| s+x} で。

有効範囲の指定ならばトランザクション処理を。

def transaction
  begin
    setup
    yield
  ensure
    teardown
  end
end
transaction { 処理! }

条件を指定するのは、Enumerable#findの兄弟とか。条件を満たす要素とともにインデックスも得る Enumerable#select_with_index を定義してみる。

module Enumerable
  def select_with_index
    ary = []
    each_with_index do |x, i|
      ary << [x, i] if yield(x)
    end
    ary
  end
end

[1, 3, 4, 5, 6].select_with_index {|x| x % 2 == 0 } # => [[4, 2], [6, 4]]

直前の値も欲しい場合は自前イテレータを定義する。

require 'enumerator'            # 1.8.7以降は不要
module Enumerable
  def each_with_previous
    yield first, nil
    each_cons(2) {|x, y| yield y, x}
  end
end

[1, 3, 4, 5].each_with_previous do |x, prev|
  [x, prev]                     # => [1, nil], [3, 1], [4, 3], [5, 4]
end

そういえば、Object#instance_evalとかにブロックを渡す場合はyieldは使えないのだった。

http://d.hatena.ne.jp/rubikitch/

id:garyo

たくさんのサンプルありがとうございます。

module Enumerableの中でeach_with_indexや、injectが定義されているのでそのまま呼べばいいわけですね。

参考になりました。

2008/08/01 19:47:50
id:kouji0625 No.3

回答回数1ベストアンサー獲得回数0

ポイント22pt

それでは、私がRubyでアプリケーションを作成するときに使った yield のサンプルを挙げます。

例:ブロックを実行する前で処理を行いたい場合

def open_with_lock(path, mode = "r")
  File.open(path, mode) do |f|
    f.flock(LOCK_EX)
    yield(f)
  end
end

open_with_lock("/path/to/file") do |f|
  s = f.read
  ...
end

例:ensureで必ず後始末をしたい場合

def periodic_process
  File.open("/path/to/timestamp", File::RDWR | File::CREAT) do |f|
    f.flock(LOCK_EX)
    begin
      yield
    ensure
      f.rewind
      f.write(Time.now)
      f.truncate(f.pos)
    end
  end
end

periodic_process do
  # ログのロテートなど...
end

私の日記: http://d.hatena.ne.jp/kouji0625/

id:garyo

ありがとうございます。確かに通常の関数より便利そうです。

rubyのコミッターおめでとうございます。

2008/08/03 11:01:23

コメントはまだありません

この質問への反応(ブックマークコメント)

トラックバック

  • Rubyのyieldを使った例を教えてください yieldをどんな時に使うと便利なのか今一つピンとこないので 「こんな時に使うと便利」というサンプルコードがあれば教えてください.. - 人力検索は
  • [ruby][book] 「初めてのruby」を読んだ オライリーは飛びついて買って積む派です。(良く言えば(?)、必要に応じて引く派) で、 初めてのRuby 作者: Yugui 出版社/メーカー: オライリージャ
  • Ruby の yeild を理解してみる **参考事例 Rubyのyieldを使った例を教えてください Enumerable - Rubyリファレンスマニュアル 標準添付ライブラリ紹介 【第 5 回】 enumerator 例題 yield はブロック呼び出
  • 2010年06月03日のツイート @cutmail: URL railsでFormHelperでSelectタグを出す - uzullaの日記 - 1981s 2010-06-03 18:06:23 via Buzzurl @cutmail: URL Rails on Vim (in English) - AkitaOnRails.com 2010-06-03 17:05:04 via Buzzurl @cutmail: URL
「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません