7

我想转换[1,2,3,4][[1 2] [2 3] [3 4]]or [(1 2) (2 3) (3 4)]。在clojure中我有(partition 2 1 [1,2,3,4]). 我怎样才能在haskell中做到这一点?我怀疑标准api中有这样的功能,但我找不到。

4

3 回答 3

21

这样做的标准技巧是zip使用它自己的列表tail

> let xs = [1,2,3,4] in zip xs (tail xs)
[(1,2),(2,3),(3,4)]

要了解为什么会这样,请在视觉上排列列表及其尾部。

      xs = 1 : 2 : 3 : 4 : []
 tail xs = 2 : 3 : 4 : []

请注意,这zip是从每一列中创建一个元组。

为什么这样做总是正确的还有两个更微妙的原因:

  • zip当任一列表用完元素时停止。这是有道理的,因为我们最后不能有一个“不完整的对”,而且它还确保我们不会从单个元素列表中得到任何对。
  • xs为空时,可能会tail xs引发异常。然而,因为zip 首先检查它的第一个参数,当它看到它是空列表时,第二个参数永远不会被评估。

上述所有内容也适用于zipWith,因此当您需要将函数成对应用于相邻元素时,您可以使用相同的方法。

对于像 Clojure 这样的通用解决方案partition,标准库中没有任何内容。但是,您可以尝试这样的事情:

partition' :: Int -> Int -> [a] -> [[a]]
partition' size offset
  | size <= 0   = error "partition': size must be positive"
  | offset <= 0 = error "partition': offset must be positive"
  | otherwise   = loop
  where
    loop :: [a] -> [[a]]
    loop xs = case splitAt size xs of
                -- If the second part is empty, we're at the end. But we might
                -- have gotten less than we asked for, hence the check.
                (ys, []) -> if length ys == size then [ys] else []
                (ys, _ ) -> ys : loop (drop offset xs)
于 2013-05-26T17:50:25.317 回答
1

只是用不同的方法抛出另一个答案:

对于 n=2,您只想简单地zip列出带有尾部的列表。对于 n=3,您希望使用它的尾部和尾部的尾部压缩列表。这种模式继续下去,所以我们要做的就是概括它:

partition n = sequence . take n . iterate tail

但这仅适用于偏移量 1。要概括偏移量,我们只需查看生成的列表。它将始终具有以下形式:

[[1..something],[2..something+1],..]

所以剩下要做的就是选择每一个offset元素,我们应该没问题。我无耻地从这个问题中从@ertes 那里偷了这个版本:

everyNth :: Int -> [a] -> [a]
everyNth n = map head . takeWhile (not . null) . iterate (drop n)

整个函数现在变为:

partition size offset = everyNth offset . sequence . take size . iterate tail
于 2016-08-02T07:27:15.240 回答
0

有时最好自己动手。递归函数赋予了 LisP 的力量和吸引力。Haskell 试图阻止它们,但通常最好使用递归函数来实现解决方案。它们通常非常简单,就像这个产生对一样。Haskell 模式匹配减少了代码。这可以通过仅将模式更改为 (x:y:yys) 来生成 (a,b)、(c,d)、(e,f) 来轻松更改。

> prs (x:yys@(y:_)) = (x,y):prs yys
> prs "abcdefg"
[('a','b'),('b','c'),('c','d'),('d','e'),('e','f'),('f','g')
于 2021-05-19T21:55:23.000 回答