1 / 30

深入浅出 Haskell

深入浅出 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.

Download Presentation

深入浅出 Haskell

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 深入浅出Haskell 第3讲

  2. 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

  3. 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)

  4. 单位转换 inchToPixel :: Float -> Int inchToPixel x = round (100*x) pixelToInch :: Int -> Float pixelToInch n = intToFloat n / 100 intToFloat :: Int -> Float intToFloat n = fromInteger (toInteger n)

  5. 坐标变换 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)

  6. 从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)

  7. 一些形状 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)]

  8. 画出来 main10 = runGraphics $ do w <- openWindow "Drawing Shapes" (xWin,yWin) drawInWindow w (withColor Red (shapeToGraphic sh1)) drawInWindow w (withColor Blue (shapeToGraphic sh2)) spaceClose w

  9. 结果

  10. 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

  11. 结果

  12. Polymorphism • 允许我们用统一的方式处理一组类型 • 多态数据类型是多态化的根源,操作多态数据的函数也随之具备了多态性 • Haskell的这种多态又被称为参数化多态(Parametric polymorphism),在Java,C++中称为泛型(Generic)

  13. 高阶函数 • 参数是函数 • 或者返回值是函数 • 函数也可以放在数据结构里

  14. 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是一个类型参数,将其小写以区别首字母大写的类型名

  15. 多态数据结构 • 如果数据结构不关心其内部数据的类型,便可让多态性派上用场 • 典型的多态数据结构有:List: [a]Tuple: (a,b) • 当然也可以自定义新的多态数据类型

  16. 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)

  17. 某种递归的模式 transList :: [Vertex] -> [Point] transList [] = []transList (p:ps) = trans p : transList ps putCharList :: [Char] -> [IO ()]putCharList [] = []putCharList (c:cs) = putChar c : putCharList cs [] [] : f

  18. 公共模式的抽象 • 上述模式中只有trans和putChar是不同的 • 试着把它们提取出来作为一个抽象函数的参数 -- 把这个抽象函数命名为mapmap f [] = []map f (x:xs) = f x : map f xs -- 用map重新定义transList和putCharListtransList xs = map trans xsputCharList cs = map putChar cs

  19. 多态的高阶的map -- map是个多态函数,并且是个高阶函数 -- 不管a具体是什么类型,所有位置的a -- 必须对应相同的类型,b也是这样 map :: (a->b) -> [a] -> [b] trans :: Vertex -> Pointmap trans :: [Vertex] -> [Point]putChar :: Char -> IO ()map putChar :: [Char] -> [IO ()]

  20. 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]

  21. 同心圆 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

  22. 结果

  23. 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

  24. 另一种递归的模式 listSum [] = 0 listSum (x:xs) = x + listSum xs listProd [] = 1 listProd (x:xs) = x * listProd xs

  25. 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

  26. 两种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更有效率!

  27. 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

  28. 有没有问题?

  29. 作业 • 在屏幕上画些Shape • 再画些有趣的分形

  30. 关于作者 @邓际锋, 网易杭研院程序员 个人研究兴趣 • 编程语言的设计与实现 • 交互式艺术及FP在该领域的应用 • 如何为儿童和非技术人员开发富有乐趣的创作工具 http://weibo.com/ideamonad soloist.deng@gmail.com http://blog.csdn.net/soloist

More Related