跳转到内容

列表处理

来自维基教科书,开放书籍,构建开放世界

论文2 - ⇑ 函数式编程基础 ⇑

← 函数式编程基础 列表处理 高阶函数 →



列表 - 一种抽象数据类型,按特定顺序存储项目。


列表是一种 抽象数据类型,按特定顺序存储项目。同一个项目可以在同一个列表中出现多次。例如,以下都是列表

[9,2,3,4,4,3,7,8]

[65,75,3,65,0]

["cabbage", "potato", "boat", "helmet", "boat"]

空列表由空括号定义

[]

列表也可以包含其他列表

[[1,2,3], [6,3,8], [9,10,3]]

在 Haskell 中,列表中包含的项目必须都是同一数据类型。

扩展:数据类型和列表

虽然像 Haskell 这样的某些编程语言只允许列表中的项目为同一数据类型,但有时可以在列表中包含多种数据类型。虽然这可能可行,但可能不是一个好主意,并且会导致一些非常难以调试的代码。例如,在 python 中,我们可能从一些看起来非常简单的代码中得到意外的输出

.> myList = ['-92', 9, 5]
.> min(myList)
5


创建和检查列表

[编辑 | 编辑源代码]

您可以在列表上执行一系列函数

您可以创建一个空列表

.> someList = []
.> someList
[]

您可以创建一个包含项目的列表

myList = [20,3,51,6,3,7]

您可以获取列表的长度

.> myList = [20,3,51,6,3,7]
.> length myList
6

您可以检查列表是否为空

.> myList = [20,3,51,6,3,7]
.> null myList
False

.> someList = []
.> null myList
True

拆分列表

[编辑 | 编辑源代码]
头部 - 列表中的第一个元素。


尾部 - 列表中除头部之外的每个元素。


一些非常常见的列表命令是头部尾部。如果您将列表视为一条蛇,头部将为您提供列表中的第一个项目(它的头部),而尾部将删除蛇的头部,并将剩下的部分作为新列表返回。

snake analogy for traversing a list
遍历列表的蛇类比喻
.> myList = [20,3,51,6,3,7]
.> head(myList)
20
.> myList = [20,3,51,6,3,7]
.> tail(myList)
[3, 51, 6, 3, 7]

您可以将多个头部和尾部命令组合在一起

.> myList = [20,3,51,6,3,7]
.> head(tail(tail(myList)))
51
示例:组合多个头部和尾部命令

在上面的示例中,我们从最里面的函数开始,在本例中是tail(...),并向外逐步进行。

head(tail(tail(myList)))

命令tail(myList)会从myList中删除第一个项目,并返回剩下的部分:[3,51,6,3,7]。然后,我们可以用此返回的列表替换tail(myList)

head(tail([3,51,6,3,7]))

现在我们进入下一个最里面的函数

head(tail([3,51,6,3,7]))

命令tail([3,51,6,3,7])会从[3,51,6,3,7]中删除第一个项目,并返回剩下的部分:[51,6,3,7]。然后,我们可以用此返回的列表替换tail([3,51,6,3,7])

head([51,6,3,7])

这只剩下一个函数要计算了。我们知道head函数会返回给定列表的第一个项目。在本例中,它获取[51,6,3,7]的第一个项目,得到

51

向列表添加元素

[编辑 | 编辑源代码]

前置 (:) 一个项目到另一个列表,即在前面添加一个元素,向列表添加一个新的头部

.> myList = [20,3,51,6,3,7]
.> 5 : myList
[5, 20, 3, 51, 6, 3, 7]
.> "c" : ["a", "t"]
["c", "a", "t"]
.> 1 : 2 : myList
[1, 2, 20, 3, 51, 6, 3, 7]
扩展:处理字符串、字符和列表时的类型不匹配

您可能习惯于其他编程语言以几乎相同的方式处理单引号和双引号,例如在 Javascript 中

.> a = "Double quotation marks with a single (') in the middle"
.> b = 'Single quotation marks with a double (") in the middle'
.> a == b
True

Haskell 并非如此。Haskell 将单引号 (') 视为定义单个元素,而将双引号 (") 视为定义一个列表,即使双引号内只有一个元素,例如 "c"。这意味着它将 'c' 视为一个类型为 Char 的单个项目,而将 "c" 视为一个类型为 Char列表。如果我们确保匹配我们的数据类型,我们将没问题。

['c', 'a', 't'] 等效于 "cat"

.> "cat"
"cat"
.> ['c','a','t']
"cat"
.> ['c','a','t'] == "cat"
True

["c", "a", "t"] 等效于 "cat",因为 ["c", "a", "t"] 是一个类型为 Char列表的列表,而 "cat" 是一个类型为 Char 的列表。当尝试以下操作时,我们可以看到这种不匹配

.> ["c","a","t"] == "cat"
error:
* Couldn't match type `Char' with `[Char]'
  Expected type: [[Char]]
  Actual type: [Char]

我们可以看到 ["c", "a", "t"] 是一个类型为 Char列表的列表,方法是:

.> ["c","a","t"] == [['c'],['a'],['t']]
True

列表 "c" 前置到列表 ["a", "t"] 很好,因为前置项目的类型(类型为 Char列表)与它所前置到的列表项的类型匹配,即类型为 Char 的列表

.> "c" : ["a", "t"]
["c", "a", "t"]

当将类型为 Char 的项目 'c' 前置到类型为 Char 的列表 ['a', 't'] 时,我们也可以这样做

.> 'c' : ['a', 't']
"cat"

但尝试将类型为 Char 的项目 'c' 前置到类型为 List Char 的列表 ["a", "t"] 将不起作用。此处的错误消息显示了类型 Char 和类型为 Char列表之间的区别,在错误消息中由 [Char] 定义

.> 'c' : ["a", "t"]
* Couldn't match expected type `Char' with actual type `[Char]'
* In the expression: "a"
  In the second argument of `(:)', namely `["'a", "t"]'
  In the expression: 'c' : ["'a", "t"]

追加 (++) 一个项目或列表到另一个列表,即在末尾添加某些内容,可能是一个项目或另一个列表

.> myList = [20,3,51,6,3,7]
.> myList ++ 99
[20, 3, 51, 6, 3, 7, 99]
.> myList ++ [5, 4, 3, 2]
[20, 3, 51, 6, 3, 7, 5, 4, 3, 2]

前置命令 (:) 只能允许您将单个元素添加到列表的前面。如果您想将一个列表添加到另一个列表的前面,您可以使用追加 (++) 命令

.> listA = ["x", "y", "z"]
.> listB = ["d", "c"]
.> listA ++ ["e"]
["x", "y", "z", "e"]
.> listA ++ listB
["x", "y", "z", "d", "c"]

您可以使用追加和前置的组合来组合多个列表和项目

.> "hello" ++ " " ++ "world"
"hello world"
.> '!' : "shocking" ++ "this" ++ ['w', 'o', 'r', 'k', 's'] ++ "!"
"!shockingthisworks!"
练习:列表

以下 Haskell 代码输出什么

myList = [3,4,5,3,4,6]
head(tail(tail(myList)))

答案

5


以下 Haskell 代码输出什么

myList = ['a', 'b', 'c', 'd']
tail(myList)

答案

['b', 'c', 'd'] 或 "bcd"


以下 Haskell 代码输出什么

myList = ["cat", "mouse", "moose", "cow", "sheep"]
length(tail(tail(tail(myList))))

答案

2


以下 Haskell 代码输出什么

myList = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]
null(head(tail(tail(myList))))
length(tail(head(tail(tail(myList ++ ["black", "white"])))))

答案


错误
5

请注意,当我们获取包含字符串的单个元素列表的尾部时,它会将字符串视为一个列表。


给定一个名为 containsPrime 的列表,其值为 [6,8,9,7,15,21],仅使用头部和尾部命令,编写一些 Haskell 代码来返回数字 7

答案


head(tail(tail(tail(containsPrime))))



给定一个名为 someList 的列表,其值为 ["badger", "fish", "vole"],仅使用 headtail 和追加 (++) 命令,编写一些 Haskell 代码来返回列表 ["vole", "frog"]

答案


let someList = ["badger", "fish", "vole"]
tail(tail(someList ++ ["frog"]))


以下代码做什么

.> listA = [1, 2, 3]
.> listB = [9, 8]
.> listA ++ tail(listB)
.> tail(tail(listA) ++ tail(listB))

答案


[1,2,3,8]
[3,8]


以下代码做什么

.> listA = ['o', 'x', 'p', 'e']
.> 'r' : [head(listA)] ++ tail(tail(listA))

答案


"vape" 或者 ['r', 'o', 'p', 'e']


华夏公益教科书