Rubyで電卓作ってみた。

前回のエントリコマンドライン電卓dcを試したけど、Rubyワンライナの方が断然便利だった。

% ruby -e 'STDIN.each{|l| puts eval(l)}'
1 + 2
3

残念ながらこのワンライナでは、

  • フォーマット指定が面倒
  • 変数が使えない

と、イマイチな所もあるので、書き直すことにした。

Ruby電卓rc

#!/usr/bin/ruby

require 'getoptlong'
include Math

opts = GetoptLong.new(
  ["--format", "-f", GetoptLong::REQUIRED_ARGUMENT],
  ["--octal",  "-o", GetoptLong::NO_ARGUMENT],
  ["--hex",    "-x", GetoptLong::NO_ARGUMENT],
  ["--binary", "-b", GetoptLong::NO_ARGUMENT],
  ["--silent", "-s", GetoptLong::NO_ARGUMENT]
)

format = ""
verbose = true

begin
  opts.each do |opt, arg|
    case opt
    when "--format":  format = arg
    when "--binary":  format = "%#b"
    when "--octal":   format = "%#o"
    when "--hex":     format = "%#x"
    when "--verbose": verbose = false
    end
  end
rescue => ex
  STDERR.puts "Usage: rc [format options ...]"
  exit 1;
end

bind = binding

STDIN.each do |line|
  begin
    res = eval(line, bind)
    if res.kind_of? Numeric
      if format.empty?
        puts res
      else
        printf "#{format}\n", res
      end
    else
      if verbose
        puts res
      end
    end
  rescue ScriptError, StandardError => err
    STDERR.puts err.message
  end
end

パスの通った所にrcで保存しておいて、実行〜

電卓として。

% rc
1 + 2
3
sin(PI / 2)
1.0
a = 10; b = 20
20
a + b
30
exit
  • evalしてるだけなので、大体Rubyな事は出来ると思います。
  • Mathをincludeしてるので、Math.sinとする必要なし。

パイプを通す。

対話的ならirb使った方が断然便利なので、パイプを通すのが本来の使い方デス。

% cat 123.txt
123
456
789
% cat 123.txt | rc -b
0b1111011
0b111001000
0b1100010101
% cat 123.txt | rc -x
0x7b
0x1c8
0x315
% cat 123.txt | rc -o
0173
0710
01425
% cat 123.txt | rc -f '%010b'
0001111011
0111001000
1100010101
  • fでsprintfのフォーマットが使えます。

メモとtodo

  • GetoptLongは案外曲者。
  • bind = binding, eval(line, bind)でeval環境を保存出来る。ピッケル本2nd Binding参照。
  • res.kind_of? NumericでNumericを継承しているのかがわかる。
  • silentはあんまりsilentにならない。
  • evalのエラー処理は、eval.rb を読むが参考になりました。irbも簡単に作れそうな・・・。

ピッケル本を熟読しないと・・・。