RubyとCの x = y + y = x について

Rubyで x = y + y = x と書くとこれは x = y + x と y = x を行ったのと同じ振る舞いをする。

> x = y = 1
=> 1
> x = y + y = x
=> 2
> [x, y]
=> [2, 1]
> x = y + y = x
=> 3
> [x, y]
=> [3, 2]
> x = y + y = x
=> 5
> [x, y]
=> [5, 3]
> x = y + y = x
=> 8
> [x, y]
=> [8, 5]

なぜかというと、まず

x = y + y = x

の y = x が実行されてその代入式の値はxになるから

x = y + x

ところで、これはCでも動くと思って試してみたがしたのようなエラーになってしまう。

error: lvalue required as left operand of assignment

これはおそらく+の結合強度の方が=の結合強度より高いから

x = (y + y) = x

と解釈されて、 y + y は左辺値ではないので代入の左辺にこれないってことなのだが、じゃあ、と

x = y + (y = x);

にしてみたら、これは x = x + x になってしまった。ふむ、+の引数って右辺から評価されるの?そう思って関数呼び出しに変えてみたら左辺が先に評価された

#include<iostream>
int y = 5;
int get_y(){
  std::cout << "get_y: " << y << std::endl;
  return y;
}

int main(){
  int x = 3;
  x = get_y() + (y = x);
  std::cout << x << "," << y << std::endl;
}

get_y()の時は8,3になり、そこをyに置き換えると6,3になる。C++ 演算子の優先順位 [C++ Reference]によれば式の評価順序は未定義な物が多いとのこと。要するに代入という「副作用のある処理」を式の中に混ぜると評価順序によって異なる結果になるからやめようね、ってことね。

ちなみにPythonでは

x = y = 1

という「= Expr が1個以上続いたもの」を「代入文」として定義しているので上のようなことがそもそもできない。下のように書く。

x, y = y, x + y



ささださんのTweetに反応して書いたのだけども、ささださん曰く出典はこれらしい: http://jarp.does.notwork.org/diary/201009a.html#201009101