510 likes | 648 Views
多元组和列表. 多元组和列表具有很强的表达能力。我们将介绍 模式匹配 列表概括 多态函数. 多元组. 一个多元组由多个值构成,每个值称为多元组的分量。 例如 平面上的点 (x,y) 三维空间的点 (x,y,z) 购物项目 (”Wulong Tea”, 55) 不包含任何元素的元组 (). 多元组类型. 一个多元组类型说明每个分量的类型。 例如 (1,2) :: (Int, Int) (1.5, 2.25) :: (Float, Float) (”Wulong Tea”, 55) :: (String, Int) () :: () 单位类型. 类型定义.
E N D
多元组和列表 多元组和列表具有很强的表达能力。我们将介绍 • 模式匹配 • 列表概括 • 多态函数
多元组 • 一个多元组由多个值构成,每个值称为多元组的分量。 • 例如 • 平面上的点(x,y) • 三维空间的点(x,y,z) • 购物项目(”Wulong Tea”, 55) • 不包含任何元素的元组()
多元组类型 一个多元组类型说明每个分量的类型。 例如 (1,2) :: (Int, Int) (1.5, 2.25) :: (Float, Float) (”Wulong Tea”, 55) :: (String, Int) () :: () 单位类型
类型定义 我们可以给多元组类型命名: type Purchase = (String, Int) Purchase和(String, Int) 可以互换: tea :: Purchase tea = (”Wulong Tea”, 55) 类型名以大写字母开始
模式匹配(Pattern matching) 多元组上的函数可以通过模式匹配定义。 例如,addPair (x, y) 返回x + y. addPair :: (Int, Int) -> Int addPair (x, y) = x + y 模式可以嵌套: shift :((Int, Int), Int) -> (Int, (Int, Int)) shift ((x, y), z) = (x, (y, z)) 一格模式,而不是一个简单变量
模式匹配 可以定义取得多元组某个分量的函数: name :: Purchase -> String name (s, i) = s price :: Purchase -> Int price (s, i) = i 一个模式必须与相应类型匹配.
标准选择函数 fst (x, y) = x snd (x, y) = y 但是,不可定义: select (x, y) 1 = x select (x, y) 2 = y 函数值类型不能依赖于参数的值。 它们的类型是什么?
列表(Lists) 一个列表由同类型的若干值构成,每个值称为列表的一个元素。 例如 [1, 2, 3] :: [Int] [True, False, True] :: [Bool] [] :: [Float] [(”Wulong Tea”, 55)] :: [Purchase]
多元组与列表 一个多元组有固定个数的元素构成,这些元素可以具有不同的类型。 一个列表由任意个数同类型元素构成。 列表类型上具有非常丰富的函数。 每当数据是由同类型的一系列元素构成时,考虑使用列表!
使用列表计数 [1..10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [1,3..10] = [1, 3, 5, 7, 9] 例 fac :: Int -> Int fac n = product [1..n] 将列表元素相乘的标准函数
列表概括(List Comprehensions) 我们经常需要对列表元素施行同一种操作: 例如 doubles :: [Int] -> [Int] doubles xs = [2 * x | x <- xs] doubles [1..3] [2, 4, 6] 对xs中的每个元素 将2*x加到生成的列表上
求1至n的平方和 n [1, 2, 3, …, n] [1, 4, 9, …, n^2] 1 + 4 + 9 + … + n^2 sumsq :: Int -> Int sumsq n = sum [i^2 | i <- [1..n]]
过滤列表 列表概括可以包含一个或者多个条件,以过滤掉那些不满足条件的元素。 例如,求因子函数 factors :: Int -> [Int] factors n = [i | i <- [2..n], n `mod` i == 0] factors 12 [2, 3, 4, 6, 12] 只包含通过测试的元素
使用因子函数 计算因子个数: numFactors :: Int -> Int numFactors n = length (factors n) 测试素数: isPrime :: Int -> Bool isPrime n = factors n == [n] 列表长度函数, 返回列表元素个数
素数列表 我们可以列出小于某个数的所有素数: primes :: Int -> [Int] primes n = [x | x <- [2..n], isPrime x] primes 30 = [2,3,5,7,11,13,17,19,23,29]
列表概括的设计 每当你对列表元素逐个进行某个操作时使用列表概括。 一些条件测试 [ e | x <- list, test ] 表达式 称为生成器,x可以变量, 也可以是一个模式.
列表模式 每个列表示通过反复使用运算(:)构造的(读作cons), 其作用是在列表前添加一个元素。 [1,2,3,4] 等价于 1:( 2: (3: (4: []))) 空列表: [] 非空列表: x:xs
列表上的函数可以使用模式匹配: head :: [a] -> a head (x: xs) = x 或者 head (x:_) = x 例如, head [1,2,3] = 1
tail :: [a] -> [a] tail (_:xs) = xs tail [1,2,3] = [2,3]
一个图书馆数据库的设计 任务:借阅信息存储到计算机上,并实现计算机管理(增加,删除,查询等) Alice Bush borrowed The Craft of Functional Programming Bob Clinton borrowed Million dollar baby!
借阅卡的表示 借阅卡包含那些信息? 借阅者名 Alice Bush borrowed The Craft of Functional Programming 书名 type Borrower = String type Book = String type Card = (Borrower, Book)
借阅卡文件表示 借阅卡文件由什么构成? -- 借阅卡序列! type Database = [Card] example :: Database example = [ (”Alice Bush”, ”The Craft of Functional Programming”), (”Bob Clinton”, ”Million Dollar Baby”) ]
查询数据库 我们希望从数据库中获得那些信息? books :: Database -> Borrower -> [Book] borrowers :: Database -> Book -> [Borrower] borrowed :: Database -> Book -> Bool numBorrowed :: Database -> Borrower -> Int 某人借阅的图书 某书是否借出 某人借阅的图书数
Alice借了那些书? books example ”Alice Bush” = [”The Craft of Functional Programming”] books example ”David Gates” = [] books db person = [book | (borrower, book) <- db, borrower == person] 没有借书 使用模式匹配.
问题 定义: numBorrowed :: Database -> Borrower -> Int
定义 numBorrowed :: Database -> Borrower -> Int numBorrowed db person = length (books db person)
更新数据库 借书和还书均需要更新数据库 makeLoan :: Database -> Borrower -> Book -> Database returnLoan :: Database -> Book -> Database … 来计算修改后的 数据库 这些函数需要使用 更新前的库信息...
借书 makeLoan :: Database -> Borrower -> Book -> Database makeLoan db borrower book = [(borrower, book)] ++ db 增加新的借阅卡
还书 returnLoan :: Database -> Book -> Database returnLoan db book = [(borrower, book’) | (borrower, book’) <- db, book’ /= book]
列表的作用 记录序列随处可见,列表为此提供了一个建模工具 Haskell能够为列表提供很好的支持,是数据结构的第一选择 但是,列表有时效率不高
一些列表标准函数 Haskell 提供了许多标准列表函数。 length的类型是什么?
多态类型(Polymorphic Types) length 具有许多类型 其类型是多态的 (polymorphic). length :: [Int] -> Int length :: [Float] -> Int length :: [Card] -> Int 对于任意类型t, length :: [t] -> Int 一个类型变量 表示任意类型 类型变量 以小写字母开始
Taking and Dropping • take n xs 返回xs的前n个元素, • drop n xs 返回xs除前n个元素外的所有元素. • 例 • take 3 (factors 12) [2, 3, 4] • drop 3 (factors 12) [6, 12]
问题 take 和drop 的类型是什么?
问题 take 和drop具有相同的多态类型. take, drop :: Int -> [a] -> [a] 例如 Int -> [Float] -> [Float] Int -> [String] -> [String] Not Int -> [Float] -> [String] a 表示同一个类型.
The Zipper 有时我们需要将两个列表的对应元素配对: zip [ ”Alice” , ”Bob”, ”Mary” ] [ ”American”, ”American”, ”English” ] [ (”Alice”,”American”), (”Bob”,”American”), (”Mary”,”English”)]
zip的类型 zip :: [a] -> [b] -> [(a, b)] 例 zip [1, 2, 3] [”a”, ”b”, ”c”] [(1,”a”), (2,”b”), (3,”c”)] 此例的类型zip :: [Int] -> [String] -> [(Int, String)]
例:求 x 在 xs 中的位置 position :: a -> [a] -> Int 例: position ”b” [”a”, ”b”, ”c”] 2 方法: 给列表每个元素标以位置号,然后查找给定的元素。
给列表元素标以位置号 [”a”, ”b”, ”c”] [(”a”,1), (”b”,2), (”c”,3)] zip [”a”, ”b”, ”c”] [ 1, 2, 3 ] [(”a”,1), (”b”,2), (”c”,3)] 使用zip xs [1..length xs]
查找合适的元素 选取查找元素出现的位置 positions x xs = [pos | (x’,pos) <- zip xs [1..length xs], x’ == x] positions ”b” [”a”, ”b”, ”c”] [2] 结果是列表 我们需要一个数
最后的函数 position :: a -> [a] -> Int position x xs = head [pos | (x’,pos) <- zip xs [1..length xs], x’ == x] 选取第一个元素.
计算路径长度 路径长度是多少?
点的表示 type Point = (Float, Float) distance :: Point -> Point -> Float distance (x, y) (x’, y’) = sqrt ((x-x’)^2 + (y-y’)^2) x- 和y-坐标.
路径的表示 Q S R P type Path = [Point] examplePath = [p, q, r, s] path length = distance p q + distance q r + distance r s
两个常用的函数 • init xs -- 不包括最后一个元素的列表, • tail xs – 不包括第一个元素的列表. • init [p, q, r, s] [p, q, r] • tail [p, q, r, s] [q, r, s] • zip … [(p,q), (q,r), (r,s)]
函数pathLength pathLength :: Path -> Float pathLength xs = sum [distance p q | (p,q) <- zip (init xs) (tail xs)] 例 pathLength [p, q, r, s] distance p q + distance q r + distance r s
原则 Question: 你需要一起遍历两个列表吗? Answer: 使用zip!
小结 • 列表可以表示任何元素序列 • 列表可以表示程序中的重复计算 • 列表的构造和丰富的多态列表函数便于列表的处理 • 每当处理元素序列时,考虑使用列表!