【问题标题】:Manipulating Tuples in Haskell在 Haskell 中操作元组
【发布时间】:2020-10-13 13:29:29
【问题描述】:

我是 Haskell 的新手,我有一个关于元组的问题。没有办法遍历元组吗?我知道使用列表进行遍历非常容易,但是如果输入作为元组给出,是否没有办法像使用列表一样检查整个元组?如果不是这种情况,是否可以将元组中的值提取到列表中并以这种方式执行遍历?

【问题讨论】:

  • 是的,(,) 是一个 Bitraversable 实例。
  • 下面的答案非常好,基本上说“不”。我们可以说得更多,但要做好它,我们需要了解更多,具体来说:你有什么周围的代码让你认为元组是正确的选择,而遍历是你想要的东西?跨度>
  • 嗯,这更像是一个我想问的一般性问题。如果这是一个要求,我想更详细地探索这个想法。

标签: haskell tuples


【解决方案1】:

在 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 中是不可能发生的。想想接受任意大小元组的函数的类型签名:你怎么写?

在您接受元组作为输入的任何情况下,可能都没有必要先将元组转换为列表,因为模式匹配语法意味着您可以单独处理元组的每个元素 - 而且您总是知道会有多少这样的元素。

【讨论】:

  • 我明白了,嗯。因此,对于我的第二部分问题,如果您在一个元组中说 5 个元素,那么将它们全部提取到一个列表中并不容易?我知道使用 fst 和 snd 但是对于更大的元组,我想实现它会更困难,或者我错了。
  • @Karna 查看我刚刚进行的编辑。您总是提前知道元组的大小,因此您可以根据需要将其转换为列表,但是不可能编写一个可以作用于任意不同大小的元组的函数。请注意,即使像fst 这样的简单函数也只能作用于固定大小的元组(两个元素,在fstsnd 的情况下)。
  • 非常感谢。这非常有用。也许我看错了,但在你回答之前它真的没有点击我,所以非常感谢。
  • @Karna 乐于助人!
  • 提一下 Pair Identity IdentitySized Vector 这样的东西可能会很有趣,它们是类似元组的实体(因为它们的大小是静态已知的)但保证所有元素都具有相同的类型(并且Traversable 的实例也是如此。
【解决方案2】:

如果你的元组是同构元组,并且你不介意使用第三方包,那么lens提供了一些函数来遍历任意元组中的每个元素。

ghci> :m +Control.Lens
ghci> over each (*10) (1, 2, 3, 4, 5)   --traverse each element
(10,20,30,40,50)

Control.Lens.Tuple 提供了一些镜头来获取和设置第 n 个元素到第 19 个。

您可以浏览more informationlens 包。如果你想学习 lens 软件包,Chris Penner 的 Optics by examples 是一本好书。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-18
    • 2021-01-03
    • 1970-01-01
    • 2013-05-17
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多