Ruby

変数の名前と値を表示するメソッド

More than 3 years have passed since last update.


はじめに

よくrubyを電卓代わりにしているんですが、計算結果を確認するために次のようなコードをしょっちゅう書きます。

length = 100.0

time = 5.0
speed = length / time

puts "speed = #{speed}"

この一番最後の行、putsのところ、変数名が2回出てきてRepeat yourself感がしてとても嫌です。変数名に意味をもたせているのだから、それをそのまま表示してくれればいいのにーと思います。

ちょっと電卓叩いて結果表示するだけの目的なのだから、もっとタイプ数減らして楽したいです。

なのでDon't repeat yourselfなメソッドを書きました。ついでにgemにしました。


インストール&使い方

$ gem install print_variable

でインストールできます。

こんな感じに使います。表示したい変数のSymbolを返すブロックを渡します。

Symbolの配列にすれば複数可です。その場合、一番長い変数名に合わせて変数名を左詰めで表示します。

require 'pv'

var = 1
var2, var_foo, v = 2, 3, 4

pv{:var} #=> var = 1
pv{%i(var2 var_foo v)} #=> var2 = 2
# var_foo = 3
# v = 4


せつめい

変数とその値を表示するだけなんだから、変数のSymbolでも渡してやれば簡単にできる!そう思っていた時期がありました。


pv1.rb

def pv(symbol)

puts "#{symbol} = #{eval(symbol.to_s)}"
end

var = 1
pv :var
#=> undefined local variable or method `var' for main:Object


pvメソッドのスコープの中ではvarが定義されていないので、エラーになりました。

つまりeval(symbol.to_s)は、メソッドを呼び出した場所のスコープで実行する必要があります。スコープの情報を渡すときに使うものといえば、そうKernel#bindingです。


pv2.rb

def pv(symbol, bind)

puts "#{symbol} = #{bind.eval(symbol.to_s)}"
end

var = 1
pv :var, binding
#=> var = 1


期待通りに動きました。でもいちいちbindingとか渡してやるのはクソ面倒です。

なんかもっとこう、書きやすく出来ないものか考えました。

…そういえば、rubyのブロック付きメソッドを呼び出す時、ブロックのスコープは呼び出した場所になっていたような?


pv3.rb

def pv(symbol, &block)

bind = block.binding
puts "#{symbol} = #{bind.eval(symbol.to_s)}"
end

var = 1
pv(:var){}
#=> var = 1


期待通りです!タイプ数が少し減りました!黒魔術感が増しました!

もっと括弧を減らしていきましょう。


pv4.rb

def pv(&block)

symbol = block.call
bind = block.binding
puts "#{symbol} = #{bind.eval(symbol.to_s)}"
end

var = 1
pv{:var}
#=> var = 1


だいぶ楽になったような気がします。

これに例外処理や、Symbolを複数渡せるように少し弄ったものがgemになっています。


print_variableと言いつつ、定数やメソッド名も表示できます。

CONST = 2

pv{:CONST} #=> CONST = 2
def mthd; 3 end
pv{:mthd} #=> mthd = 3


おわりに

これ以上タイプ数減らす方法あったらコメントください。


(2014/12/05追記)


binding_of_caller

@pasela さまに教えていただきました!ありがとうございます!

binding_of_callerというgemを使うともっと簡単に出来ました。


pv5.rb

require 'binding_of_caller'

def pv(symbol)
puts "#{symbol} = #{binding.of_caller(1).eval(symbol.to_s)}"
end

var = 1
pv :var
#=> var = 1


READMEを見ると


Recommended for use only in debugging situations. Do not use this in production apps.

Only works in MRI Ruby 1.9.2, 1.9.3, 2.0, 2.1 and RBX (Rubinius)


とかなんか怖いこと書いてありますけど気にしない(゚ε゚)

怖いのが嫌な場合は、私の作ったgemを使ってくださるといいと思います。