2006年2月25日土曜日

ruby電卓



しのさんの作ったirc電卓モジュールの単純なフロトエンド



#!/usr/local/bin/ruby
load "/usr/local/bin/Calculator.rb"
require "readline"
while $_=Readline.readline("> ", true)

bunsu=Calc.calculate( $_ )

if( bunsu)
syosu=bunsu.to_f
end
print bunsu," (",syosu,")\n"
end

んでもってしのさんが作ったモジュール.ぐぐってもでてこないので。。。張り付け



#----------------------------------------------------------------------
#
# Calculator.rb
# 電卓 ボットに電卓機能を!
#
# Programmed by SHINOHARA Takayuki (shino)
#
# 2003/06/22 shino 複素数リテラル('1+4i'のような)に対応
# 2003/06/15 shino 複素数(Complex)に対応
# 2003/06/12 shino いろいろ拡張
# 2003/05/25 shino いろいろ拡張
# 2003/05/10 shino 作成
#
# このソフトウェアはPublic Domain Softwareです。
# 自由に利用・改変して構いません。
# 改変の有無にかかわらず、自由に再配布することが出来ます。
# 作者はこのソフトウェアに関して、全ての権利と全ての義務を放棄します。
#
#----------------------------------------------------------------------
require 'rational'
require 'complex'

class Calc; INFO = false; end

# Ruby 1.6 には asin, acos, atan がないので
def Math.asin x; atan2 x,sqrt(1-x*x) end
def Math.acos x; atan2 sqrt(1-x*x),x end
def Math.atan x; atan2 x, 1 end

class Calc
# 型をなるべく簡単なものへ還元
# Complex --> Float/Rational/Integer
# Rational --> Integer
# Floatは有限精度なので、Integerへの変換はしない
def self.reduce_num( num )
if num.kind_of?(Complex) && num.image == 0
num = num.real
end
if num.kind_of?(Rational)
num = Rational.reduce( num.numerator, num.denominator )
if num.denominator == 1
num = num.to_i
end
end
return num
end
end

# デフォルトの Rational ** Rational は無条件にFloatで計算してしまうので、
# 10.to_r**100.to_r のような整数演算が精度良くできない。
# そこで右の値が整数の時は Rational ** Integer を呼び出すように再定義する。
class Rational
alias r_power **;
def ** (other)
self.r_power( Calc.reduce_num( other ) )
end
end
# Complexも同様
class Complex
alias c_power **;
def ** (other)
self.c_power( Calc.reduce_num( other ) )
end
end

# RationalとComplexには、
# Rational(123) == Complex(0,123) になってしまう問題があるので、
# == を再定義する。
class Rational
alias r_eq ==;
def == (other)
if other.kind_of?(Complex)
other == self
else
self.r_eq( other )
end
end
end
class Calc
public
# 定数
Infinity = 1.0/0
Inf = 1.0/0
NaN = 0.0/0
Pi = Math::PI
I = Complex::I
def self.infinity; Inf; end
def self.inf; Inf; end
def self.nan; NaN; end
def self.pi; Math::PI; end
def self.e; Math::E; end
def self.i; Complex::I; end

# 関数
def self.ln x; Math::log x; end
def self.sgn x; (x>0) ? 1 : (x<0) ? -1 : 0; end
def self.abs x; (x<0) ? -x : x; end

#
RS_const_Math = '(\b(PI|E)\b)'
RS_const_Calc = '(\b(Inf(inity)?|inf(inity)?|NaN|nan|Pi|pi|e|I|i)\b)'
RS_const_SP = '(\b(π|無限大|∞)\b)'
RS_func_Math = '(\b(sin|asin|cos|acos|tan|atan|exp|log|log10|sqrt)\b)'
RS_func_Calc = '(\b(ln|sgn|abs)\b)'
RS_numsuffix = '(I|i|PI|Pi|pi|π)'
RS_num = '(0x[0-9a-fA-F]+|(0|[1-9][0-9]*)(\.[0-9]+)?|\.[0-9]+)'
# RS_num = '(\b(0|[1-9][0-9]*)(\.[0-9]+)?\b|\.[0-9]+\b)'
RS_num2 = '('+RS_num+RS_numsuffix+'?)'
RS_binop = '(\+|\-|\*{1,2}|\/|\%|\^)'

# 値
RS_const = '(' + RS_const_Math +
'|' + RS_const_Calc +
'|' + RS_const_SP + ')'
RS_val = '(' + RS_num2 +
'|' + RS_const +
')'

RS_lparen = '('+ "(#{RS_func_Math}|#{RS_func_Calc})" + '?\()'

RS_binopex = '(\)*' + RS_binop + RS_lparen + '*)' #括弧付き

# 多項式
RS_expr1 = '(' \
+ '([\-\+]*' + RS_lparen + ')*' \
+ '([\-\+]*'+RS_val+'\s*'+RS_binopex+'\s*)+' \
+ '[\-\+]*' + RS_val \
+ '\)*' \
+ ')'
# 括弧または関数で始まって中身が単項式
RS_expr2 = '(' \
+ '[\-\+]*' + RS_lparen + '+' \
+ '[\-\+]*' + RS_val \
+ '\)+' \
+ ')'

# 正規表現
RE_Expression1 = Regexp.new( RS_expr1 )
RE_Expression2 = Regexp.new( RS_expr2 )

# 計算
def Calc.calculate( expr )
begin
expr = String.new( expr )

# 式をRubyに変換する
expr.gsub!( /\^/, '**' )
expr.gsub!( Regexp.new(RS_const_SP), 'Calc::const(\'\1\')' )
expr.gsub!( Regexp.new(RS_num2), 'Calc::literal(\'\1\')' )
expr.gsub!( Regexp.new(RS_const_Math), '(Math::\1)' )
expr.gsub!( Regexp.new(RS_func_Math), 'Math::\1' )
expr.gsub!( Regexp.new(RS_const_Calc), '(Calc::\1)' )
expr.gsub!( Regexp.new(RS_func_Calc), 'Calc::\1' )

STDERR.puts '==> ' + expr if INFO

# 評価
answer = do_with_safelevel( 4 ) { eval( expr ) }

answer = reduce_num( answer ) if answer

return answer

rescue => ex
STDERR.puts 'Exception:' + ex.message if INFO
return nil
rescue ScriptError => ex
STDERR.puts 'ScriptError:' + ex.message if INFO
return nil
end
end

def self.literal( str )
case str
when /(π|pi)$/i; return literal( Regexp.last_match.pre_match )*Pi
when /i$/i; return literal( Regexp.last_match.pre_match )*I
when /^0x/; return str.hex
when /\./; return str.to_f
else; return Rational( str.to_i, 1 )
end
end

def self.const( str )
case str
when 'π'; return Calc::Pi
when '∞','無限大'; return Calc::Inf
else; return Calc::NaN
end
end

def self.do_with_safelevel( level )
result = nil
Thread.start {
$SAFE = level
result = yield
}.join
result
end
end





0 件のコメント:

コメントを投稿