元组与列表#
元组#
元组定义#
(<element>, ...)
元组:储存一系列不同类型元素的序列,只包含两个元素的元组称为序对;
语法格式:
- 使用小括号
()
包围; - 多个元素间用逗号
,
分隔; - 逗号后的空格可省略;
- 使用小括号
类型:元组的类型由其储存的元素个数和各个元素的类型决定;
(1, 2) :: (Num a, Num b) => (a, b) (1, 2, 3) :: (Num a, Num b, Num c) => (a, b, c) ("one", 2) :: Num b => ([Char], b) (1, "two") :: Num a => (a, [Char])
Haskell 中有三元组、四元组等,但不应使用除序对外的元组,因为处理两个以上不同类型的元素有更好的办法;
sumPair :: (Int, Int) -> Int sumPair (x, y) = x + y exp1 = sumPair (2, 3) -- 求值为 2 + 3。返回 5。
元组访问#
fst :: (a, b) -> a
snd :: (a, b) -> b
fst
函数接受一个序对,返回序对的第一个元素;exp1 = fst ("alice", "liddell") -- "alice"
snd
函数接受一个序对,返回序对的第二个元素;exp2 = snd ("alice", "liddell") -- "liddell"
运算#
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry
函数对一个序对应用一个二目运算函数并返回运算结果;res = uncurry (+) (3, 4) -- 7
列表#
语法#
[<element>, ...]
列表:储存一系列相同类型元素的序列;
语法格式:
- 使用中括号
[]
包围; - 多个元素间用逗号
,
分隔; - 逗号后的空格可省略;
- 使用中括号
字符串实质上是字符的列表,
String
只是[Char]
的缩写,因此可以对字符串使用列表函数;Prelude> :t "alice" "alice" :: [Char]
列表可以嵌套,但列表只能包含相同类型的元素;
exp1 = [[1], [1, 2], [1, 2, 3]] -- 合法 exp2 = [1, [1, 2]] -- 非法 exp3 = [(1, 2), (3, 4)] -- 合法 exp4 = [(1, 2), (3, 4, 5)] -- 非法 exp5 = [(1, 2), ('a', 4)] -- 非法
列表定义#
<listName> = []
<listName> = ... : <element> : [<elements>]
空列表:一对中括号
[]
定义一个空列表;构造符:
分号
:
称为构造器,用于构造列表;构造符左侧接受一个元素,右侧接受一个列表;
构造符将左侧元素追加到右侧列表中的第一个位置,并产生一个新列表;
exp1 = 1 : [] -- [1] exp2 = 3 : 1 : [] -- [3,1] exp3 = [2, 3, 4] == 2 : 3 : 4 : [] -- True
中括号
[x,y]
只是x : [y]
的简写,列表本质上由单个元素逐一链接而成;
模式匹配#
<functionName> (x : xs) = <functionBody>
(x : xs)
:表示传入列表的第一个元素x
和剩余列表xs
;
1doubleList :: [Integer] -> [Integer]
2doubleList [] = []
3doubleList (fstElm : rstLst) = fstElm * 2 : doubleList rstLst
4
5exp1 = doubleList [1, 2, 3]
6-- '1 : [2, 3]' 匹配 '(fstElm : rstLst)'
7-- 求值为 '1 * 2 : doubleList [2, 3]'
8 -- '2 : [3]' 匹配 '(fstElm : rstLst)'
9 -- 求值为 '2 * 2 : doublelist [3]'
10 -- '3 : []' 匹配 '(fstElm : rstLst)'
11 -- 求值为 '3 * 2 : doubleList []'
12 -- '[]' 匹配 '[]'
13 -- 求值为 '[]'
14 -- 返回 '[]'
15 -- 返回 '3 * 2 : []'
16 -- 返回 '2 * 2 : [6]'
17-- 返回 '1 * 2 : [4, 6]'
18-- exp1 == [2, 4, 6]
范围#
[s, a .. b]
a .. b
:列表元素生成范围为从
a
到b
;exp1 = [1 .. 10] -- [1,2,3,4,5,6,7,8,9,10]
a
和b
可以为数值或字符;exp2 = ['a' .. 'd'] -- "abcd"
当不存在
s
、且a
大于b
时返回空列表;当不存在
b
时,列表无限长(因为 Haskell 为惰性的,所以只有用到的元素会生成);exp3 = take 5 [2, 4 ..] -- [2,4,6,8,10]
尽量不使用浮点数,因为存在精度问题;
exp4 = [0.1, 0.2 .. 0.5] -- [0.1,0.2,0.30000000000000004,0.4,0.5]
s
:列表头两个值的差值为该范围的步长;exp5 = [2, 4 .. 20] -- [2,4,6,8,10,12,14,16,18,20] exp6 = [10, 9 .. 1] -- [10,9,8,7,6,5,4,3,2,1]
列表访问#
-
GHC.List.
(!!)
:: [a] -> Int -> a# 接受一个列表和一个索引值,返回对应索引的元素。
索引值从 0 开始。若索引值小于 0 则报错(
negative index
),大于列表长度则报错(index too large
)。a :: [Integer] a = [1, 4, 2, 3] exp1 = a !! 2 -- 2 exp2 = a !! 0 -- 1
-
GHC.List.
head
:: [a] -> a# 接受一个非空列表,返回列表第一个元素。
exp3 = head a -- 1
-
GHC.List.
last
:: [a] -> a# 接受一个非空列表,返回列表最后一个元素。
exp4 = last a -- 3
-
GHC.List.
tail
:: [a] -> [a]# 接受一个非空列表,返回列表除第一个元素的剩余元素列表。
exp5 = tail a -- [4,2,3]
-
GHC.List.
init
:: [a] -> [a]# 接受一个非空列表,返回列表除最后一个元素的剩余元素列表。
exp6 = init a -- [1,4,2]
-
GHC.List.
take
:: Int -> [a] -> [a]# 接受一个整数
n
和一个列表,返回列表前n
个元素组成的新列表。若整数
n
小于等于 0 则返回空列表,大于列表长度则返回整个列表。exp7 = take 2 a -- [1,4] exp8 = a -- [1,4,2,3] exp9 = take (-1) a -- [] exp10 = take 10 a -- [1,4,2,3]
-
GHC.List.
drop
:: Int -> [a] -> [a]# 接受一个整数
n
和一个列表,返回除列表前n
个元素外剩余元素组成的新列表。若整数
n
小于等于 0 则返回整个列表,大于列表长度则返回空列表。exp11 = drop 1 a -- [4,2,3] exp12 = drop 3 a -- [3] exp13 = drop (-1) a -- [1,4,2,3] exp14 = drop 10 a -- []
合并#
-
GHC.Base.
(++)
:: [a] -> [a] -> [a]# 接受两个列表,并将后一个列表追加在前一个列表后,返回一个新列表。
exp1 = [1, 2, 3] ++ [2, 3, 4] -- [1,2,3,2,3,4] exp2 = "alice" ++ "liddell" -- "aliceliddell" exp3 = [] ++ [] -- []
-
GHC.List.
zip
:: [a] -> [b] -> [(a, b)]# 接受两个列表,返回储存一系列序对的新列表,每对序对储存对应索引值的元素,当短列表用尽时,函数执行完毕。
有对应函数
zip3
,用于合并三个列表。exp4 = zip [4, 5, 2] "alice" -- [(4,'a'),(5,'l'),(2,'i')]
-
GHC.List.
zipWith
:: (a -> b -> c) -> [a] -> [b] -> [c]# 接受一个函数和两个列表,对两个列表进行遍历,并将两个列表的元素传入函数,计算结果,最终返回结果列表。
有对应函数
zipWith3
,用于合并三个列表。exp5 = zipWith (+) [1, 2, 3] [4, 5, 6] -- [5,7,9] exp6 = zipWith (zipWith (*)) [[1], [1, 2]] [[3], [3, 4]] -- [[3],[3,8]]
拆分#
-
GHC.List.
splitAt
:: Int -> [a] -> ([a], [a])# 接受一个索引值
n
和一个列表,返回从索引处拆分后的两个新列表组成的序对。索引值从 0 开始。若索引值小于等于 0,则序对第一个元素为空元素,若大于等于列表长度,则序对第二个元素为空元素。
a :: String a = "liddell" exp1 = splitAt 0 a -- ("","liddell") exp2 = splitAt 3 a -- ("lid","dell") exp3 = splitAt 10 a -- ("liddell","")
-
Data.OldList.
lines
:: String -> [String]# 接受一个字符串,按换行符拆分字符串并返回列表。
有对应函数
unlines
,接受一个字符串列表,在每个字符串元素后添加换行符后合并为一个新字符串。exp4 = lines "alice\nliddell" -- ["alice","liddell"] exp5 = unlines ["foo", "bar"] -- "foo\nbar\n"
-
Data.OldList.
words
:: String -> [String]# 接受一个字符串,按一或多个连续空白字符拆分字符串并返回列表。
有对应函数
unwords
,接受一个字符串列表,用一个空格符链接所有元素。exp6 = words "quick \r brown\n fox" -- ["quick","brown","fox"] exp7 = unwords ["quick", "brown", "fox"] -- "quick brown fox"
排序#
-
GHC.List.
reverse
:: [a] -> [a]# 接受一个列表,返回倒序排序的新列表。
a :: String a = "alice" exp1 = reverse a -- "ecila"
统计#
-
Data.Foldable.
length
:: Foldable t => t a -> Int# 接受一个序列,返回序列的元素个数。
exp1 = length "alice" -- 5
-
Data.Foldable.
maximum
:: (Foldable t, Ord a) => t a -> a# 接受一个序列,返回序列中的最大元素。
exp2 = maximum "alice" -- 'l'
-
Data.Foldable.
minimum
:: (Foldable t, Ord a) => t a -> a# 接受一个序列,返回序列中的最小元素。
exp3 = minimum "alice" -- 'a'
-
Data.Foldable.
sum
:: (Foldable t, Num a) => t a -> a# 接受一个数值列表,返回所有元素之和。
exp4 = sum [8, 4, 10, 4, 8, 2] -- 36
-
Data.Foldable.
product
:: (Foldable t, Num a) => t a -> a# 接受一个数值列表,返回所有元素之积。
exp5 = product [2, 4, 6, 6, 3] -- 864
判断#
-
Data.Foldable.
elem
:: (Foldable t, Eq a) => a -> t a -> Bool# 接受一个元素
x
和一个序列,若元素x
存在于序列中,则返回True
。有对应函数
notElem
,当元素x
不存在于序列中时返回True
。a :: [Integer] a = [1, 4, 2, 3] exp1 = 5 `elem` a -- False exp2 = elem 3 a -- True
-
Data.Foldable.
null
:: Foldable t => t a -> Bool# 接受一个序列,若序列为空,则返回
True
。exp3 = null a -- False exp4 = null [] -- True
-
Data.Foldable.
and
:: Foldable t => t Bool -> Bool# 接受一个布尔值列表,若所有元素为
True
,则返回True
。exp5 = and [True, True, True] -- True exp6 = and [True, False, True] -- False
-
Data.Foldable.
or
:: Foldable t => t Bool -> Bool# 接受一个布尔值列表,若存在一个元素为
True
,则返回True
。exp7 = or [True, True, False] -- True exp8 = or [False, False, False] -- False
-
Data.Foldable.
all
:: Foldable t => (a -> Bool) -> t a -> Bool# 接受一个谓词和一个列表,若谓词对所有元素的判断均为
True
,则返回True
。exp9 = all odd [1, 3, 5, 7] -- True exp10 = all even [2, 3, 4, 6] -- False
-
Data.Foldable.
any
:: Foldable t => (a -> Bool) -> t a -> Bool# 接受一个谓词和一个列表,若谓词对至少一个元素的判断为
True
,则返回True
。exp11 = any odd [1, 2, 3, 4] -- True exp12 = any even [1, 3, 5, 7] -- False
-
GHC.List.
takeWhile
:: (a -> Bool) -> [a] -> [a]# 接受一个谓词和一个列表,直到谓词判断为
False
之前,函数将所有判断为True
的元素返回为新列表。exp13 = takeWhile odd [1, 3, 5, 6, 7, 8, 9] -- [1,3,5]
-
GHC.List.
dropWhile
:: (a -> Bool) -> [a] -> [a]# 接受一个谓词和一个列表,直到谓词判断为
False
之前,函数将所有判断为True
的元素排除并将剩余元素返回为新列表。exp14 = dropWhile odd [1, 3, 5, 6, 7, 8, 9] -- [6,7,8,9]
-
GHC.List.
filter
:: (a -> Bool) -> [a] -> [a]# 接受一个谓词和一个列表,返回列表中判断为
True
的每个元素。exp15 = filter odd [1, 3, 5, 6, 7, 8, 9] -- [1,3,5,7,9]
其他#
-
Data.Foldable.
concat
:: Foldable t => t [a] -> [a]# 接受一个元素为列表的列表,去掉一层列表并返回结果。
exp1 = concat [[1], [1, 2], [1, 2, 3]] -- [1,1,2,1,2,3]
-
Data.Foldable.
concatMap
:: Foldable t => (a -> [b]) -> t a -> [b]# 接受一个函数和一个列表,对列表中每个元素应用函数,合成新列表后去掉一层列表。
exp2 = concatMap (replicate 3) [1..3] -- [1,1,1,2,2,2,3,3,3]
-
GHC.List.
cycle
:: [a] -> [a]# 接受一个列表,并无限循环该列表。
exp3 = take 10 (cycle [1, 2, 3]) -- [1,2,3,1,2,3,1,2,3,1]
-
GHC.List.
repeat
:: a -> [a]# 接受一个元素,并无限循环该元素。
exp4 = take 10 (repeat 5) -- [5,5,5,5,5,5,5,5,5,5]
-
GHC.List.
replicate
:: Int -> a -> [a]# 接受一个整数
n
和一个元素,并循环该元素n
次。exp5 = replicate 5 'a' -- "aaaaa"
-
GHC.List.
iterate
:: (a -> a) -> a -> [a]# 接受一个函数和一个起始值,函数对起始值应用函数,得到结果并对结果应用函数,依此类推,返回无限长的列表,包含所有结果。
exp6 = take 10 (iterate (* 2) 1) -- [1,2,4,8,16,32,64,128,256,512]
-
GHC.Base.
map
:: (a -> b) -> [a] -> [b]# 接受一个函数和一个列表,遍历列表并对各个元素应用函数,返回结果列表。
exp7 = map (+ 1) [1 .. 5] -- [2,3,4,5,6]
列表推导式#
[<output> | <var> <- <input>, ..., <predicate>, ...]
- 来源:推导式来源于数学中的集合推导式,如 \(S = \{2 \cdot x | x \in \mathbb{N}, x \le 10\}\) 表示集合 \(\{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20\}\),其中 \(2 \cdot x\) 为输出函数,\(x\) 为变量,\(x \in \mathbb{N}\) 为输入集合,\(x \le 10\) 为条件或谓词;
- 语法格式:
- 输出函数:对每个元素要做的操作,可使用表达式,可嵌套;
|
:分隔输出函数部分和剩余部分;- 变量:指定与序列个元素相绑定的变量名,可使用下划线
_
; <-
:遍历序列,并将变量与序列中的各个元素进行绑定;- 输入:变量的数据来源,多个输入用逗号
,
分隔; - 谓词:条件判断,丢弃谓词不成立的元素,和守卫语法基本一致;
1exp1 = [ x * 2 | x <- [0 .. 10] ] -- [0,2,4,6,8,10,12,14,16,18,20]
2exp2 = [ x * 2 | x <- [0 .. 10], x * 2 >= 12 ] -- [12,14,16,18,20]
3exp3 = [ if x < 10 then "FOO" else "BAR" | x <- [7 .. 13], odd x ]
4 -- ["FOO","FOO","BAR","BAR"]
5exp4 = [ x | x <- [10 .. 20], x /= 13, x /= 15, x /= 19 ]
6 -- [10,11,12,14,16,17,18,20]
7exp5 = [ x * y | x <- [2, 5, 10], y <- [8, 10, 11], x * y > 50 ]
8 -- [55,80,100,110]
9keepUpper :: String -> String
10keepUpper st = [ x | x <- st, x `elem` ['A' .. 'Z'] ]
11
12exp6 = keepUpper "AliceInWonderland" -- "AIW"
13
14xxs :: [[Int]]
15xxs =
16 [ [1, 3, 5, 2, 3, 1, 2]
17 , [1, 2, 3, 4, 5, 6, 7]
18 , [1, 2, 4, 2, 1, 6, 3, 1, 3, 2]
19 ]
20
21exp7 = [ [ x | x <- xs, even x ] | xs <- xxs ]
22 -- [[2,2],[2,4,6],[2,4,2,6,2]]
23exp8 = -- brittany 风格
24 [ (a, b, c)
25 | c <- [1 .. 10]
26 , b <- [1 .. c]
27 , a <- [1 .. b]
28 , a + b + c == 24
29 , a ^ 2 + b ^ 2 == c ^ 2
30 ] -- [(6,8,10)]