列表 (List),是一种单类型 (Homogeneous) 数据结构,用于存储一个或多个同类型元素,例如一系列的数字。

将一系列的元素使用方括号 ([]) 包含,元素间使用逗号 (,) 分隔,就形成了列表。列表允许嵌套。

Haskell> let aList = [1,2,3,4,5,435,213,546,34,54345]
Haskell> aList
[1,2,3,4,5,435,213,546,34,54345]
Haskell>

此处的 let 被用于在 GHCi 中定义常量,let a = 0 与先在 .hs 文件中写入 a = 0 后通过 :l 加载的效果相同。

来看一些常见的列表操作:

Haskell> [1,2,3] ++ [8,9,0]
[1,2,3,8,9,0]
Haskell> ['i','l','o','v','e','u']
"iloveu"
Haskell>  "hello" ++ " " ++ "world"
"hello world"

上面的代码都是列表的拼接操作,字符串实际上是字符列表的语法糖。

需要注意的是,使用 ++ 时左边的参数会被整个遍历,这就意味着列表长度越长,拼接效率越低,耗时越长。

Haskell> 'a':"bcdefg"
"abcdefg"
Haskell> 1:[2,3,4,5,6]
[1,2,3,4,5,6]
Haskell> 1:2:3:4:[]
[1,2,3,4]

像这样将一个元素拼接到列表开头的操作,成本几乎为零。

Haskell> ['a','b','c','d','e'] !! 1
'b'
Haskell> "hello" !! 4
'o'

使用 !! 可以访问列表中制定下标的元素,下标从 0 开始。

此外,部分情况下,列表也是可以进行比较的:

Haskell> [1,2,3] == [1,2,3]
True
Haskell> "hi" == ['h','i']
True
Haskell> 1:2:3:[] == [1,2,3]
True
Haskell> [1,1,2] < [2,1,2]
True
Haskell> [2,2,1] < [2,2,2]
True
Haskell>

当列表元素可以进行比较时,会从左到右依次对每个下标位置进行比较。非空列表总是大于空列表。

Haskell> head [5,4,3,2,1]
5
Haskell> tail [5,4,3,2,1]
[4,3,2,1]
Haskell> last [5,4,3,2,1]
1
Haskell> init [5,4,3,2,1]
[5,4,3,2]
Haskell>
  • head 返回列表第一个元素

  • last 返回列表最后一个元素

  • tail 返回除去第一个元素外的剩余元素

  • init 返回除去最后一个元素外的剩余元素

注意,它们都不可作用于空列表。

Haskell> length [1,2,3,4,5]
5
Haskell> length "hello"
5
Haskell> null []
True
Haskell> null [1,2,3]
False
Haskell> null ""
True
Haskell> null "a"
False
Haskell> reverse "dlrow"
"world"
Haskell> reverse [1,2,3]
[3,2,1]
Haskell> take 2 [1,2,3]
[1,2]
Haskell> take 2 "hello"
"he"
Haskell> take 0 [2,3,4]
[]
Haskell> drop 2 "hello"
"llo"
Haskell> drop 2 [5,4,3,2]
[3,2]
Haskell> drop 1000 [1,2,3]
[]
Haskell> maximum [1,2,3,56,7,8,56,345]
345
Haskell> minimum [213,345,546,1]
1
Haskell> sum [1,2,3,4,5]
15
Haskell> product [1,2]
2
Haskell> product [2,5]
10
Haskell> product [3,5]
15
Haskell> product [2,3,4]
24
Haskell> elem 1 [2,3,4]
False
Haskell> elem 2 [2,3,4]
True
Haskell> 3 `elem` [2,3,4]
True
Haskell>
  • lenght 返回列表长度

  • null 检查列表是否为空

  • reverse 翻转列表

  • take 从列表中从前向后取指定数量的元素

  • drop 从列表中从前向后删除指定数量的元素

  • maximumminimum 分别返回列表中最大和最小的元素

  • sum 返回列表中元素的和

  • product 返回列表中元素的乘积

  • elem 判断某个值是否是列表元素之一

Haskell> [1..10]
[1,2,3,4,5,6,7,8,9,10]
Haskell> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
Haskell> ['F'..'Q']
"FGHIJKLMNOPQ"
Haskell> [1,4..100]
[1,4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79,82,85,88,91,94,97,100]
Haskell> [5,10..100]
[5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100]
Haskell> [1,3..3*5]
[1,3,5,7,9,11,13,15]
Haskell> [1,2..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40.............
Haskell> take 3 [1,2..]
[1,2,3]
Haskell> take 9 (repeat "ABc")
["ABc","ABc","ABc","ABc","ABc","ABc","ABc","ABc","ABc"]
Haskell> replicate 3 10
[10,10,10]

区间是很聪明的,但对于浮点数使用区间要格外小心,可能会出现很糟糕的精度问题。

Haskell> [x | x <- [1..10]]
[1,2,3,4,5,6,7,8,9,10]
Haskell> [x | x <- [10..20]]
[10,11,12,13,14,15,16,17,18,19,20]
Haskell> [x | x <- [1..10], x `mod` 2 == 0]
[2,4,6,8,10]
Haskell> [x * 2 | x <- [1..10], x `mod` 2 == 0]
[4,8,12,16,20]
Haskell>

上面这些代码被称为列表推导式 (List Comprehension),其中,符号 | 右边表示推导的依据 (谓词,Predicate),可以有多个,使用逗号分隔,根据这些依据得出的列表会经过 | 左侧进行处理后返回。

以最后一句代码 [x * 2 | x <- [1..10], x `mod` 2 == 0] 为例,求 [1..10] 区间所有除以 2 余数为 0 的数 (即 [2,4,6,8,10]),然后将这些数全部乘以 2,得到 [4,8,12,16,20]