これは、add_one
関数内で関数外と同じi
変数を操作していないためです。
それについて少し説明させてください。Rubyでは、一般的に変更可能なオブジェクトを扱います(注目すべき例外は、数字、、、、ですtrue
)。変数は、そのようなオブジェクトへのポインターです。複数の変数が同じオブジェクトを指すことができます。false
nil
a = 123
b = a
ここで、まったく同じオブジェクトa
を参照します。またはのいずれかb
に新しいオブジェクトを割り当てると、名前を保持したまま、別のオブジェクトを再度参照します。a
b
上にあるのはローカル変数です。これらはスコープ内でのみ有効であり、ほとんどの場合、メソッドの期間中です。(値を割り当てることによって)新しいローカル変数を作成する場合、それはメソッドの期間中のみ有効であり、メソッドを離れた後のある時点でガベージコレクションされます。
上で述べたように、Rubyのほとんどのオブジェクトは変更可能です。つまり、変数ポインターを保持したままオブジェクトを変更できます。例として、配列に要素を追加します。
array = []
array << :foo
これで、array
変数は初期化されたのと同じ配列オブジェクトを参照します。しかし、あなたはオブジェクトを変更しました。array
変数に次のような新しい配列を割り当てる場合
array = [:foo]
同じオブジェクトのように見えますが、事実上、それらは異なります(object_id
すべてのオブジェクトでメソッドをチェックしていることを確認できます。同じ場合は、まったく同じオブジェクトを参照しています)
これi += 1
で、add_one
メソッドで実行すると、効果的に実行i = i + 1
され、ローカルメソッドスコープでi変数が新しい値に設定されます。実際にはi
変数を変更していませんが、ローカルメソッドスコープで新しい値を割り当てます。これは、外部スコープ(つまり、開始/終了ブロック)で指定された変数が、メソッド内の変数とi
は異なるオブジェクトを参照することを意味します。これは、名前は同じですが、スコープが異なるためです。内側のスコープは常に外側のスコープをマスクするため、異なるスコープに同じ名前の変数がある場合でも、干渉することはありません(これはインスタンス変数またはクラス変数を処理するときに変更されます)i
add_one
残念ながら、上で述べたように、数値は不変です。それらを変更することはできません。変数に新しい番号を割り当てると、それは新しいオブジェクトになります。したがって、値を変更するコードとして、別のスコープ内の数値を指す変数の値を変更することはできません。
i
これを回避するには、関数から値を返し、このように外部スコープの変数に明示的に割り当てることができます。
i = 0
def add_one(i)
i+=1
puts "FUNCTION:#{i}"
return i
end
i = add_one(i)
または、このようなオブジェクトのインスタンス変数を使用できます
class Foo
def initialize
@i = 0
end
def add_one
@i += 1
end
def do_something
begin
puts "BEGIN:#{@i}"
raise unless @i>5
rescue
add_one
puts "RESCUE:#{@i}"
retry
end
end
end
# create a new object and run the instance method
Foo.new.do_something