質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.61%

  • Ruby

    7319questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby yieldでのコードの書き方について

解決済

回答 3

投稿

  • 評価
  • クリップ 3
  • VIEW 4,238

cmu2008

score 98

yieldやProcについて学習をしているのですが、yieldの理解に苦しんでいます。

yieldはブロックを呼び出す、Procはブロックのオブジェクト化というのは理解しました。
ここでブロックがでてきたりして、yieldの事がよくわからなくなってきました。

どなたか、yieldを使ったコードの書き方の理由について詳しく教えてください。

宜しくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

「 yield を使ったコードの書き方の理由」の意味がよくわかりませんが、"どうして yield のような書き方をするのか" というような感じで説明してみます。



例えば、次のように配列の要素に順にアクセスして何らかの処理(p it)をするコードがあったとします。
ary = [1, 2, 3]
cnt = 0
while ( cnt < ary.size)
  it = ary[cnt]
  
  p it

  cnt += 1
end

ループカウンターを使うとしたら、同じようなコードを毎回書かないといけません。
そのような場合は、同じようなコードは関数にまとめて、変わる部分だけをパラメーターとして渡すようにした方が楽です。
ここでパラメーターとしたいのは配列と処理の部分 p it です。

def aryeach(ary, xxx)
  cnt = 0
  while ( cnt < ary.size)
    it = ary[cnt]
    
    # 処理 xxx を実行したい

    cnt += 1
  end
end

# p it の処理を渡したい
aryeach([1,2,3], xxx)
xxx としてますが、p it というコードを関数aryeachに渡たせれば、用途の広い関数になります。



まず渡す側を考えてみます。
処理を渡す場合には素直に考えると、関数にしてその関数を渡します。
def dumpfunc(it)
  p it
end

aryeach([1,2,3], dumpfunc)

しかし、 上記のコードではエラーになります。 Ruby では関数をそのままでは渡せないので、 コードを Proc オブジェクトにします。
dumpfunc = Proc.new{|it|
  p it
}

aryeach([1,2,3], dumpfunc)

処理を離して書くのも嫌ですし、いちいち dumpfunc の変数に入れるのも無駄なので処理を関数に直接渡します。
aryeach([1,2,3],
        Proc.new{|it|
          p it
        })


次に Proc の受け取り側 を考えます。
Proc は call で実行することができます。
def aryeach(ary, func)
  cnt = 0
  while ( cnt < ary.size)
    it = ary[cnt]

    func.call(it)   # p it を実行

    cnt += 1
  end
end

aryeach([1,2,3],
        Proc.new{|it|
          p it
        })

これが Proc を使って書きなおした場合です。
他の言語でも、 Proc ではなく function など関数定義になりますが、同じようなことができます。




しかし、処理のコードを引数として渡すと ) が後ろの方に来るなど、ちょっと汚いです。
Ruby では、一歩進んでもっとエレガントに書くことができます。それがブロックです。

ブロックを渡す側 では引数として渡していたのを関数の後ろにつけることができます。
これで ) も前にきますし、 Proc.new も書く必要がありません。
aryeach([1,2,3]){|it|
  p it
}

これで渡す側は綺麗に書けましたが、ブロックにしたことによって受け取り側も変える必要があります。
ブロックも同じような引数として受け取りますが、必ず最後の引数になりますし、ブロックのサインである & が付きます。
def aryeach(ary, &blk)
  cnt = 0
  while ( cnt < ary.size)
    it = ary[cnt]
    
    blk.call(it)   # p it を実行

    cnt += 1
  end
end

blk.call は引数の変数blkを使ってますが、ブロックは一つと決まっているので、もう少し簡単に書けます。
それが yield です。
def aryeach(ary, &blk)
  cnt = 0
  while ( cnt < ary.size)
    it = ary[cnt]
    
    yield(it)   # p it を実行

    cnt += 1
  end
end

Ruby では大抵 () は省略できます。また、 blk の引数も出てこなくなったので、省略できます。
最終的にブロック で書き直すと次のようになります。

def aryeach(ary)
  cnt = 0
  while ( cnt < ary.size)
    it = ary[cnt]
    
    yield it  # p it を実行

    cnt += 1
  end
end

aryeach([1,2,3]){|it|
  p it
}


結局のところ、 "ブロックと yield は Proc を綺麗に書けるようにしたもの" ということです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2014/09/17 09:35

    とても親切な回答ありがとうございました!

    キャンセル

+1

yieldを使ったコードの書き方という表現が少しわかりにくいのですが、yieldの省略した形についてでしょうかね。

まず、ブロックはどういったものがわかりますでしょうか?
do end、{}で囲まれている引数になるためのまとまったものというのがブロックといいます。
ここで質問者様が仰っておられるように、yieldはブロックを呼び出し、Procはブロックをオブジェクト化したものです。

そこでyeildについて説明します。

(例1)
def gblock
yield
end

gblock do
p 'block'
end
そこで以下の書き方もできます。

(例2)
def gblock(&block)
block.call
end

gblock do
p 'block'
end

結果は同じです。
ここでの&は、&をつけることで、引数にブロックが渡ってきた時にProcオブジェクトに変換しています。
Procオブジェクトというのは、ブロックをオブジェクトしたものです。
またProcオブジェクトはcallで呼び出せます。さらに、ブロック引数は1つだけしか渡せません。

なので、以下のように編集できます。
(例3)
def gblock($block)
yield
end

gblock do
p 'block'
end

ここで&blockも実際必要がないということがわかります。

(例4)
def gblock
yield
end

gblock do
p 'block'
end

ということでyieldは(例4)の形になって使われています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1


ここ teratail でも何回も yield に関する質問がでています。
yield で検索して、回答を眺めてみるといいです。
(この質問ページに右側の 【関連した質問】にそれらが表示されていないのはちょっと不便ですね。)

Qiita にも yield についての投稿がたくさんあります。そのうちの1つを紹介します。
ブロックとProcをちゃんと理解する

yield について理解する方法として 私のおすすめは、書籍 "Ruby メタプログミング" を読む事です。
2010 年に日本語訳が出てますが、今年 原書のほうでは 新版がでました。
http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Facets/dp/1941222129

teratail で この本の読んでみんなで質疑応答する なんて事ができないかなぁ...

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    Ruby ブロックを他のメソッドに渡す方法

    メソッドの中で、yieldを使ってブロックを実行していたのですが、 yieldだと足りない場面が2つでてきました。 1つ目が、他のメソッドにブロックを渡したい時 2つ目が、ブロ

  • 解決済

    Ruby 呼び出し可能なオブジェクトについて

    スコープを持ち運べるコードで、呼び出し可能なオブジェクトについてなのですが、 私は、ブロックと、Procとメソッドを知っているのですが、 これらのほかに呼び出し可能なオブジェク

  • 解決済

    Ruby ブロックの渡しについて

    Rubyをはじめたばかりのものです。 そこで、困ったことがあります。 ブロックを使ったメソッドの呼び出しについてなのですが、 ブロックはメソッド1つに対して、1つしか渡すことが

  • 解決済

    Ruby ブロックはオブジェクトではないのですか?

    Rubyにあるものすべてがオブジェクトだと思っていたのですが、 ブロックはオブジェクトではないようなことを聞きました。 これは本当なのでしょうか? どなたか教えてください。

  • 解決済

    Ruby yieldの使い方について詳しく教えてください

    Ruby初心者です。 まだ始めたばかりであまり理解ができていないことが多いです。 今回、わからなくなっているのが 「yield」についてです。 どなたかyieldについてわかり

  • 受付中

    Ruby ブロックの基本について

    Ruby初心者です。 ブロックというのがどうもわからず困っています。 基本で躓いてしまっているのですが、 どなたかわかりやすく、どういったものかを教えてください。 また、使用例も

  • 解決済

    JavaScriptで;ではなく,で区切る意図は?

    JavaScriptを眺めていると、以下のようなコードに出くわすことがあります。 1,2,3,4,5;6,7,8;9 私はこれまで、文は;で区切るものだと思っていたのですが、コ

  • 解決済

    Rubyのyieldの挙動が理解できていません。

    Rubyのyieldの挙動が理解できていません。 以下がサンプルコードになります。 def with_current_time yield Time.now end

同じタグがついた質問を見る

  • Ruby

    7319questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

関連ワード: ruby yield