1 / 40

高阶函数 Higher Order Functions

高阶函数 Higher Order Functions. 阅读第九章、第十章. 计算模式的推广. 我们经常需要对列表的元素进行某种统一的操作 : doubleAll :: [Int] -> [Int] doubleAll [] = [] doubleAll (x:xs) = 2*x : doubleAll xs 或者 doubleAll xs = [2*x | x<- xs]. even :: Int -> Bool even n = n`mod` 2 == 0. 又如 : is Even xs = [even x | x <- xs]. 高阶函数.

tuan
Download Presentation

高阶函数 Higher Order Functions

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. 高阶函数Higher Order Functions 阅读第九章、第十章

  2. 计算模式的推广 我们经常需要对列表的元素进行某种统一的操作: doubleAll :: [Int] -> [Int] doubleAll [] = [] doubleAll (x:xs) = 2*x : doubleAll xs 或者 doubleAll xs = [2*x | x<- xs] even :: Int -> Bool even n = n`mod` 2 == 0 又如: isEven xs = [even x | x <- xs]

  3. 高阶函数 一般的计算模式: [x, y, z, …] [ f x, f y, f z, …] 我们把这种模式称作映射map: map f xs = [f x | x <- xs] map的第一个参数是函数,这种以函数为输入 的函数称为高阶函数(higher order functions)。 map的另一种定义方法 map f [] = [] . map f (x:xs) = f x : map f xs

  4. 例如 map double [1,2,3,4,5] = [2,4,6,8,10] map even [1, 2, 3, 4, 5] = [False, True, False, True, False] 使用高阶函数可以书写更简洁的函数定义: doubleAll xs = map double xs isEven xs = map even xs

  5. map的类型是什么? map even [1, 2, 3, 4, 5] = [False, True, False, True, False] map :: (Int -> Bool) -> [Int] -> [Bool] map double [1,2,3,4,5] = [2,4,6,8,10] map :: (Int -> Int) -> [Int] -> [Int] map的类型是多态的 map :: (a -> b) -> [a] -> [b] 结果是b上的列表 任意一元函数 任何a上的列表

  6. 使用map定义水平翻转 [” # ”, ” # ”, ”######”, ” # ”, ” # ”] [” # ”, ” # ”, ”######”, ” # ”, ” # ”] flipH flipH :: Picture -> Picture flipH pic = [reverse line | line <- pic] filpH pic = map reverse pic

  7. 过滤函数 例如,选出列表中的偶数: [x | x<- [1..5], even x] = [2,4] 又如,选取一个串中的数字 [x | x <- “born on 15 April 1986”, isDigit x] = “151986” isDigit :: Char -> Bool 一般地,我们定义如下过滤模式 filter p xs = [x | x <- xs, p x]

  8. Filter的类型是什么? filter even [1, 2, 3, 4, 5] = [2, 4] even :: Int -> Bool filter :: (Int -> Bool) -> [Int] -> [Int] filter :: (a -> Bool) -> [a] -> [a] 一个表示性质的函数类型

  9. 为什么使用高阶函数? • 高阶函数是函数程序设计的灵魂。 • 许多相似的函数都可以使用一个高阶函数代替。 • 高阶函数比一阶函数更具有表现力,我们可以通过使用不同的函数参数解决不同的问题。 • 每当多个函数具有相似的模式时,定义一个高阶函数。

  10. 折叠的例子 sum [] = 0 sum (x:xs) = x + sum xs and [] = True and (x:xs) = x && (and xs) and [True, False, True] = False sum [2,4,6] = 12 计算模式:使用同一个运算将列表的元素结合起来。 具体到sum: 起始值0, 运算 + and: 起始值True, 运算 &&

  11. 折叠函数 sum [] = 0 sum (x:xs) = x + sum xs 将0和+分别用参数代替, 定义一个高阶函数: foldr op z [] = z foldr op z (x:xs) = x `op` foldr op z xs sum xs = foldr plus 0 xs where plus x y = x+y

  12. 使用foldr定义的sum sum xs = foldr plus 0 xs where plus x y = x+y 或者 sum xs = foldr (+) 0 xs

  13. 使用foldr的函数定义 将列表元素使用某种运算结合起来是一种常见运算,这种运算均可以使用foldr定义,而不必使用递归。 product xs = foldr (*) 1 xs concat xs = foldr (++) [] xs maximum (x:xs) = foldr max x xs

  14. 函数foldr的另一种解读 foldr op z [] = z foldr op z (x:xs) = x `op` foldr op z xs 例如 foldr op z (a:(b:(c:[]))) = a `op` foldr op z (b:(c:[])) = a `op` (b `op` foldr op z (c:[])) = a `op` (b `op` (c `op` foldr op z [])) = a `op` (b `op` (c `op` z)) 运算 “:” 被`op`代替, [] 被z代替.

  15. 问题 解释下列表达式的语义 foldr (:) [] xs foldr (:) [] xs = xs

  16. 问题 解释 foldr (:) ys xs foldr (:) ys [a,b,c] = foldr (:) ys (a:(b:(c:[]))) = (a:(b: (c: ys)) = [a,b,c] ++ ys xs++ys = foldr (:) ys xs

  17. 问题 解释 foldr snoc [] xs where snoc y ys = ys++[y] foldr snoc [] (a:(b:(c:[]))) = a `snoc` (b `snoc` (c `snoc` [])) = (([] ++ [c]) ++ [b] ++ [a] 结果是xs的逆! reverse xs = foldr snoc [] xs where snoc y ys = ys++[y]

  18. 表达式 表达式是函数的一种表示方法。 例如 double x = 2*x 函数double可以表示为 double = x. 2*x 当输入参数是x时,函数的结果是2*x 在Haskell中表示为 double = \x -> 2*x

  19. max x y = if (x<y) then y else x 使用lambda表达式 max = \x\y -> if (x<y) then y else x 或者 max = \x y -> if (x<y) then y lese x 一般地,下列定义等价: f x y z = e f = \x y z -> e

  20. 使用表达式定义函数 reverse xs = foldr snoc [] xs where snoc y ys = ys++[y] 可以直接使用lambda表达式代替只出现一次的函数 reverse xs = foldr (\y ys -> ys++[y]) [] xs

  21. 定义函数unlines unlines [“abc”, “def”, “ghi”] = “abc\ndef\nghi\n” unlines [xs,ys,zs] = xs ++ “\n” ++ (ys ++ “\n” ++ (zs ++ “\n” ++ [])) unlines xss = foldr (xs ys -> xs++“\n”++ys) [] xss 等价于 unlines xss = foldr join [] xss where join xs ys = xs ++ “\n” ++ ys

  22. 又一个计算模式 在一个串中取出一个单词: takeWord“Hello, world!” = “Hello” takeWord [] = [] takeWord (x:xs) | x/=‘ ‘ && x/=‘,’= x:takeWord xs | otherwise = [] 模式: 当某个条件不成立时取得列表元素,直至条件为True。

  23. 推广 takeWord takeWord [] = [] takeWord (x:xs) | notSpace x = x:takeWord xs | otherwise = [] where notSpace x = elem x “ ,;.” takeWhile p [] = [] takeWhile p (x:xs) | p x = x:takeWhile p xs | otherwise = [] 新定义 takeWord xs = takeWhile (x -> elem x “ ,.;”) xs

  24. 函数的表示法: Sections • 一个二元运算只带有一个参数时表示一个函数,称为部分运算(sections): • (+1) :: Int -> Int • (1+) :: Int -> Int • map (+1) [1,2,3] = [2,3,4] • filter (<0) [1,-2,3] = [-2] • takeWhile (0<) [1,-2,3] = [1,3] (a¤) b = a¤b (¤a) b = b¤a

  25. 部分应用 sum xs = foldr (+) 0 xs Haskell 允许如下定义 sum = foldr (+) 0 foldr 只带有三个参数中的 两个。

  26. sum = foldr (+) 0 计算sum [1,2,3] = {replacing sum by its definition} foldr (+) 0 [1,2,3] = {by the behaviour of foldr} 1 + (2 + (3 + 0)) = 6 foldr 有三个参数

  27. 部分应用 任何函数都可以应用于部分输入参数,结果是剩余参数的函数: 如果 f ::Int -> Bool -> Int -> Bool 则 f 3 :: Bool -> Int -> Bool f 3 True :: Int -> Bool f 3 True 4 :: Bool 一个函数得到一个输入后成为等待其他输入的函数

  28. 函数应用和类型的括号结合方法 函数应用是左结合的,函数类型是右结合的: 例如f ::Int -> (Bool -> (Int -> Bool)) 或者 f :: Int -> Bool -> Int -> Bool 则f 3 :: Bool -> (Int -> Bool) (f 3) True :: Int -> Bool ((f 3) True) 4 :: Bool

  29. 使用高阶函数 • 将问题分解成一系列能够使用高阶函数编程的步骤; • 逐渐将输入转换为期望的输出; • 将这些转换函数组合起来。

  30. 例:计算词数 输入: 一个由许多词构成、表示文本的串。 例如 “hello clouds \n hello sky” 输出: 一个文本中出现的所有词及其出现次数的列表,单词按照字典序排列。如 “clouds: 1\nhello: 2\nsky: 1” clouds: 1 hello: 2 sky: 1

  31. 第一步:将输入分解成词的列表 “hello clouds \n hello sky” words [“hello”, “clouds”, “hello”, “sky”]

  32. 第二步:排序 [“hello”, “clouds”, “hello”, “sky”] sort [“clouds”, “hello”, “hello”, “sky”]

  33. 第三步:将相同的词并入一组 [“clouds”, “hello”, “hello”, “sky”] group [[“clouds”], [“hello”, “hello”], [“sky”]]

  34. 第四步:计算每组词数 [[“clouds”], [“hello”, “hello”], [“sky”]] map (ws -> (head ws, length ws)) [(“clouds”,1), (“hello”, 2), (“sky”,1)]

  35. 第五步:格式化每一组 [(“clouds”,1), (“hello”, 2), (“sky”,1)] map ((w,n) -> w++show n) [“clouds: 1”, “hello: 2”, “sky: 1”]

  36. 第六步:将列表格式化 [“clouds: 1”, “hello: 2”, “sky: 1”] unlines “clouds: 1\nhello: 2\nsky: 1\n” clouds: 1 hello: 2 sky: 1

  37. 完整的程序 countWords :: String -> String countWords = unlines . map ((w,n) -> w++show n) . map (ws -> (head ws, length ws)) . groupBy (==) . sort . words

  38. 函数 groupBy group = groupBy (==) groupBy :: (a -> a -> Bool) -> [a] -> [[a]] groupBy p xs -- 将xs分解成段的列表[x1,x2…], 其中每一段上的所有元素均满足某个性质

  39. 小结 •  高阶函数是计算模式的抽象。高阶函数的参数是函数。 •  高阶函数使得许多函数的更简洁、灵活,而且大大减少了编程工作量。 • 表达式、部分运算和部分应用可用于表示函数,作为参数时无需另行定义。 • Haskell提供了许多高阶函数。 • 解决问题的一般步骤:将问题分解成能够用现有函数解决的问题,然后将这些函数粘合起来。

  40. 习题:9.9, 9.10, 9.14, 9.16, 10.4, 10.7, 10.9

More Related