Rubyおもしろい

Rubyは括弧をつけなくてもメソッドを呼び出せます。メソッド名は普通は英小文字で始まります。ローカル変数も英小文字で始まります。

こんなRubyプログラムを実行すると(ifの条件部で代入しているのはtypoではありません)、

def hoge
  123
end
p hoge
x = 456
if hoge = x
  p hoge
end
p hoge

こういう出力が得られます。

123
456
456

途中でhogeがメソッド呼び出しから変数参照に変わったのがわかります。 変数宣言後は括弧「()」をつけない場合はメソッド呼び出しではなく変数参照になります。

def hoge
  123
end
p hoge         # hogeメソッド呼び出しなので123
x = 456
if hoge = x    # hogeにx(456)を代入。その結果は真なのでif内を実行
  p hoge       # ここでのhogeはローカル変数参照なので456
end
p hoge         # ここもローカル変数参照なので456

ところでRubyはif内が1行なら後置ifで記述できます。

if 条件
  式
end

if 条件

と記述できます。

さっきのプログラムのifを後置ifで書き直してみます。

def hoge
  123
end
p hoge
x = 456
p hoge if hoge = x
p hoge

実行すると

123
123
456

結果が変わった!

Rubyがプログラムをどのようにパースしているかを見てみます。

最初の例では :var_ref で変数hogeを参照していますが、

# ruby -rpp -rripper -e 'pp Ripper.sexp(File.read("a.rb"))'
[:program,
 [[:def,
   [:@ident, "hoge", [1, 4]],
   [:params, nil, nil, nil, nil, nil, nil, nil],
   [:bodystmt, [[:@int, "123", [2, 2]]], nil, nil, nil]],
  [:command,
   [:@ident, "p", [4, 0]],
   [:args_add_block, [[:vcall, [:@ident, "hoge", [4, 2]]]], false]],
  [:assign, [:var_field, [:@ident, "x", [5, 0]]], [:@int, "456", [5, 4]]],
  [:if,
   [:assign,
    [:var_field, [:@ident, "hoge", [6, 3]]],
    [:var_ref, [:@ident, "x", [6, 10]]]],
   [[:command,
     [:@ident, "p", [7, 2]],
★   [:args_add_block, [[:var_ref, [:@ident, "hoge", [7, 4]]]], false]]],
   nil],
  [:command,
   [:@ident, "p", [9, 0]],
   [:args_add_block, [[:var_ref, [:@ident, "hoge", [9, 2]]]], false]]]]

後置ifの場合は :vcall でメソッド呼び出しをしています。

# ruby -rpp -rripper -e 'pp Ripper.sexp(File.read("b.rb"))'
[:program,
 [[:def,
   [:@ident, "hoge", [1, 4]],
   [:params, nil, nil, nil, nil, nil, nil, nil],
   [:bodystmt, [[:@int, "123", [2, 2]]], nil, nil, nil]],
  [:command,
   [:@ident, "p", [4, 0]],
   [:args_add_block, [[:vcall, [:@ident, "hoge", [4, 2]]]], false]],
  [:assign, [:var_field, [:@ident, "x", [5, 0]]], [:@int, "456", [5, 4]]],
  [:if_mod,
   [:assign,
    [:var_field, [:@ident, "hoge", [6, 10]]],
    [:var_ref, [:@ident, "x", [6, 17]]]],
   [:command,
    [:@ident, "p", [6, 0]],
★  [:args_add_block, [[:vcall, [:@ident, "hoge", [6, 2]]]], false]]],
  [:command,
   [:@ident, "p", [7, 0]],
   [:args_add_block, [[:var_ref, [:@ident, "hoge", [7, 2]]]], false]]]]

Rubyのパース処理はよく理解してませんけど、

p hoge if hoge = x

この行は実行時には if の条件が先に評価されるけど、パース時は左から処理するため、hoge変数はまだ宣言されてないとみなしhogeメソッドを呼ぶようにパースされる感じでしょうか。

Rubyおもしろい!