🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby

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

スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

Q&A

解決済

2回答

689閲覧

Ruby yieldを使ったコードとProcオブジェクトを生成したコードで出力が違う

Tai4

総合スコア49

Ruby

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

スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

0グッド

0クリップ

投稿2020/12/09 14:00

ソースコード

Ruby

1def enjoy 2 output = "Enjoy " 3 yield 4 puts output 5end 6 7enjoy { output << "Programming!"}

Ruby

1def enjoy 2 output = "Enjoy " 3 Proc.new { output << "Programming!"}.call 4 puts output 5end 6 7enjoy

出力

Main.rb:7:in `block in <main>': undefined local variable or method `output' for main:Object (NameError) from Main.rb:3:in `enjoy' from Main.rb:7:in `<main>'
Enjoy Programming!

質問

1.上記二つのコードの出力結果が異なっている原因がわかりません。ブロックがProcオブジェクトへ変換されているのならこのようにしても同じになると思っています。

2.この原因について調べていたところ、手続きオブジェクトについてリファレンスには以下のように記載されていたのですが、あまり良く理解できませんでした。教えていただけると幸いです。

手続きオブジェクトとは

手続きオブジェクトとはブロックをコンテキスト(ローカル変数のスコープやスタックフレーム)とともにオブジェクトしたものです。Procクラスのインスタンスとして実現されています。ブロック内では、新たなスコープが導入されるとともに、外側のローカル変数を参照できます。

試したこと

ローカル変数をインスタンス変数に変えて実行したところ、予想通りの出力を得られました。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

外側のローカル変数を参照できます。

とありますが、ブロック内から参照できるのは字句的に外側にあるローカル変数です。ブロックを呼び出したメソッドのローカル変数と結びつくわけではありません。

投稿2020/12/09 14:18

maisumakun

総合スコア145977

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

enjoy { output << "Programming!"}

outputという変数が未定義で、いきなり<<しているのでエラーです。

def enjoyの中で定義しているつもりかも知れませんが、それはメソッドのローカル変数です。
メソッドの外とは関係ないです。

手続きオブジェクトとは

外側のローカル変数を参照できます。

なので、

Proc.new { output << "Programming!"}.call

のブロック内のoutputはその外側(def enjoyの内側)のoutputと同じです。

意図通りのことをしたいのなら、outputというローカル変数じゃなくてグローバル変数にすると出来ます。

Ruby

1def enjoy 2 $output = "Enjoy " 3 yield 4 puts $output 5end 6 7enjoy { $output << "Programming!"}

グローバル変数をこのように使うのは普通では無いですが。

普通は、ブロック引き数を使って、

Ruby

1def enjoy 2 output = "Enjoy " 3 yield output 4 puts output 5end 6 7enjoy {|x| x << "Programming!"}

でしょうね。

投稿2020/12/09 14:17

編集2020/12/09 14:23
otn

総合スコア85893

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Tai4

2020/12/09 14:48

> outputという変数が未定義で、いきなり<<しているのでエラーです。 Procオブジェクトが実行されるのはcallされてからだと思うので、yieldの前でoutputを定義しておけば大丈夫だと思っていたのですが... defined?を使ってみると、たしかに未定義なことが確認できました。 ただ、未定義になる理由がわからないです...
otn

2020/12/09 14:55 編集

プログラムにはクラスやメソッドという構造があります。 メソッド定義の中のローカル変数は、メソッドにローカルな変数です。
Tai4

2020/12/09 14:59

yieldでProcオブジェクトが実行されるのであれば、 メソッド定義内で output << Programming! としていることにはやっぱりならないんですかね...?
otn

2020/12/09 15:03

ブロックやメソッドは、それが書かれた環境で実行されます。 呼び出した側の環境で実行されるわけじゃいです。
Tai4

2020/12/09 15:13 編集

実行されるのはcallされてからではないのですか? ブロックは実行される前にまず手続きオブジェクト(Procオブジェクト)に変換されてから、callされてやっと実行されるという認識なのですが... ```Ruby def enjoy(&proc) output = "Enjoy " puts @output end enjoy { output << "Programming!" } ``` のようにしてもエラーを吐きませんでした。 解釈が違っていればすみません... P.S. マークダウン使えないの忘れてました。 もしかして、実行される場所のことをおっしゃっていたのですか? それなら自分の解釈が間違ってますね... すみません
otn

2020/12/09 15:16

> プログラムにはクラスやメソッドという構造があります。 > メソッド定義の中のローカル変数は、メソッドにローカルな変数です。 > ブロックやメソッドは、それが書かれた環境で実行されます。 > 呼び出した側の環境で実行されるわけじゃいです。 の意味が理解できないということでしょうか? > 実行されるのはcallされてからではないのですか? それは当たり前です。 > のようにしてもエラーを吐きませんでした。 ブロックが呼ばれないので、エラーにはなりません。 if false puts xxxxxx end と未定義変数を実行されないところに書いてもエラーにならないのと同じです。
Tai4

2020/12/09 15:20

> ブロックやメソッドは、それが書かれた環境で実行されます。 これを即実行と仰られているのかと勘違いしておりました。 callするとenjoyの行で実行されるという認識で大丈夫ですか?
otn

2020/12/09 15:38

> > ブロックやメソッドは、それが書かれた環境で実行されます。 > これを即実行と仰られているのかと勘違いしておりました。 ああ、そういう誤読ですか。 「呼ばれないと実行されないのは当たり前」という大前提があるので。。。 > callするとenjoyの行で実行されるという認識で大丈夫ですか? enjoyメソッド呼び出しの行ということであれば、その通りです。 他の言語だと、呼び出し時にブロックを直接書けないので、Rubyだとブロックに書くのと同内容の関数を引き数に渡しますが、それを「コールバック関数」と呼びます。呼び返してもらうと言うことで、Rubyのブロックも同じです。 > enjoy { output << "Programming!"} で、enjoyを呼び出したけど、そのenjoyからブロックが呼び返されるということ。
Tai4

2020/12/09 15:54

なるほど~ Ruby以外はほとんど触ったことがないので勉強になります。 しかし、callした環境で実行されるものだと勝手に思ってました... 自分の検索力が弱いのか中々調べてもそういうことは出てこなかったもので(苦笑) 今回は何回も質問してしまって申し訳ないです。回答ありがとうございました。
otn

2020/12/09 16:28 編集

呼んだ環境で実行しようとすると、eval系を使うしか無いでしょうね。 def enjoy(proc) output = "Enjoy " eval proc puts output end enjoy 'output << "Programming!"' 調べるともうちょっとましな方法があるかも。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問