01

About

Exercise 3

次のような計算をするメソッドを作って動かす。

Exercise 6

その他、コンピュータの実数計算が数学と違っている具体例を示すようなプログラムを好きなように探究してみてください。

Program, execution and description

Exercise 3

コードは以下のようになります。(演習 6 で作成した Sym class を事前に定義する必要があります。)

# a. 2 つの実数を与え、和を返す (差、商、積も)。 def add(x, y) x + y end def sub(x, y) x - y end def mul(x, y) x * y end def div(x, y) x / y end # b. 剰余 (remainder) を求める演算 def dec(x, y) x % y end # c. 数値 x を与え、逆数 $1 \over x$ を出力する def inv(x) 1.0 / x end # d. 数値 x を与え、その 8 乗を返す def oct(x) x ** 2 ** 2 ** 2 end # e. 円錐の底面の半径と高さを与え、体積を返す。 def cone(r, h) r ** 2 * Math::PI * h / 3.0 end # f. 実数 x を与え、x の平方根 def sqrt(x) x ** 0.5 end # g その他、自分が面白いと思う計算を行うメソッドを作って動かせ。 x, y = (0..1).map do Sym.new end # a ~ f 課題の記号計算 a0 = add(x, y) a1 = sub(x, y) a2 = mul(x, y) a3 = div(x, y) b = dec(x, y) d = oct(x) e = cone(x, y) f = sqrt(x) puts " i | j | i+j | i-j | i*j | i/j | i%j | i^8 | core | sqrt " puts ":- |:- | :-- | :-- | :-- | :-- | :-- | :-- | :--- | :---" (1..5).each do |i| (1..5).each do |j| x <=> i y <=> j puts "#{i} | #{j} | #{a0} | #{a1} | #{a2} | #{a3} | #{b} | #{d} | #{e} | #{f}" end end

Exercise 6

Sym は代数計算をするために各演算を override している class です。 数学のように変数から式をつくり、後から数字を代入できるようになります。

仕組みとしてはまず、Sym class の演算子を override をすることで、 演算毎におこりうる処理を lambda 関数として継承させつつ、 @_ で新しく Sym object を生成して return させます。 つぎに Sym object に x <=> 1.0 などで数値を代入したあと to_f が呼ばれたときに、再帰的に全ての処理を実行できます。

class Sym def initialize(init = 0.0) @v = init @_ = ->fun{Sym.new(fun)} end def to_f() (@v.is_a? Numeric) ? @v: @v[] end def to_i() self.to_f.to_i end def to_s() self.to_f.to_s end def +(_) @_[->{self.to_f + _.to_f}] end def -(_) @_[->{self.to_f - _.to_f}] end def *(_) @_[->{self.to_f * _.to_f}] end def /(_) @_[->{self.to_f / _.to_f}] end def %(_) @_[->{self.to_f % _.to_f}] end def **(_) @_[->{self.to_f ** _.to_f}] end def ==(_) self.to_f == _.to_f end def <=>(_) @v = _.to_f end end x, y = (0..1).map do Sym.new end z = (x ** 2 + y ** 2) x <=> 3 y <=> 4 p "expect #{z} to be 25.0" x <=> 7 y <=> 24 p "expect #{z} to be 625.0"

About Task

Exercise 3 - g

実行結果は以下のようになります。 (演習 3 - c の逆数についてですが、演算の左側のオペランドが Sym objectではなく 1.0 なので 同様にテストすることが出来ませんでしたが、_1 = Sym(1.0) を用いると同様に実行できます。)

i j i+j i-j i*j i/j i%j i^8 core sqrt
1 1 2.0 0.0 1.0 1.0 0.0 1.0 1.0471975511965976 1.0
1 2 3.0 -1.0 2.0 0.5 1.0 1.0 2.0943951023931953 1.0
1 3 4.0 -2.0 3.0 0.3333333333333333 1.0 1.0 3.141592653589793 1.0
1 4 5.0 -3.0 4.0 0.25 1.0 1.0 4.1887902047863905 1.0
1 5 6.0 -4.0 5.0 0.2 1.0 1.0 5.235987755982989 1.0
2 1 3.0 1.0 2.0 2.0 0.0 65536.0 4.1887902047863905 1.4142135623730951
2 2 4.0 0.0 4.0 1.0 0.0 65536.0 8.377580409572781 1.4142135623730951
2 3 5.0 -1.0 6.0 0.6666666666666666 2.0 65536.0 12.566370614359172 1.4142135623730951
2 4 6.0 -2.0 8.0 0.5 2.0 65536.0 16.755160819145562 1.4142135623730951
2 5 7.0 -3.0 10.0 0.4 2.0 65536.0 20.943951023931955 1.4142135623730951
3 1 4.0 2.0 3.0 3.0 0.0 43046721.0 9.42477796076938 1.7320508075688772
3 2 5.0 1.0 6.0 1.5 1.0 43046721.0 18.84955592153876 1.7320508075688772
3 3 6.0 0.0 9.0 1.0 0.0 43046721.0 28.274333882308138 1.7320508075688772
3 4 7.0 -1.0 12.0 0.75 3.0 43046721.0 37.69911184307752 1.7320508075688772
3 5 8.0 -2.0 15.0 0.6 3.0 43046721.0 47.1238898038469 1.7320508075688772
4 1 5.0 3.0 4.0 4.0 0.0 4294967296.0 16.755160819145562 2.0
4 2 6.0 2.0 8.0 2.0 0.0 4294967296.0 33.510321638291124 2.0
4 3 7.0 1.0 12.0 1.3333333333333333 1.0 4294967296.0 50.26548245743669 2.0
4 4 8.0 0.0 16.0 1.0 0.0 4294967296.0 67.02064327658225 2.0
4 5 9.0 -1.0 20.0 0.8 4.0 4294967296.0 83.77580409572782 2.0
5 1 6.0 4.0 5.0 5.0 0.0 152587890625.0 26.179938779914945 2.23606797749979
5 2 7.0 3.0 10.0 2.5 1.0 152587890625.0 52.35987755982989 2.23606797749979
5 3 8.0 2.0 15.0 1.6666666666666667 2.0 152587890625.0 78.53981633974483 2.23606797749979
5 4 9.0 1.0 20.0 1.25 1.0 152587890625.0 104.71975511965978 2.23606797749979
5 5 10.0 0.0 25.0 1.0 0.0 152587890625.0 130.89969389957471 2.23606797749979

Consideration

a + b - c * d / e のような演算を図にすると以下のようになります。 ab = a + b に対して、 a, b を parent、ab を childとしたとき、 parentでする処理を各演算子 override で child に継承させることで、 最後の childが to_f を実行すると、全ての親の to_fが再帰的に実行させます。

ab = a + b # add(a, b) cd = c * d # mul(cd) cde = cd / e # div(cd, e) = c * d / e # div(mul(c, d), e) ∵ cd abcde = ab - cde # sub(ab, cde) = a + b - c * d / e # sub(add(a, b), div(mul(c, d), e)) ∵ ab, cde

img

flowchart LR a --> add[a + b] b --> add[a + b] c --> mul[c * d] d --> mul[c * d] mul --> div[c * d / e] e --> div[c * d / e] add --> sub[a + b - c * d / e] div --> sub[a + b - c * d / e]