haskell.vim
おひさしぶりです。最近忙しくてHaskellを書けていないんですが、こんなものをみつけました。
cons,car,cdr
今日は、問題2.4をといてみます。
問題2.4は、lispのconsと、carを
(define (cons x y) (lambda (m) (m x y))) (define (car z) (x (lambda (p q) p)))
と定義されているときのcdrはどう定義されるかと問題。
ということで、cons,car,cdrのこの定義で実装
cons x y = \x' -> x' x y car x = x ( \x' y' -> x') cdr x = x ( \x' y' -> y')
で、実行。
$ ghci cons.hs *Main> :l cons.hs Compiling Main ( condcons.hs, interpreted ) Ok, modules loaded: Main. *Main> :t cons cons :: t -> t1 -> (t -> t1 -> t2) -> t2 *Main> :t car car :: ((t1 -> t2 -> t1) -> t) -> t *Main> :t cdr cdr :: ((t1 -> t2 -> t2) -> t) -> t *Main> let test = cons 8 10 *Main> car test 8 *Main> cdr test 10
うまく動いてるようです
長方形を表現
SICPの問題2.3。長方形を表し、周囲の長さと面積を計算する手続きを作る。
今回は、問題2.2とほぼ同じなので、以下にソースと実行結果を示します。
$cat rectangle.hs data Point = Point Integer Integer instance Show Point where show (Point x y ) = "( "++show(x)++" , " ++ show(y)++" )" data Rectangle = Rectangle Point Point instance Show Rectangle where show (Rectangle (Point x y) (Point x' y')) = show (Point x y) ++ " - " ++ show (Point x' y) ++ "\n" ++ show (Point x y') ++ " - " ++ show (Point x' y') makeRectangle:: Integer->Integer->Integer->Integer->Rectangle makeRectangle x y x' y' = Rectangle (Point x y) (Point x' y') getCircuit::Rectangle->Integer getCircuit (Rectangle (Point x y) (Point x' y')) = double $( getLength x x') + (getLength y y') where double = (*) 2 getDimension (Rectangle (Point x y) (Point x' y')) = (*) (getLength x x') (getLength y y') getLength::Integer->Integer->Integer getLength x x' = abs ( x' -x )
$ghci rectangle.hs Loading package base-1.0 ... linking ... done. Compiling Main ( rectangle.hs, interpreted ) Ok, modules loaded: Main. *Main> makeRectangle 3 5 6 1 ( 3 , 5 ) - ( 6 , 5 ) ( 3 , 1 ) - ( 6 , 1 ) *Main> let rect = makeRectangle 3 5 6 1 *Main> getCircuit rect 14 *Main> getDimension rect 12
平面上の線分
今日は、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が式だったことを確認。すみませんでした。
使える文字
前回、関数の名前で定義できない文字を使いエラーを出していたので、名前に使える文字をまとめてみます。
まず、関数名。
1文字目は、アルファベットの小文字か、アンダースコア。
それ以降は、アルファベットの大文字、小文字。数字。アンダースコア。シングルクォート。
正規表現で書くとこんな感じかな?
funcName = [a-z_][a-zA-Z0-9_']*
また、Haskellでは、アンダースコアから始まる関数名は、あまり良しとされていないようです。
次に、データコンストラクタ、型コンストラクタ、クラス名
これらは、関数名と違いアルファベットの大文字から始めなくてはなりません。また、クラス名は、データコンストラクタとは、かぶってはいけないようです。
話はかわりますが、マージンFXのひまわり証券さん、ニンテンドーDS Lite欲しい!と、いってもいいですか?
有理数の計算
前回の記事へ、id:nobusunさんからツッコミをいただいて、原因判明。Haskellの変数名は、小文字英数から始めないといけなかったんですね。で、id:nobusunさんが示していただいた、ソースに変更。
$ cat yuuri.hs data Yuuri = Yu Integer Integer yAdd::Yuuri -> Yuuri -> Yuuri yAdd (Yu s b ) (Yu s' b') = Yu (s*b'+s'*b) (b*b')
変数が短く、シングルクォートも使っているところが、いかにもHaskellっぽいです。で、実行してみます。
$ ghci yuuri.hs Loading package base-1.0 ... linking ... done. Compiling Main ( yuuri.hs, interpreted ) Ok, modules loaded: Main. *Main> yAdd (Yu 2 3) (Yu 3 2) Top level: No instance for (Show Yuuri) arising from use of `print' at Top level Probable fix: add an instance declaration for (Show Yuuri) In a 'do' expression: print it
ロードは、成功したけど、新たに問題が。エラーメッセージをみるとYuuriはShowのインスタンスでないから、表示させてあげないってことみたい。ということで、
$ cat yuuri.hs data Yuuri = Yu Integer Integer deriving Show yAdd::Yuuri -> Yuuri -> Yuuri yAdd (Yu s b ) (Yu s' b') = Yu (s*b'+s'*b) (b*b') $ ghci yuuri.hs Loading package base-1.0 ... linking ... done. Compiling Main ( yuuri.hs, interpreted ) Ok, modules loaded: Main. *Main> yAdd (Yu 2 3 ) (Yu 3 2) Yu 13 6
dataが苦手
今日は、有理数の計算(足し算)をしようとして、
data Yuuri = Yu Integer Integer YAdd::Yuuri -> Yuuri -> Yuuri YAdd (Yu s1 b1 ) (Yu s2 b2) = Yu (s1*b2+s2*b1) (b1*b2)
というプログラムを書いて、ghciで実行しようとしたのですが、
$ ghci yuuri.hs Loading package base-1.0 ... linking ... done. Compiling Main ( yuuri.hs, interpreted ) yuuri.hs:3:0: Not in scope: data constructor `YAdd' yuuri.hs:4:0: Not in scope: data constructor `YAdd' Failed, modules loaded: none. Prelude>
うーん。