300 likes | 539 Views
深入浅出 Haskell. 第 3 讲. Shape 定义回顾. data Shape = Rectangle Side Side | Ellipse Radius Radius | RtTriangle Side Side | Polygon [ Vertex ] deriving Show type Vertex = (Float,Float) type Side = Float type Radius = Float. Shape. Graphic.
E N D
深入浅出Haskell 第3讲
Shape定义回顾 data Shape = Rectangle Side Side | Ellipse Radius Radius | RtTriangle Side Side | Polygon [ Vertex ] deriving Show type Vertex = (Float,Float) type Side = Float type Radius = Float
Shape Graphic (0,1) Graphic坐标系统 度量:Int 单位:pixel (0,0) (200,0) • 度量:Float • 单位:inch Shape坐标系统 (-1,0) (0,0) (1,0) (200,200)pixels or (1,-1)inches (0,200) (0,-1)
单位转换 inchToPixel :: Float -> Int inchToPixel x = round (100*x) pixelToInch :: Int -> Float pixelToInch n = intToFloat n / 100 intToFloat :: Int -> Float intToFloat n = fromInteger (toInteger n)
坐标变换 xWin, yWin :: Int xWin = 600 yWin = 500 xWin2, yWin2 :: Int xWin2 = xWin `div` 2 yWin2 = yWin `div` 2 trans :: Vertex -> Point trans (x,y) = ( xWin2 + inchToPixel x, yWin2 - inchToPixel y ) transList :: [Vertex] -> [Point] transList [] = [] transList (p:ps) = trans p : transList ps (xWin2,yWin2) (xWin,yWin)
从Shape到Graphic shapeToGraphic :: Shape -> Graphic shapeToGraphic (Rectangle s1 s2) = let s12 = s1/2 s22 = s2/2 in polygon (transList [(-s12,-s22),(-s12,s22), (s12,s22), (s12,-s22)]) shapeToGraphic (Ellipse r1 r2) = ellipse (trans (-r1,-r2)) (trans (r1,r2)) shapeToGraphic (RtTriangle s1 s2) = polygon (transList [(0,0),(s1,0),(0,s2)]) shapeToGraphic (Polygon pts) = polygon (transList pts)
一些形状 sh1,sh2,sh3,sh4 :: Shape sh1 = Rectangle 3 2 sh2 = Ellipse 1 1.5 sh3 = RtTriangle 3 2 sh4 = Polygon [(-2.5,2.5), (-1.5,2.0), (-1.1,0.2), (-1.7,-1.0), (-3.0,0)]
画出来 main10 = runGraphics $ do w <- openWindow "Drawing Shapes" (xWin,yWin) drawInWindow w (withColor Red (shapeToGraphic sh1)) drawInWindow w (withColor Blue (shapeToGraphic sh2)) spaceClose w
Shape列表的绘制 type ColoredShapes = [(Color,Shape)] shs :: ColoredShapes shs = [(Red,sh1),(Blue,sh2), (Yellow,sh3),(Magenta,sh4)] drawShapes :: Window -> ColoredShapes -> IO () drawShapes w [] = return () drawShapes w ((c,s):cs) = do drawInWindow w (withColor c (shapeToGraphic s)) drawShapes w cs main11 = runGraphics $ do w <- openWindow "Drawing Shapes" (xWin,yWin) drawShapes w shs spaceClose w
Polymorphism • 允许我们用统一的方式处理一组类型 • 多态数据类型是多态化的根源,操作多态数据的函数也随之具备了多态性 • Haskell的这种多态又被称为参数化多态(Parametric polymorphism),在Java,C++中称为泛型(Generic)
高阶函数 • 参数是函数 • 或者返回值是函数 • 函数也可以放在数据结构里
Polymorphic Length length :: [a] -> Int length [] = 0 length (x:xs) = 1 + length xs --多态函数不会去窥视多态化参数,也不关心这个类型具体是什么 length [1,2,3] -- 3 length [’a’,’b’,’c’] -- 3 length [[2],[],[1,2,3]] -- 3 a是一个类型参数,将其小写以区别首字母大写的类型名
多态数据结构 • 如果数据结构不关心其内部数据的类型,便可让多态性派上用场 • 典型的多态数据结构有:List: [a]Tuple: (a,b) • 当然也可以自定义新的多态数据类型
Maybemaybe useful -- 类型参数a使得Maybe具有了多态性 data Maybe a = Nothing | Just a -- 注意构造器的类型Nothing :: Maybe aJust :: a -> Maybe a Just 3 :: Maybe IntJust "x" :: Maybe StringJust (3,True) :: Maybe (Int,Bool)Just (Just 1) :: Maybe (Maybe Int) safeDivide :: Int -> Int -> Maybe Int safeDivide x 0 = Nothing safeDivide x y = Just (x/y)
某种递归的模式 transList :: [Vertex] -> [Point] transList [] = []transList (p:ps) = trans p : transList ps putCharList :: [Char] -> [IO ()]putCharList [] = []putCharList (c:cs) = putChar c : putCharList cs [] [] : f
公共模式的抽象 • 上述模式中只有trans和putChar是不同的 • 试着把它们提取出来作为一个抽象函数的参数 -- 把这个抽象函数命名为mapmap f [] = []map f (x:xs) = f x : map f xs -- 用map重新定义transList和putCharListtransList xs = map trans xsputCharList cs = map putChar cs
多态的高阶的map -- map是个多态函数,并且是个高阶函数 -- 不管a具体是什么类型,所有位置的a -- 必须对应相同的类型,b也是这样 map :: (a->b) -> [a] -> [b] trans :: Vertex -> Pointmap trans :: [Vertex] -> [Point]putChar :: Char -> IO ()map putChar :: [Char] -> [IO ()]
Arithmetic Sequences • 构建符合一定规律的List [1 .. 6] = [1,2,3,4,5,6] [1,3 .. 9] = [1,3,5,7,9] [5,4 .. 1] = [5,4,3,2,1] • [a, b..c]称作arithmetic sequence, 实际是[a, a + d, a + 2 * d, ..., c]的简写,其中d = b - a • 无穷列表 take 9 [1,3..] = [1,3,5,7,9,11,13,15,17] take 5 [5..] = [5,6,7,8,9]
同心圆 conCircles = map circle [2.4, 2.1 .. 0.3] coloredCircles = zip [Black,Blue,Green,Cyan,Red,Magenta,Yellow,White] conCircles main = runGraphics $ do w <- openWindow "Drawing Shapes" (xWin,yWin) drawShapes w (reverse coloredCircles) spaceClose w
Append List (++) :: [a] -> [a] -> [a] [] ++ ys == ys (x:xs) ++ ys == x : (xs ++ ys) "hello" ++ " world" -- "hello world" [1,2,3] ++ [4,5,6] -- [1,2,3,4,5,6] -- 上式按照定义展开得到 1:(2:(3:([]++[4,5,6]))) 时间复杂度和第一个参数的长度成正比 • 满足结合率(xs ++ ys) ++ zs == xs ++ (ys ++ zs) • Prelude对(++) fixity的定义:infixr 5 ++,所以xs ++ ys ++ zs xs ++ (ys ++ zs) length xs + (length xs + length ys) length xs + length ys
另一种递归的模式 listSum [] = 0 listSum (x:xs) = x + listSum xs listProd [] = 1 listProd (x:xs) = x * listProd xs
fold -- 注意fold也是多态的,当然也是高阶的 fold :: (a -> b -> b) -> b -> [a] -> b -- 再一次地把重复的模式抽象出来,成为:fold op init [] = initfold op init (x:xs) = x `op` fold op init xs -- 尝尝foldlistSum xs = fold (+) 0 xslistProd xs = fold (*) 1 xs
两种fold • fold在Haskell里已经预定义了,只不过名字是叫foldr, 因为它是从右开始折叠的:foldr op init (x1 : x2 : ... : xn : []) x1 `op` (x2 `op` (...(xn `op` init)...)) • 还有一个foldl,是从左开始折叠:foldl op init (x1 : x2 : ... : xn : []) (...((init `op` x1) `op` x2)...) `op` xn • 为什么要有两个fold?因为有时候其中一个会比另一个更有效率:foldr (++) [] [x,y,z] x ++ (y ++ z) foldl (++) [] [x,y,z] (x ++ y) ++ z上述两个计算,前者要比后者更有效率,另外的情况下可能是foldl更有效率!
Reverse List • 最简单的实现,不过效率呢?reverse [] = [] reverse (x::xs) = reverse xs ++ [x] • 改进一下,好了一点吗?reverse xs = rev [] xs where rev acc [] = acc rev acc (x:xs) = rev (x:acc) xs • 看起来象foldl,再改进一下:reverse xs = foldl revOp [] xs where revOp a b = b : a
作业 • 在屏幕上画些Shape • 再画些有趣的分形
关于作者 @邓际锋, 网易杭研院程序员 个人研究兴趣 • 编程语言的设计与实现 • 交互式艺术及FP在该领域的应用 • 如何为儿童和非技术人员开发富有乐趣的创作工具 http://weibo.com/ideamonad soloist.deng@gmail.com http://blog.csdn.net/soloist