平面上の線分
今日は、SICPの問題2.2を解いてみます。
問題は、線分を表現するデータ構造を作り、その線分の中間点を求めよという問題。データ構造は、x座標と、y座標を表すデータ構造を作って、ポイントを表すデータ構造を作れとなっているんだけど、そこは割愛。そのかわり、線分の長さを求める関数も作ることにします。
で、線分と座標を表すデータ構造は、このような感じ。
data Point = Point Integer Integer instance Show Point where show (Point x y ) = "( "++show(x)++" , " ++ show(y)++" )" data Line = Line Point Point instance Show Line where show (Line s e) = show s ++ " - " ++ show e makeLine:: Integer->Integer->Integer->Integer->Line makeLine x y x' y' = Line (Point x y) (Point x' y')
毎回、Lineを作るのに、Pointとかうつのが面倒臭そうなので、makeLineなる関数も定義 。
中間点と、長さをあらわす関数は、こちら。
getMiddlePoint::Line->Point getMiddlePoint (Line (Point x y) (Point x' y')) = Point ( calMiddle x x' ) (calMiddle y y') calMiddle::Integer->Integer->Integer calMiddle x x' = quot (x+x') 2 getLength::Line->Float getLength (Line (Point x y) (Point x' y')) = sqrt ((func x x') + (func y y')) where func x x' = (x'-x) ^ 2
関数funcは、自分のボキャブラリーから適切な名前が浮かばなかったので、こんな名前に。では、実行してみます。
$ ghci calline.hs calline.hs:22:10: No instance for (Floating Integer) arising from use of `sqrt' at calline.hs:22:10-13 Probable fix: add an instance declaration for (Floating Integer) In the definition of `getLength': getLength (Line (Point x y) (Point x' y')) = sqrt ((func x x') + (func y y')) Failed, modules loaded: none.
どうやら、(func x x') + (func y y')の返す値の型と、sqrtの引数の型があわないよう です。
(func x x') + (func y y') は、Integerだとおもわれます。sqrtの型は、
Prelude> :t sqrt sqrt :: (Floating a) => a -> a
Float型みたいです。どうやら、IntegerはFloatには、勝手にならないようです。
ということで、ここではfromInteger関数を使ってみることにします。
Prelude> :t fromInteger fromInteger :: (Num a) => Integer -> a
fromIntegerは、Integer型の値をNum型に直してくれる関数です。
Num型は、数値の規定クラスです。Javaをやっている人には、数値のなかでのObjectクラ スとでも言えば、わかりやすいでしょうか?rubyでいうとこのNumericです。
なので、Floatは、Numであるともいえるので、この関数を適用することで、今回の問題を解決できます。
以下に、完全なソースを
data Point = Point Integer Integer instance Show Point where show (Point x y ) = "( "++show(x)++" , " ++ show(y)++" )" data Line = Line Point Point instance Show Line where show (Line s e) = show s ++ " - " ++ show e makeLine:: Integer->Integer->Integer->Integer->Line makeLine x y x' y' = Line (Point x y) (Point x' y') getMiddlePoint::Line->Point getMiddlePoint (Line (Point x y) (Point x' y')) = Point ( calMiddle x x' ) (calMiddle y y') calMiddle::Integer->Integer->Integer calMiddle x x' = quot (x+x') 2 getLength::Line->Float getLength (Line (Point x y) (Point x' y')) = sqrt $ fromInteger ((func x x') + (func y y')) where func x x' = (x'-x) ^ 2
では、実行結果です。
$ ghci calline.hs Loading package base-1.0 ... linking ... done. Compiling Main ( calline.hs, interpreted ) Ok, modules loaded: Main. *Main> makeLine 2 4 2 12 ( 2 , 4 ) - ( 2 , 12 ) *Main> let linea = makeLine 2 4 2 12 *Main> getMiddlePoint linea ( 2 , 8 ) *Main> getLength linea 8.0
途中ででてくる、letは、変数に値を束縛することができます。なので、lineaは、makeLine 2 4 2 12の値に束縛されることになります。
追記:id:nobsunさんから指摘。letは関数ではないとのこと。ふつけるを読んで、letが式だったことを確認。すみませんでした。