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

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

ただいまの
回答率

90.51%

  • Ruby

    9434questions

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

  • Ruby on Rails

    8857questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

ブロック、Proc、Lambdaの使いどころ

解決済

回答 5

投稿

  • 評価
  • クリップ 0
  • VIEW 780

yoshiky

score 99

お世話になっております。
Rubyで使われるブロックやProc,lambdaの使いどころがよくわかりません。
検索すれば情報がたくさん出てくるので、コードの理解はできるのですが、
「なぜそのように書かねばならないのか(または書いたほうがいいのか)」
が理解できません。

具体的には、わざわざ難しく書かなくても普通の関数ではいけないのかと思ってしまいます。
下記に例を示します。

# http://www.atmarkit.co.jp/ait/articles/1409/29/news035_2.html
# サンプルコード block02.rbを一部簡略化

def arithmetic_sequence(init: 1, diff: 1, count: 10)
    current = init
    count.times do
        yield(current)
        current += diff
    end
end

arithmetic_sequence(init: 2, diff: 3, count: 5) do |n|
  puts n
end


このコードは下記のように書き換えられると思います。

def arithmetic_sequence(init: 1, diff: 1, count: 10)
    current = init
    count.times do
        puts current  # <= ここがyieldから変わっただけ
        current += diff
    end
end

arithmetic_sequence(init: 1, diff: 4, count: 10)


例が悪いかもしれませんが、要はyieldと書いてあるところでブロック内の処理をする、ということだと思います。

ならばブロック内の処理をdef...end で関数定義し、yieldと書いてあるところで呼び出せばいいだけ、と思ってしまいます(または上記例のようにyieldの箇所をそのままputs のように置き換える)

ブロックやProcを説明しているサイトは大抵「例が悪いが」とか「ありがたみが薄いかも」という注釈があるため、処理自体は理解できても、メリットが伝わりません。どういう場面で使うのか、具体的にメリットが感じられる例をご紹介頂けないでしょうか。
どういう流れでProcやLambdaを定義しようと思いつくのか、その思考回路でも構いません。
Qiitaの記事に『例えば、開発者Aが「5という数で好きな事をしてもらうメソッドを作ろう」と考えた場合』とあるような形です(この例もメリットは感じられませんでしたが。。)

依頼ばかりの質問で申し訳ないですが、よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 5

checkベストアンサー

+3

決まった事しかしないなら、その決まった処理のコードを書けばいいです。

いろんな事を出来るようにするために、その処理を引数化します。

あなたの言っている事は、「

def arithmetic_sequence
    current = 1
    10.times do
        puts current
        current += 1
    end
end


と書けば、呼び出し側から初期値、増分、終了値を指定する必要が無くなります。」
と言ってるのと本質的には同じです。

また、

ならばブロック内の処理をdef...end で関数定義し、yieldと書いてあるところで呼び出せばいいだけ、と思ってしまいます

というのは、「引数にしなくても、グローバル変数で渡せばいいじゃない」と言っているのと同じです。

標準のメソッドで、each map sort_by などなど便利な物がたくさんありますが、
mapで呼び出してもらうメソッドを、(引数で渡すのじゃなくて)map_callbackという名前で事前に定義した上でmapを呼んでください」という仕様だと、mapを複数回呼ぼうとした時点で破綻します。

追記

上記の後半(「また、」以降)が理解出来ないと言う事なので。

yoshikyさんの言ってるのはこういう事ですよね?

  1. 処理を引数で渡さなくても、呼び出されるメソッドの中に直接処理を書けばいいじゃないか
  2. それだと、他のことをしたいときに、メソッドを毎回書き直さないといけないじゃないか
  3. じゃあ、書き直さなくて言いように、処理にdef~endで名前を付けて、その名前をメソッドの中から呼べばいいじゃないか

「その考えはこれと同じだよ」と私が書いたのは、

  1. 引数なんかで渡さなくても、メソッド中でその値を直接書けばいいじゃないか
  2. それだと、呼び出し時に値を変えたい時に困るじゃないか。メソッドにした意味がない
  3. じゃあ、グローバル変数経由で値を渡せば引数が要らないですよ

こちらは map(&:hoge)などの記法のことでしょうか。こちらもこのコードの意味は理解できても、メリットが思いつかない、

そもそも、メソッドを作るメリットを何だと思っていますか?そこから分からないですか?
メソッドに引数という仕組みがあるメリットは理解出来ますか?そこから分からないですか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/02 10:46

    ご回答ありがとうございます。

    「いろんなことができるようにする」ためにブロックなりProcの機能がある、ということは理解しているつもりです。しかし、具体的な例が思いつかないのです。確かにおっしゃる通り「決まった事しかしないなら、その決まった処理のコードを書けばいい」のですが、、、どういう思いつきで「ここはProcを使おう」と思い至るのでしょうね。

    また「呼び出し側が(ブロック内に)処理を定義する」メリットがいまいち分かりません。「定義した処理を呼び出す」と大差がないように思えてしまいます。

    キャンセル

  • 2017/05/02 11:08

    > 「定義した処理を呼び出す」と大差がないように思えてしまいます。

    回答の後半にその答えを書いたつもりですが、いかがでしょうか?

    キャンセル

  • 2017/05/02 11:43

    申し訳ないです。お恥ずかしいですが、ご回答の後半は理解できませんでした。

    > 「引数にしなくても、グローバル変数で渡せばいいじゃない」と言っているのと同じ
    本質の話をして頂いたのかもしれませんが、引数とかグローバル変数がどう関係するのか分かりませんでした。

    > mapで呼び出してもらうメソッドを…
    こちらは map(&:hoge)などの記法のことでしょうか。こちらもこのコードの意味は理解できても、メリットが思いつかない、もっと言えばどういう経緯でこういうコードを書こうと思ったかが分からないのです。(そのため、ブロックとかProcを一から勉強しようと思い、こちらに投稿させて頂いた次第です)

    阿呆を相手にしていると疲れるかと思います、、申し訳ないです。。

    キャンセル

  • 2017/05/02 13:51 編集

    追記しました。最後の2つの問いに答えてください。

    キャンセル

  • 2017/05/02 14:24

    お付き合い頂き、ありがとうございます。
    メソッドを作るメリットは「処理の共通化、部品化」と捉えております。
    メソッドに引数という仕組みがあるメリットは「共通化・部品化した処理を更に汎用的にするため」と解釈しています。

    > じゃあ、グローバル変数経由で値を渡せば引数が要らないですよ
    順序立ててご説明頂き、ありがとうございます。

    例えば2つの値を足し算する関数sumがあったとして、引数を使わないで実現するならグローバル変数を2つ定義し、sum内でそのグローバル変数同士を加算すればよく、加算する値を変えたいときは定義したグローバル変数の値を変えてあげればよい、というように理解しました。
    確かにグローバル変数を使う例でも要件は満たせるが、加算したい値が増えたら?減算したい場合は? ということを考えると、値ではなく「処理」を渡したほうが効率的、というように思い至ることができました。

    キャンセル

  • 2017/05/02 15:45 編集

    ご理解の通りで良いと思います。

    「処理」を引数で渡せないと、map や sort_by のような汎用メソッドが書けないです。
    「処理をメソッドとして別途定義して、そのメソッドの名前を引数で渡す」というやり方でも同じことが出来ます。
    名前を付けるほどではないその場限りの処理であれば、名前を付けずにでも書ける方が楽です。

    キャンセル

+3

yoorwmさまの言われていることで全てを説明しているのですが
さすがにこれではyoshiky様が納得しないと思うので。補足させていただきます。

block yieldの強み(の一つ?)は、使うタイミングで簡単に、関数を書き換えて使うことができる
ということです。

Rubyを語る上で欠かせないmap関数を例にとってみてみましょう。

mapは配列から配列を返す関数です。

これをblockを渡さないで実行すると自分自身を返す何もしない関数になります。(正確には違いますが)

[1,2,3].map #=>[1,2,3]


こんなもの何に使うんだよ!!
ただの恒等写像じゃん!!
と思うかもしれませんがblockを渡すとあら不思議
各項を2倍したり

[1,2,3].map{|a|a*2} #=>[2,4,6]


各項を文字列にしたり

[1,2,3].map{|a| a.to_s } #=>['1','2','3']


奇数だけを抽出したり

[1,2,3].map{|a| a if (a % 2) == 1 } #=>[1,nil,3]


素数だけを抽出したり

[1,2,3].map{|a| a if a.primary? } #=>[nil,2,3]


3の倍数の時にあほになったり

[1,2,3].map{|a| (a % 3) == 0 ? "( ^ิ౪^ิ )" : "(・`ω´・ ○)" } #=>["(・`ω´・ ○)","(・`ω´・ ○)","( ^ิ౪^ิ )"]


あなたはこれらのことをするのに
関数をいちいち定義して、読み込んで使うのですか?

ツールを自由度を高く作り、使用者が使用時に好きにカスタマイズして使う
これはRubyの哲学の一つです。(DRYの一部って言われたら反論のしようはございませんが...)

ProcやLambdaは奥が深く、良さの1割も説明していないと思いますが
(そもそも、私も半分理解してるのかさえ怪しい)
yoshiky様の疑問がトンチンカンなことは理解できてくれると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/02 14:11 編集

    ご回答ありがとうございます。
    頂いた例は、分かりやすかったです。おっしゃる通り、関数を定義してご提示いただいた例を実現するには、その分関数を作る必要があるので、冗長になりますね。

    ブロックやProcの使用例がアプリ開発で使うような内容でないため、イメージがつきません。。
    普段はネット上の例しか見れないので、実際のアプリ開発においてどのように使われるのかが想像できませんが、それはネットを巡回して公開されているソースでも読むしかないですね。。
    思いつく限りだと
    「csvファイルから売上ファイルをインポートするが、取引先に応じてファイルのフォーマットが異なるので、取引先ごとに取込用に変換する処理を定義し、取込時にはその「処理」を引数にしてインポートする」とかでした。

    キャンセル

  • 2017/05/02 14:59

    ああ、そっちがメインだったんですね。すみません、早とちりでした。
    基本csvファイルのようなライブラリとか根本的なところで使いますものね
    Railsではあらかじめyieldを使ったmethodが組み込まれているので
    作るタイミングは少ない…。
    そうだ、helper methodとかを作ると、ありがたみがわかるかもしれません。

    キャンセル

+1

リストの各要素を

  • 2倍する。
  • 3倍する。
  • 4倍する。
  • ...

に対してそれぞれメソッドを作るのは、
「リストの各要素を何倍かする」という部分は共通ですから無駄ですね。
それだったら何倍するかは引数で持たせて、

  • n倍する。

でいいですね。

同様に、
リストの各要素を

  • n足す。
  • nで割る。
  • n倍する。
  • 平方根を取る。
  • 0以下だったら0にする。

に対してそれぞれメソッドを作るのは、
「リストの各要素に何かする」という部分は共通ですから無駄ですね。
それだったら何をするかは引数で持たせて、

  • リストの各要素に関数fを作用させる。

でいいわけです。

こういう発想でできているのがmapです。


質問文のこれ

def arithmetic_sequence(init: 1, diff: 1, count: 10)
    current = init
    count.times do
        puts current
        current += diff
    end
end

arithmetic_sequence(init: 1, diff: 4, count: 10)

putもしつつ、ファイル書き出しもしたくなったらどうしますか?
arithmetic_sequence_putarithmetic_sequence_write_fileを作りますか?
もしその2つを作った後に、共通部分にバグや変更があったらどうしますか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/02 13:58

    ご回答ありがとうございます。
    「putもしつつ、ファイル書き出しもしたくなったらどうしますか?」という部分は合点がいきました。数字や文字列、配列などを引数に渡す時と同様、ブロックの形で「処理(関数)」を引数に渡し、渡された側でその「処理(関数)」を実行することで共通化を図っている、というように理解しました。

    私の例で言えば、putsだけでなくファイル書き出しが追加、または別の処理ではputsではなくDBに保存する、また別の処理ではDBに保存しつつファイルにも書き出す、、、などなど、「処理」の部分を引数に渡してあげることで、if で分岐させたりせずに冗長なコードをすっきりさせることができそうですね。

    残念ながら他のいい例が思いつかなので、その辺りは公開されているソースコードでも読むしかないですね。。

    キャンセル

0

yieldを使っている部分は、俗にジェネレータと呼ばれます

ジェネレータで作ると、呼び出し側で好きなタイミングで処理させる事が出来ますので、部品として使えるようになります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/02 11:50

    ご回答ありがとうございます。

    >呼び出し側で好きなタイミングで処理させる事が出来ます
    こちらは関数でもできるのではないでしょうか?例えば先述の私の例だと、
    「引数の値をputsで出力する」と言う関数を
    def hoge(arg)
    puts arg
    end

    などと定義して、呼び出したい箇所で

    hoge (123)

    とすればいいのでは、、と思ってしまいます。
    ブロックで処理するメリットが見出せませんでした(例が悪いのでしょうが)。

    「ブロックを使ったほうがいい!」と言う例があればご教示いただきたいです。

    キャンセル

0

すみません。
回答ではないのですが、コメントへの返信も込みで
全く私と同じ考えだったのでコメントさせて頂きました。
(全く同じ内容でたった今質問をしようとしていました笑)

例えば、
ログイン確認処理が必要なアプリで、
before_acitonが便利な理由として、

全メソッドでユーザがログイン済みかどうか確認するのは面倒だから
ログイン済みか確認する処理を独立したメソッドとして登録しておく。

そして、
それをbefore_actionに登録しておくことで、
全メソッドでいちいちログイン確認しなくても
一箇所で宣言だけしてあとはそれを呼ぶだけでいいので
「可読性が上がり、変更も容易になる」というメリットがある。

というのは腹落ちする。

しかし、procに関しては
本当に実際の開発時に便利な例というのが探しても見当たらないので
メリットがよくわからないということですよね笑

(具体的な例といえば、yoshikyさまが仰る通り『例えば、開発者Aが「5という数で好きな事をしてもらうメソッドを作ろう」と考えた場合』というような例なのですが、これも実際の現場で活かせるメリットが感じられず、結局何が嬉しいのかわからずじまいです。。)

私も
procが重要か重要でないかという話ではなくて
現場の開発においてこういう時は(使わないより)使った方が便利という具体例を知りたいです。

ご質問から1年経たれて
もしyoshiky様が
こういう時にprocを使ったら便利だったよというような例があれば
ぜひご教示頂きたいです。(逆に質問することになってしまい申し訳ありません。)

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

  • Ruby

    9434questions

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

  • Ruby on Rails

    8857questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。