深入浅出 haskell
Post on 02-Jan-2016
105 Views
Preview:
DESCRIPTION
TRANSCRIPT
深入浅出 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 = Floattype Radius = Float
Shape• 度量 :Float
• 单位 :inch
• 度量 :Int
• 单位 :pixel
Graphic 坐标系统
Shape 坐标系统
(200,200) pixelsor(1,-1) inches
(0,0)
(0,0)
(200,0)
(0,200)
(1,0)(-1,0)
(0,1)
(0,-1)
Graphic
单位转换inchToPixel :: Float -> IntinchToPixel x = round (100*x)
pixelToInch :: Int -> FloatpixelToInch n = intToFloat n / 100
intToFloat :: Int -> FloatintToFloat n = fromInteger (toInteger n)
坐标变换xWin, yWin :: IntxWin = 600yWin = 500
xWin2, yWin2 :: IntxWin2 = xWin `div` 2 yWin2 = yWin `div` 2
trans :: Vertex -> Pointtrans (x,y) = ( xWin2 + inchToPixel x, yWin2 - inchToPixel y )
transList :: [Vertex] -> [Point]transList [] = []transList (p:ps) = trans p : transList ps
(xWin2,yWin2)
(xWin,yWin)
从 Shape 到 GraphicshapeToGraphic :: Shape -> GraphicshapeToGraphic (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 2sh2 = Ellipse 1 1.5sh3 = RtTriangle 3 2sh4 = 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 :: ColoredShapesshs = [(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 csmain11 = runGraphics $ do w <- openWindow "Drawing Shapes" (xWin,yWin) drawShapes w shs spaceClose w
结果
Polymorphism• 允许我们用统一的方式处理一组类型• 多态数据类型是多态化的根源,操作多态数据的函数也随之具备了多
态性• Haskell 的这种多态又被称为参数化多态 (Parametric polymorphism)
,在 Java , C++ 中称为泛型 (Generic)
高阶函数• 参数是函数
• 或者返回值是函数
• 函数也可以放在数据结构里
Polymorphic Length
length :: [a] -> Intlength [] = 0length (x:xs) = 1 + length xs
-- 多态函数不会去窥视多态化参数,也不关心这个类型具体是什么length [1,2,3] -- 3length [’a’,’b’,’c’] -- 3length [[2],[],[1,2,3]] -- 3
a 是一个类型参数,将其小写以区别首字母大写的类型名
多态数据结构• 如果数据结构不关心其内部数据的类型,便可让多态性派
上用场
• 典型的多态数据结构有:
List: [a]Tuple: (a,b)
• 当然也可以自定义新的多态数据类型
Maybe maybe 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 IntsafeDivide x 0 = NothingsafeDivide 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
公共模式的抽象• 上述模式中只有 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
结果
• 满足结合率(xs ++ ys) ++ zs == xs ++ (ys ++ zs)
• Prelude对 (++) fixity 的定义: infixr 5 ++ ,所以
xs ++ ys ++ zs xs ++ (ys ++ zs)
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])))
时间复杂度和第一个参数的长度成正比
length xs + length ys
length xs + (length xs + length ys)
另一种递归的模式listSum [] = 0listSum (x:xs) = x + listSum xs
listProd [] = 1listProd (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
top related