class Expr
def initialize(left, op, right)
@left = left
@op = op
@right = right
end
def to_s
s = "("
s += @left.to_s
s += @op
s += @right.to_s
s + ")"
end
def to_f
left = @left.to_f
right = @right.to_f
if @op == "+" then
left + right
elsif @op == "-" then
left - right
elsif @op == "*" then
left * right
elsif @op == "/" then
left / right
else
0
end
end
end
Expr.new("x","+",Expr.new("y","-",Expr.new("x","*",Expr.new("x","*","x"))))
のように表すことができる.これは,x+(y-x*x*x)という式を表している.変数
に具体的な値を代入して値を計算するメソッドevaluateを定義してみることにする.
このメソッドevaluate は式を表す引数 exp と,変数と具体的な値との対応を示す引数 env の二つを取るものとする.実行例は,以下のようになる.
>> e1=Expr.new("x","+",Expr.new("y","-",Expr.new("x","*",Expr.new("x","*","x"))))
=> #, @op="*", @left="x">, @op="-", @left="y">, @op="+", @left="x">
>> evaluate(e1,{"x"=>3,"y"=>4})
evaluate(e1,{"x"=>3,"y"=>4})
=> -20 ### 3+4-3*3*3
>> evaluate(e1,{"x"=>1,"y"=>10})
=> 10 ### 1+10-1*1*1
>> evaluate("x",{"x"=>3})
=> 3
>> evaluate(30,{"x"=>3})
=> 30
このメソッドの設計方針としては,数値(Numeric)と文字列(String)はメソッド内で
処理して,式(Expr)の場合はExprクラスのインスタンスメソッドevalで扱うことに
する.
Exprクラスに定義するメソッドevalの穴を埋めてプログラム(expr1.rb)を完成させなさい.
# 式expを環境(変数をキーとしてその変数の値を得るハッシュ)envのもとで
# 評価した値を得る
def evaluate(exp,env)
# 数値の時は,値を浮動小数点数に変換する
if exp.kind_of?(Numeric)
exp.to_f
# 変数の時は,ハッシュenvの中から変数の値を得て浮動小数点数に変換する
elsif exp.kind_of?(String)
env[exp].to_f
# クラスExprのインスタンスの時は,クラスExprのインスタンスメソッド evalを呼ぶ
elsif exp.kind_of?(Expr)
exp.eval(env)
end
end
class Expr
def eval(env)
# 式 @left を環境 envの元で評価した値をローカル変数leftに代入する
left = ### (1) ###
# 式 @right を環境 envの元で評価した値をローカル変数rightに代入する
right = ### (2) ###
if @op == "+" then
left + right
elsif @op == "-" then
left - right
elsif @op == "*" then
left * right
elsif @op == "/" then
left / right
else
0
end
end
end
これが完成すれば,
>> load "./expr.rb"
=> true
>> load "./expr1.rb"
=> true
>> e1=Expr.new("x","+",Expr.new("y","-",Expr.new("x","*",Expr.new("x","*","x"))))
=> #, @op="*", @left="x">, @op="-", @left="y">, @op="+", @left="x">
>> evaluate(e1,{"x"=>3,"y"=>4})
evaluate(e1,{"x"=>3,"y"=>4})
=> -20
>> evaluate(e1,{"x"=>1,"y"=>10})
=> 10
>> evaluate("x",{"x"=>3})
=> 3
>> evaluate(30,{"x"=>3})
=> 30
のような実行例になるはずである.
上のプログラムはエラー処理をきちんと行っていないので,すべての変数の値が定まっていない場合などは問題が出ることがある.
# 式exp を 変数varで微分した結果の式を返す
# 結果の式はどんなにひどい形でも構わない
def d(exp,var)
# dx/dxは1
if exp == var
1.0
# クラスExprのインスタンスの場合はインスタンスメソッドexp_dを呼ぶ
elsif exp.kind_of?(Expr)
exp.exp_d(var)
else
# 定数を微分すると0.0, エラーになる場合も無視して0とする
0.0
end
end
class Expr
def exp_d(var)
######
end
end
上のプログラムの穴を埋めて,
evaluate(d(Expr.new("x","/",Expr.new("x","+",3.0)),"x"),{"x"=>3.0})
=> 0.0833333333333333 # d(x/(x+3))/dx= 3/(x+3)**2, 3/6**2
evaluate(d(Expr.new("x","/",Expr.new("x","+",3.0)),"x"),{"x"=>5.0})
=> 0.046875 ## 3/8**2
evaluate(d(Expr.new("x","-",Expr.new(Expr.new("x","*","x"),"*","x")),"x"),{"x"=>2.0})
=> -11.0 # d(x-x**3)/dx = 1-3*x**2, 1-3*2**2=-11
evaluate(d(Expr.new("x","-",Expr.new(Expr.new("x","*","x"),"*","x")),"x"),{"x"=>0.0})
=> 1.0 # 1-3*0**2=1
となるようにプログラムを完成させなさい.
上のプログラムはエラー処理をきちんと行っていないので,すべての変数の値が定まっていない場合などは問題が出ることがある.
## 実行例
e1=Expr.new("x","+",Expr.new(nil,"log","x"))
=> #, @op="+", @left="x">
evaluate(e1,{"x"=>1})
=> 1.0
## 実行例
e1=parse("3*x+y-z")
=> #>>
## 実行例
simplify(parse("(1+(0-((1*(x*x))+(x*((1*x)+(x*1))))))"))
=> #, @op="+", @left=1>
class Expr ----- class NumberExpr # 数(整数,実数)
|
+-- class VarExpr # 変数
|
+-- class AddExpr # ??? + ???
|
+-- class SubExpr # ??? - ???
|
+-- class MulExpr # ??? * ???
|
+-- class DivExpr # ??? / ???
|
+-- class LogExpr # log(???)
|
.....
このようにクラス階層を変更するようにプログラムを書き換えて動くことを確かめなさい.
講義の感想も100字以上1000字以下で加えること.
期限内なら何度でも再提出が可能である.1/9の夜23:55までに提出したものは8割以上を満点(8割以上は同じ),それ以降講義期間内(試験前)に提出したものは,10割を満点として採点する.もっとも点数の高いものを評価に用いる.