《Haskell趣学指南》—— 第1章,第1.3节列表入门

1.3 列表入门
在Haskell中,列表是一种单类型的(homogeneous)数据结构,可以用来存储多个类型相同的元素。我们可以在里面装一组数字或者一组字符,但不能把字符和数字装在一起。

列表由方括号括起,其中的元素用逗号分隔开来:

ghci> let lostNumbers = [4,8,15,16,23,42]
ghci> lostNumbers
[4,8,15,16,23,42] 

注意:
在GHCi中,可以使用let关键字来定义一个常量。在GHCi中执行let a = 1与在脚本中编写a=1随后用:l装载的效果是等价的。

1.3.1 拼接列表
拼接两个列表可以算是最常见的操作之一了,在Haskell中这可以通过++运算符实现:

ghci> [1,2,3,4] ++ [9,10,11,12]
[1,2,3,4,9,10,11,12]
ghci> "hello" ++ " " ++ "world"
"hello world"
ghci> ['w','o'] ++ ['o','t']
"woot"

注意:
Haskell中的字符串实际上就是一组字符组成的列表。"hello"只是['h','e','l','l','o']的语法糖而已。因此,我们可以使用处理列表的函数来对字符串进行操作。
在使用++运算符处理长字符串时要格外小心(对长的列表也是同样),Haskell会遍历整个第一个列表(++符号左边的列表)。在处理较短的字符串时问题还不大,但要是在一个长度为5000万的列表上追加元素,那可得执行好一会儿了。

不过,仅仅将一个元素插入列表头部的成本几乎为零,这里使用: 运算符(被称作Cons1运算符)即可:

ghci> 'A':" SMALL CAT"
"A SMALL CAT"
ghci> 5:[1,2,3,4,5]
[5,1,2,3,4,5] 

可以留意,第一个例子中,取一个字符和一个字符构成的列表(即字符串)作为参数;第二个例子很相似,取一个数字和一个数字组成的列表作为参数。:运算符的第一个参数的类型一定要与第二个参数中列表中元素的类型保持一致。

而++运算符总是取两个列表作为参数。若要使用++运算符拼接单个元素到一个列表的最后,必须用方括号把它括起来,使之成为单元素的列表。

ghci> [1,2,3,4] ++ [5]
[1,2,3,4,5] 

这里如果写成[1,2,3,4] ++ 5就错了,因为++运算符的两边都必须是列表才行,这里的5是个数,不是列表。

有趣的是,[1,2,3]实际上是1:2:3:[]的语法糖。[]表示一个空列表,若从前端插入3,它就成了[3],再插入2,它就成了[2,3],依此类推。

注意:
[]、[[]]和[[], [], []]是不同的。第一个是一个空的列表,第二个是含有一个空列表的列表,第三个是含有三个空列表的列表。

1.3.2 访问列表中的元素
若是要按照索引取得列表中的元素,可以使用!!运算符,下标从0开始。

ghci> "Steve Buscemi" !! 6
'B'
ghci> [9.4,33.2,96.2,11.2,23.25] !! 1
33.2

但你如果想在一个只含有4个元素的列表中取它的第6个元素,就会得到一个错误。要小心!

1.3.3 嵌套列表
列表同样也可以以列表为元素,甚至是列表的列表的列表:

ghci> let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
ghci> b
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
ghci> b ++ [[1,1,1,1]]
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]
ghci> [6,6,6]:b
[[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
ghci> b !! 2
[1,2,2,3,4] 

列表中的列表可以是不同长度的,但类型必须相同。与不能在同一个列表中混合放置字符和数组一样,混合放置数的列表和字符的列表也是不可以的。

1.3.4 比较列表
只要列表内的元素是可以比较的,那就可以使用<、>、>= 和 <= 来比较两个列表的大小。 它会按照字典顺序,先比较两个列表的第一个元素,若它们的值相等,则比较二者的第二个元素,如果第二个仍然相等,再比较第三个,依此类推,直至遇到不同为止。两个列表的大小以二者第一个不等的元素的大小为准。

比如,执行[3, 4, 2] < [3, 4, 3]时,Haskell就会发现3与3相等,随后比较4与4,依然相等,再比较2与3,这时就得出结论了:第一个列表比第二个列表小。>、>=与<=也是同样的道理。

ghci> [3,2,1] > [2,1,0]
True
ghci> [3,2,1] > [2,10,100]
True
ghci> [3,4,2] < [3,4,3]
True
ghci> [3,4,2] > [2,4]
True
ghci> [3,4,2] == [3,4,2]
True

此外,非空列表总认为比空列表更大。这样即可保证两个列表总是可以顺利地做比较,即使其中一个列表完全等同于另一个列表的开头部分。

1.3.5 更多列表操作
下面是几个有关列表的常用函数,稍带用途与示例。
head函数返回一个列表的头部,也就是列表的第一个元素。

ghci> head [5,4,3,2,1]
5

tail函数返回一个列表的尾部,也就是列表除去头部之后的部分:

ghci> tail [5,4,3,2,1]
[4,3,2,1] 

last函数返回一个列表的最后一个元素。

ghci> last [5,4,3,2,1]
1

init函数返回一个列表除去最后一个元素的部分。

ghci> init [5,4,3,2,1]
[5,4,3,2] 

要更直观地看这些函数,我们可以把列表想象为一头怪兽,下面就是它的样子:

试一下,若是取一个空列表的头部又会怎样?

ghci> head []
*** Exception: Prelude.head: empty list

天哪,它翻脸了!如果怪兽压根就不存在,head又从何而来?在使用head、tail、last和init时要小心,不要用到空列表上。这个错误不会在编译时被捕获,所以说做些工作以防止Haskell从空列表中取值是一个好的习惯。

length函数返回一个列表的长度:

ghci> length [5,4,3,2,1]
5

null函数检查一个列表是否为空。如果是,则返回True;否则返回False:

ghci> null [1,2,3]
False
ghci> null []
True

reverse函数将一个列表反转:

ghci> reverse [5,4,3,2,1]
[1,2,3,4,5] 

take函数取一个数字和一个列表作为参数,返回列表中指定的前几个元素,如下:

ghci> take 3 [5,4,3,2,1]
[5,4,3]
ghci> take 1 [3,9,3]
[3]
ghci> take 5 [1,2]
[1,2]
ghci> take 0 [6,6,6]
[]

如上,若是试图取超过列表长度的元素个数,只能得到原先的列表。若取0个元素,就会得到一个空列表。

drop函数的用法大体相同,不过它是删除一个列表中指定的前几个元素:

ghci> drop 3 [8,4,2,1,5,6]
[1,5,6]
ghci> drop 0 [1,2,3,4]
[1,2,3,4]
ghci> drop 100 [1,2,3,4]
[]

maximum函数取一个列表作为参数,并返回最大的元素,其中的元素必须可以做比较。minimum函数与之相似,不过是返回最小的元素。

ghci> maximum [1,9,2,3,4]
9
ghci> minimum [8,4,2,1,5,6]
1

sum函数返回一个列表中所有元素的和。product函数返回一个列表中所有元素的积。

ghci> sum [5,2,1,6,3,2,5,7]
31
ghci> product [6,2,1,2]
24
ghci> product [1,2,5,6,7,9,2,0]
0

elem函数可用来判断一个元素是否包含于一个列表,通常以中缀函数的形式调用它,这样更加清晰。

ghci> 4 elem [3,4,5,6]
True
ghci> 10 elem [3,4,5,6]
False
时间: 2024-10-29 19:55:38

《Haskell趣学指南》—— 第1章,第1.3节列表入门的相关文章

《Haskell趣学指南》—— 第2章,第2.1节显式类型声明

第 2 章 相信类型Haskell趣学指南强大的类型系统是Haskell的秘密武器.在Haskell中,每个表达式都会在编译时得到明确的类型,从而提高代码的安全性.若你写的程序试图让布尔值与数相除,就不会通过编译.这样的好处就是与其让程序在运行时崩溃,不如在编译时捕获可能的错误.Haskell中一切皆有类型,因此编译器在编译时可以得到较多的信息来检查错误. 与Java和Pascal不同,Haskell支持类型推导(type inference).写下一个数,不必额外告诉Haskell说"它是个数

《Haskell趣学指南》—— 第1章,第1.1节调用函数

第 1 章 各就各位,预备!Haskell趣学指南如果你属于那种从不看前言的人,我建议你还是回头看一下本书前言的最后一节比较好.那里讲解了如何使用本书,以及如何通过GHC加载函数. 首先,我们要做的就是进入GHC的交互模式,调用几个函数,以便我们简单地体验一把Haskel.打开终端,输入ghci,可以看到如下欢迎信息: GHCi, version 6.12.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ..

《Haskell趣学指南》——导读

目 录 第 1 章 各就各位,预备! 第 1 章第 1 节调用函数第 1 章第 2 节小朋友的第一个函数第 1 章第 3 节列表入门第 1 章第 4 节得州区间2第 1 章第 5 节我是列表推导式第 1 章第 6 节元组 第 2 章 相信类型 第 2 章第 1 节显式类型声明第 2 章第 2 节Haskell的常见类型第 2 章第 3 节类型变量第 2 章第 4 节类型类入门 第 3 章 函数的语法 第 4 章 你好,递归 第 5 章 高阶函数 第 6 章 模块 第 7 章 构造我们自己的类型和

《Haskell趣学指南》—— 第1章,第1.5节我是列表推导式

1.5 我是列表推导式 列表推导式(list comprehension)是一种过滤.转换或者组合列表的方法. 学过数学的你对集合推导式(set comprehension)概念一定不会陌生.通过它,可以从既有的集合中按照规则产生一个新集合.前10个偶数的集合推导式可以写为{2 · x | x∈N, x≤ 10},先不管语法,它的含义十分直观:"取所有小于等于10的自然数,各自乘以2,将所得的结果组成一个新的集合." 若要在Haskell中实现上述表达式,我们可以通过类似take 10

《Haskell趣学指南》—— 第2章,第2.4节类型类入门

2.4 类型类入门 类型类(typeclass)是定义行为的接口.如果一个类型是某类型类的实例(instance),那它必实现了该类型类所描述的行为. 说得更具体些,类型类是一组函数的集合,如果将某类型实现为某类型类的实例,那就需要为这一类型提供这些函数的相应实现. 可以拿定义相等性的类型类作为例子.许多类型的值都可以通过==运算符来判断相等性,我们先检查一下它的类型签名: ghci> :t (==) (==) :: (Eq a) => a -> a -> Bool 注意,判断相等

《Haskell趣学指南》—— 第1章,第1.4节得州区间2

1.4 得州区间2该怎样得到一个由1-20所有数组成的列表呢?我们完全可以用手把它们全都录入一遍,但显而易见,这并不是完美人士的方案,完美人士都用区间(range).区间是构造列表的方法之一,而其中的值必须是可枚举的,或者说,是可以排序的. 例如,数字可以枚举为1.2.3.4等.字符同样也可以枚举:字母表就是A-Z所有字符的枚举.然而人名就不可以枚举了,"John"后面是谁?我不知道. 要得到包含1-20中所有自然数的列表,只要录入[1..20]即可,这与录入[1,2,3,4,5,6,

《Haskell趣学指南》—— 第2章,第2.3节类型变量

2.3 类型变量有时让一些函数处理多种类型将更加合理.比如head函数,它可以取一个列表作为参数,返回这一列表头部的元素.在这里列表中元素的类型不管是数值.字符还是列表,都不重要.不管它具体的类型是什么,只要是列表,head函数都能够处理. 猜猜head函数的类型是什么呢?用:t检查一下: ghci> :t head head :: [a] -> a 这里的a是什么?是类型吗?想想我们在前面说过,凡是类型其首字母必大写,所以它不是类型.它其实是个类型变量(type variable),意味着a

《Haskell趣学指南》—— 第2章,第2.2节Haskell的常见类型

2.2 Haskell的常见类型接下来我们看几个Haskell中常见的基本类型,比如用于表示数.字符.布尔值的类型. Int意为整数.7可以是Int,但7.2不可以.Int是有界的(bounded),它的值一定界于最小值与最大值之间.注意:我们使用的 GHC 编译器规定 Int 的界限与机器相关.如果你的机器采用64位CPU,那么Int 的最小值一般为−263,最大值为263−1.Integer也是用来表示整数的,但它是无界的.这就意味着可以用它存放非常非常大的数(真的非常非常大!),不过它的效

《Haskell趣学指南》—— 第1章,第1.6节元组

1.6 元组元组(tuple)允许我们将多个异构的值组合成为一个单一的值.从某种意义上讲,元组很像列表.但它们却有着本质的不同.首先就像前面所说,元组是异构的,这表示单个元组可以含有多种类型的元素.其次,元组的长度固定,在将元素存入元组的同时,必须明确元素的数目. 元组由括号括起,其中的项由逗号隔开. ghci> (1, 3) (1,3) ghci> (3, 'a', "hello") (3,'a',"hello") ghci> (50, 50.4