在 Haskell 中,将元组用作通用的可遍历容器不被认为是惯用的(也不是真的可能)。您处理的任何元组都将具有固定数量的元素,这些元素的类型也是固定的。 (这与元组在例如 Python 中的惯用用法完全不同。)您询问“输入作为元组给出”的情况,但如果输入将具有灵活数量的元素,那么它肯定不会作为元组给出——列表是一个更有可能的选择。
这使得元组看起来不像其他一些语言那样灵活。好处是您可以使用模式匹配来检查它们。例如,如果您想为元组的每个元素评估某个谓词并返回True,如果谓词对所有元素都通过,您可以编写类似
all2 :: (a -> Bool) -> (a, a) -> Bool
all2 predicate (x, y) = predicate x && predicate y
或者,对于三元素元组,
all3 :: (a -> Bool) -> (a, a, a) -> Bool
all3 predicate (x, y, z) = predicate x && predicate y && predicate z
您可能会想,“等等,您需要为每个元组大小设置一个单独的函数吗?!”是的,你知道了,你可以开始明白为什么元组的用例和列表的用例之间没有太多的重叠。元组的优点正是它们有点不灵活:你总是知道它们包含多少个值,以及这些值有什么类型。前者不适用于列表。
没有办法遍历一个元组吗?
据我所知,没有内置的方法可以做到这一点。写下遍历 2 元组、遍历 3 元组等的指令很容易,但这会有很大的限制,即您只能处理元素都具有相同元素的元组输入。
以map 函数为例。只要您有函数a -> b,您就可以将map 应用于[a] 类型的列表。在这种情况下,map 依次查看每个a 值,将其传递给函数,并组装得到的b 值列表。但是对于一个元组,你可能有三个元素的值都是不同的类型。如果元组由两个a 值和一个c 组成,则将as 转换为bs 的函数是不够的!如果您尝试开始写下 Foldable 实例或 Traversable 实例,即使只是针对二元素元组,您很快就会意识到这些类型类并非旨在处理其值可能具有不同类型的容器。
是否可以将元组中的值提取到列表中?
是的,但是对于输入元组的每个可能大小,您都需要一个单独的函数。例如,
tupleToList2 :: (a, a) -> [a]
tupleToList2 (x, y) = [x, y]
tupleToList3 :: (a, a, a) -> [a]
tupleToList3 (x, y, z) = [x, y, z]
当然,好消息是您永远不会遇到必须处理任意大小的元组的情况,因为这在 Haskell 中是不可能发生的。想想接受任意大小元组的函数的类型签名:你怎么写?
在您接受元组作为输入的任何情况下,可能都没有必要先将元组转换为列表,因为模式匹配语法意味着您可以单独处理元组的每个元素 - 而且您总是知道会有多少这样的元素。