Help us understand the problem. What is going on with this article?

ループ構文内外とブロック内外でのスコープの違いについて

More than 3 years have passed since last update.

基本的なスコープの違いを正しく理解できていませんでした。
以下にまとめました。

スコープを作らないグループ(for, while)

for

ループ構文の内外でスコープは変わらない。
forで渡す式はブロックではない

for
for i in [1, 2, 3] do
  num = i
end
puts num
出力
3

forではdoは省略可能

for(doの省略)
for i in [1, 2, 3]
  num = i
end
puts num
出力
3

forではループ変数もスコープが変わらない

ループ変数iをforの外でも参照できる

for(ループ変数の参照)
for i in [1, 2, 3]
end
puts i
出力
3

while

whileで渡す式はブロックではない

while
a = [1, 2, 3]
counter = 0

while counter < a.size do
  num = a[counter]
  counter += 1  
end

puts num
出力
3

whileでもdoは省略可能

while(do省略)
a = [1, 2, 3]
counter = 0

while counter < a.size
  num = a[counter]
  counter += 1  
end

puts num
出力
3

スコープを作るグループ(each, loop)

eachメソッド

以下の例ではArrayクラスのeachメソッドである。Class: Array (Ruby 2_4_0)
ブロックの内外でスコープが変わる。
eachはもちろんdo必須

each
[1, 2, 3].each do |i|
  num = i
end
puts num
出力
each.rb:5:in `<main>': undefined local variable or method `num' for main:Object (NameError)

loopメソッド

Kernelモジュールのloopメソッドである。Module: Kernel (Ruby 2_4_0)
ブロックの内外でスコープが変わる。
loopでもdo必須

loop
a = [1, 2, 3]
counter = 0
loop do
  break if counter == a.size
  num = a[counter]
  counter += 1
end

puts num
出力
loop.rb:9:in `<main>': undefined local variable or method `num' for main:Object (NameError)

loopでdoを省略すると、構文エラーで怒られる

loop(do省略)
a = [1, 2, 3]
counter = 0
loop
  break if counter == a.size
  num = a[counter]
  counter += 1
end

puts num
出力
loop.rb:7: syntax error, unexpected keyword_end, expecting end-of-input

まとめ

for, whileではループ構文の内外で変数のスコープは変わらない
each, loopではブロックの内外で変数のスコープが変わる

参考リンク

るりま

http://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fcontrol.html#for
制御構造 > 繰り返し > for

for i in [1, 2, 3]
  print i*2, "\n"
end
文法
for lhs ...  in  [do]
  ..
end

式を評価した結果のオブジェクトの各要素に対して本体を繰り返して実行します。これは以下の式とほぼ等価です。

().each `{' `|' lhs..`|' .. `}'

「ほぼ」というのは、do ... endまたは{ }によるブロックは新しいローカル変数の有効範囲を導入するのに対し、for文はローカル変数のスコープに影響を及ぼさない点が異なるからです。
for は、in に指定したオブジェクトの each メソッドの戻り値を返します。

パーフェクトRuby(p.80 3-2-2繰り返し)

for name in %w(Alice Bob Carol)
  puts name # 配列の要素が順番に出力される
end

# ループの中で定義された変数を参照できる
puts name # "Carol" と表示

each では繰り返しをブロックで記述していましたが、for に渡す式はブロックではありません。
ループの内外で変数のスコープは変わらないため、for の中で使用している変数 name は for の外から参照できます。

その他

Ruby勉強会@札幌-22に参加しました
※「初めてのRuby読み合わせ 6.3.5 for式」の部分

eachはブロック内で定義した変数のスコープはブロック内だけだけど、forの場合はブロックの外側でも変数を参照できる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away