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