13. data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
Circle, Rectangle のフィールドを
Point 型で定義して整理
14. area :: Shape -> Float
area (Circle _ r) = pi * r ^ 2
area (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
パターンマッチも
若干すっきり (?)
15. nudge :: Shape -> Float -> Float -> Shape
nudge (Circle (Point x y) r) a b = Circle (Point (x + a) (y + b)) r
nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1 + a) (y1 + b))
(Point (x2 + a) (y2 + b))
!
baseCircle :: Float -> Shape
baseCircle r = Circle (Point 0 0) r
!
baseRect :: Float -> Float -> Shape
baseRect width height = Rectangle (Point 0 0) (Point width height)
baseCircle
baseRectangle
19. data Person = Person String String Int Float String String deriving (Show)
!
firstName :: Person -> String
firstName (Person firstname _ _ _ _ _) = firstname
!
lastName :: Person -> String
lastName (Person _ lastname _ _ _ _) = lastname
!
age :: Person -> Int
age (Person _ _ age _ _ _) = age
!
height :: Person -> Float
height (Person _ _ _ height _ _) = height
!
phoneNumber :: Person -> String
phoneNumber (Person _ _ _ _ number _) = number
!
flavor :: Person -> String
flavor (Person _ _ _ _ _ flavor) = flavor
20. data Person = Person { firstName :: String
, lastName :: String
, age :: Int
, height :: Float
, phoneNumber :: String
, flavor :: String } deriving (Show)
それぞれ、フィールド名とその
型名を指定する
ghci> :t firstName
firstName :: Person -> String 対応する関数が自動的
に作られる
21. data Car = Car { company :: String
, model :: String
, year :: Int } deriving (Show)
ghci> Car { company = "Ford", model = "Mustang", year = 1967}
Car {company = "Ford", model = "Mustang", year = 1967}
ghci> Car { company = "Ford", year = 1967, model = "Mustang"}
Car {company = "Ford", model = "Mustang", year = 1967}
フィールドを任意の順
番で指定できる
(型クラス Show を
derive した場合) 出力
が整形される
28. Carをパラメーター化すると?
data Car a b c = Car { company :: a
, model :: b
, year :: c } deriving (Show)
tellCar :: (Show a) => Car String String a -> String
tellCar (Car { company = c, model = m, year = y}) =
"This " ++ c ++ " " ++ m ++ " was made in " ++ show y
tellCar では year の型
しかパラメーター化さ
れてない
結局、ほとんどのケースで
Car String String Int 型を
使うことになりそう
31. 3次元ベクトルを表わす
Vector a 型 の場合
data Vector a = Vector a a a deriving (Show)
!
vplus :: (Num a) => Vector a -> Vector a -> Vector a
(Vector i j k) `vplus` (Vector l m n) = Vector (i + l) (j + m) (k + n)
!
dotProd :: (Num a) => Vector a -> Vector a -> a
(Vector i j k) `dotProd` (Vector l m n) = i * l + j * m + k * n
!
vmult :: (Num a) => Vector a -> a -> Vector a
(Vector i j k) `vmult` m = Vector (i * m) (j * m) (k * m)
型クラス制約は
付けない
関数の方に型クラ
ス制約を付ける
44. 型シノニムの型パラメーター化
type AssocList k v = [(k, v)]
type IntMap v = Map Int v
型パラメーターを持つ
型シノニムの定義
型パラメーターを
部分適用した型シノニム
AssocList, IntMap は
型コンストラクターになる
!
値コンストラクターと
混同しないよう注意
45. data Either a b = Left a | Right b
deriving (Eq, Ord, Read, Show)
• ある型か他のある型の値を表現
• Maybe a と同様に、処理の結果を表わすのに
よく使われる
• 失敗した場合もデータを保持できるのが違
い
47. import qualified Data.Map as Map
!
data LockerState = Taken | Free deriving (Show, Eq)
!
type Code = String
!
type LockerMap = Map.Map Int (LockerState, Code)
!
lockerLookup :: Int -> LockerMap -> Either String Code
lockerLookup lockerNumber map = case Map.lookup lockerNumber map of
Nothing -> Left $ "Locker " ++ show lockerNumber ++ " doesn't exist!"
Just (state, code) -> if state /= Taken
then Right code
else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"
!
lockers :: LockerMap
lockers = Map.fromList
[(100,(Taken, "ZD39I"))
,(101,(Free,"JAH3I"))
,(103,(Free,"IQSA9"))
,(105,(Free,"QOTSA"))
,(109,(Taken,"893JJ"))
,(110,(Taken,"99292"))]
48. 練習問題
• 上のような構造を持つ、URIを表現する型を作ってみましょう
• query は複数の key, value のペアを持ちます
• 必須の要素とオプショナルの要素があることに注意して下さい
https://user:password@example.com:80/path/somewhere?foo=bar#baz
scheme userinfo host port path query fragment
authority