我是 Haskell 的新手,我有一个关于元组的问题。没有办法遍历元组吗?我知道使用列表遍历非常容易,但是如果输入是作为元组给出的,那么是否没有办法像使用列表一样检查整个元组?如果不是这种情况,是否可以将元组中的值提取到列表中并以这种方式执行遍历?
在 Haskell 中,将元组用作通用的可遍历容器不被认为是惯用的(也不是真的可能)。您处理的任何元组都将具有固定数量的元素,这些元素的类型也是固定的。(这与元组在例如 Python 中惯用的方式完全不同。)您询问“输入作为元组给出”的情况,但如果输入将具有灵活数量的元素,那么它肯定不会作为元组给出——列表是一个更有可能的选择。
这使得元组看起来不如其他一些语言灵活。好处是您可以使用模式匹配来检查它们。例如,如果您想为元组的每个元素评估某个谓词,True并在所有这些元素都通过该谓词时返回,您可以这样写
all2 :: (a -> Bool) -> (a, a) -> Bool
all2 predicate (x, y) = predicate x && predicate y
Run Code Online (Sandbox Code Playgroud)
或者,对于三元素元组,
all3 :: (a -> Bool) -> (a, a, a) -> Bool
all3 predicate (x, y, z) = predicate x && predicate y && predicate z
Run Code Online (Sandbox Code Playgroud)
您可能会想,“等等,您需要为每个元组大小设置一个单独的函数?!” 是的,您确实这样做了,并且您可以开始了解为什么元组用例和列表用例之间没有很多重叠。元组的优点是准确,他们是那种不灵活:你总是知道他们有多少值包含,以及什么类型的值有。前者对于列表来说并不是真的。
没有办法遍历元组吗?
据我所知,没有内置的方法可以做到这一点。写下遍历 2 元组、遍历 3 元组等等的指令是很容易的,但这会有很大的限制,你只能处理元素都相同的元组类型。
将该map函数视为一个简单示例。只要你有一个函数,你就可以申请map一个类型的列表。在这种情况下,依次查看每个值,将其传递给函数,然后组合结果值列表。但是对于元组,您可能拥有三个值都是不同类型的元素。如果元组由两个值和 a组成,则将 s转换为s 的函数是不够的。如果您尝试开始写下实例或实例,甚至只是为二元素元组写下实例,您很快就会意识到这些类型类并非旨在处理其值可能具有不同类型的容器。[a]a -> bmapababacFoldableTraversable
是否可以仅将元组中的值提取到列表中?
是的,但是对于输入元组的每个可能大小,您都需要一个单独的函数。例如,
tupleToList2 :: (a, a) -> [a]
tupleToList2 (x, y) = [x, y]
tupleToList3 :: (a, a, a) -> [a]
tupleToList3 (x, y, z) = [x, y, z]
Run Code Online (Sandbox Code Playgroud)
当然,好消息是您永远不会遇到必须处理任意大小的元组的情况,因为这在 Haskell 中不会发生。想想接受任意大小元组的函数的类型签名:你怎么写?
在您接受元组作为输入的任何情况下,可能没有必要先将元组转换为列表,因为模式匹配语法意味着您可以单独处理元组的每个元素——而且您总是确切地知道会有多少这样的元素。
如果你的元组是同构元组,并且你不介意使用第三方包,那么lens提供一些函数来遍历任意元组中的每个元素。
ghci> :m +Control.Lens
ghci> over each (*10) (1, 2, 3, 4, 5) --traverse each element
(10,20,30,40,50)
Run Code Online (Sandbox Code Playgroud)
Control.Lens.Tuple 提供一些镜头来获取和设置第 n 个元素到第 19 个。
您可以浏览该lens软件包以获取更多信息。如果你想学习这个lens包,Chris Penner 的Optics by examples是一本好书。