Haskell:列表操作

Ron*_*ese 1 haskell list

我想编写一个接受输入列表并按以下方式操作的函数:

步骤1:将列表的第一个元素和列表的最后一个元素放在一个子列表中。

步骤2:获取列表的第二个元素和列表的倒数第二个元素,并将其放到下一个子列表中。

步骤3:获取列表的第三个元素和列表的倒数第三个元素,并将其放到下一个子列表中。

根据相同的方案继续此操作(获取n个元素的列表)...

如果输入列表的元素数为奇数,则将输入列表的n / 2个元素添加为输出列表的最后一个子列表。

例:

[1,2,3,4,5,6,7]

-- should be transformed to

[[1,7],[2,6],[3,5],[4]]
Run Code Online (Sandbox Code Playgroud)

我已经编写了一个函数,该函数接受列表的每两个元素并将其放到子列表中,我想知道这段代码是否可以帮助我解决上述问题:

g2 :: [a] -> [[a]]
g2 [] = []
g2 (x1:x2:xs) = [x1,x2]: g2 xs
g2 xs = [xs]
Run Code Online (Sandbox Code Playgroud)

ois*_*sdk 6

这是一次完成的操作:

pairs :: [a] -> [[a]]
pairs xs = fst (go xs xs) where
  go (x:xs) (_:_:ys) = f x (go xs ys)
  go (x:xs) [_]      = ([[x]],xs)
  go xs     []       = ([],xs)

  f x (xs,y:ys) = ([x,y]:xs,ys)
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?让我们看一下go first的前两个参数,尤其是这一行:

  go (x:xs) (_:_:ys) = f x (go xs ys)
Run Code Online (Sandbox Code Playgroud)

这两个参数都来自同一个列表(xs),但是我们从一项中减去了2项,而从另一项中减去了一项。为什么?因此,我们知道何时到达中间点。查看此函数进行比较:

halfway xs = go xs xs
  where
    go (_:xs) (_:_:ys) = go xs ys
    go xs _ = xs

>>> halfway [1..6]
[4,5,6]
Run Code Online (Sandbox Code Playgroud)

现在,一旦到达中点,我们需要将其与其他列表“压缩”在一起。但这需要相反!我们如何做到这一点?一种轻松地一次反转任何功能的方法是先将其写为折叠。这是折叠的拉链:

zip = foldr (\x k (y:ys) -> (x,y) : k ys) (const [])
Run Code Online (Sandbox Code Playgroud)

要“反转”它,您只需应用“ foldl而不是” foldr(也必须翻转闭包)。

对于我们的使用,我们基本上会在构建过程中建立基础(以的形式k)。所以没有我们的函数看起来像这样:

pairs :: [a] -> [[a]]
pairs xs = go xs xs (const []) where
  go (y:ys) (_:_:zs) k = go ys zs (f y k)
  go (y:ys) [_]      k = [y] : k ys
  go ys     []       k = k ys

  f x k (y:ys) = [x,y] : k ys -- same `f` as from `zip`
Run Code Online (Sandbox Code Playgroud)

仍然存在一个问题:列表以错误的顺序返回。为了解决这个问题,我们用差异列表替换列表,并交换追加的顺序。

最后,我们取消对CPS的功能,然后得到了上面的内容。