2011年4月24日日曜日

多重代入と変数の swap - Python と Ruby の比較

Python で多重代入を利用した変数の swap

Python の 6.3 代入文 (assignment statement) によると、

代入の定義では、左辺値と右辺値がオーバラップするような代入 (例えば、"a, b = b, a" を行うと、二つの変数を入れ替えます) を定義しても `安全 (safe)' に代入できます …

例えば、2 つの変数 a, b を入れ替えるなら、変数の値を退避しておく一時変数を省略できる。

a = "hoge"
b = "piyo"

a, b = b, a

print a,b         #=> piyo hoge

 

代入対象の変数にオーバーラップがある場合

注意点としては、

代入対象となる変数群 の間で オーバラップがある場合は安全ではありません!

(同上より)

次のようなケース。

ary = ["hoge", "piyo", "fuga"]
i = 100

i, ary[i] = 1, 999

print ary          #=> ['hoge', 999, 'fuga']

もし、swap が見かけ通り一つの文として同時に実行されているなら、swap を行う時点の i の値は 100 となり、ary[100] は存在しないためにエラーとなる。しかし、実際には最初の i に 1 が代入されたことにより、i の値が上書きされ、ary[1] に 999 が代入される。

以下のように順次実行されているのと同じように見える。

i= 1
ary[i] = 999

しかし、変数を a, b を swap したときのように swap させると、

ary = [1,2,3,4,5]
i = 0

i, ary[i] = ary[i], i

print i, ary

結果は、

1 [1, 0, 3, 4, 5]

となる。 右辺の i は更新される前の i の値を参照しているようで、何だかよくわからなくなってきた。  (+_+)

てっきり、以下のように実行されるのかと思った。

i = ary[i]
ary[i] = i

右辺の代入される値は、多重代入の前の変数で名前解決が行われるのかな?

i = ary[0]
ary[i] = 0

 

Ruby で多重代入を利用した変数の swap

同じく多重代入ができる Ruby で動作を確認すると、

a = "hoge"
b = "piyo"

a, b = b, a

p a,b        

次のように同じ結果となる。

"piyo"
"hoge"

また、「代入対象となる変数群の間でオーバラップがある場合」も結果は同じ。

ary = ["hoge", "piyo", "fuga"]
i = 100       

i, ary[i] = 1, 999

p ary          #=> ["hoge", 999, "fuga"]

以下も Python と同じ。

ary = [1,2,3,4,5]
i = 0

i, ary[i] = ary[i], i

p i, ary

結果は、

1
[1, 0, 3, 4, 5]