Edited at

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

More than 1 year has 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の場合はブロックの外側でも変数を参照できる。