Haskell列表理解 - 所有列表拆分列表

Mar*_*oma 3 haskell list

我只是想写一个函数splits,它接受一个列表l并返回一个包含所有可能的拆分方法的元组列表l.

所以它应该像这样工作:

splits "Hello"
[("","Hello"),("H","ello"),("He","llo"),("Hel","lo"),("Hell","o"),("Hello","")]
Run Code Online (Sandbox Code Playgroud)

实施1我写的是:

splits l = [(x,y) | i <- [0 ..length l], x <- take i l, y <- drop i l]
Run Code Online (Sandbox Code Playgroud)

这使

[('H','e'),('H','l'),('H','l'),('H','o'),('H','l'),('H','l'),('H','o'),
 ('e','l'),('e','l'),('e','o'),
 ('H','l'),('H','o'),
 ('e','l'),('e','o'),
 ('l','l'),('l','o'),
 ('H','o'),('e','o'),('l','o'),('l','o')]
Run Code Online (Sandbox Code Playgroud)

实施2正确的解决方案是

splits l = [(take i l, drop i l) | i <- [0 ..length l]]
Run Code Online (Sandbox Code Playgroud)

问题:为什么实现1和实现2做不同的事情?实施1中发生了什么?

eps*_*lbe 5

关键的观察是该声明x <- list在第一个版本中的作用.让我们看一下不同的例子

[i | i <-[1..3]] => [1,2,3]
Run Code Online (Sandbox Code Playgroud)

因为String = [Char]

[c | c <- "Word"] => "Word" or equivalently ['W','o','r','d']
Run Code Online (Sandbox Code Playgroud)

所以我们可以纠正你的第一个版本一个小小的一点,然后得到后者

splits l = [(x,y) | i <- [0 ..length l], x <- [take i l], y <- [drop i l]]
Run Code Online (Sandbox Code Playgroud)

但我仍然不得不说这是相当单一的,在我眼中更好的解决方案是使用递归函数.

splits :: [a] -> [([a],[a])]
splits xx = splits' [] ([],xx)
  where splits' :: [([a],[a])]-> ([a],[a]) -> [([a],[a])]
        splits' acc xs@(_,[]) = reverse (xs:acc)
        splits' acc (xs,y:ys) = let xs' = (xs++[y],ys)
                                in splits' (xs':acc) xs'
Run Code Online (Sandbox Code Playgroud)

或具有更高阶的功能

splits :: [a] -> [([a],[a])]
splits xx = zipWith splitAt [0..(length xx)] (repeat xx)
Run Code Online (Sandbox Code Playgroud)