Haskell:如何返回列表的可能拆分列表

时间:2013-02-20 10:24:43

标签: list haskell

我正在尝试编写一个名为split的函数,该函数接受一个列表并返回所有不同可能性的对的列表以对其进行分区,例如

split [4,3,6] = [([],[4,3,6]),([4],[3,6]),([4,3],[6]),([4,3,6],[])]

现在我写了这个

split :: [a] -> [([a],[a])]
split []     = [([],[])]
split (x:xs) = ([],(x:xs)):(zip (map (x:) (map fst split(xs))) (map snd split(xs)))

一段代码和Hugs以及我选择的解释器让我这个

ERROR file:.\split.hs:3 - Type error in application
*** Expression     : map snd split xs
*** Term           : map
*** Type           : (e -> f) -> [e] -> [f]
*** Does not match : a -> b -> c -> d

错误消息。我到底做错了什么?为什么(map snd split xs)的类型为
(a-> b-> c-> d)?

2 个答案:

答案 0 :(得分:10)

你错了你的parens。尝试

split (x:xs) = ([],(x:xs)):(zip (map (x:) (map fst (split xs))) (map snd (split xs)))

Haskell不像C和Java那样使用括号进行函数调用。当您编写map fst split(xs)时,这与map fst split xs相同,即编译器认为您尝试使用三个参数调用map。因此,您需要像split这样对map fst (split xs)的调用进行父母代理。

您实际编写的内容是列表的简单zipper。实现它的最简单方法是

import Data.List (inits, tails)

split xs = zip (inits xs) (tails xs)

答案 1 :(得分:6)

这是另一种定义:

splits :: [a] -> [(a, a)]
splits xs = map (flip splitAt xs) [0 .. length xs]

不可否认,这不是很有效,但至少它简洁: - )

使用来自inits的{​​{1}}和tails的另一个版本更短,可能效率更高:

Data.List

现在让我们玩得开心吧。我们可以将splits :: [a] -> [(a, a)] splits xs = zip (inits xs) (tails xs) inits写为tails s,我们使用foldrinitsA来表示所谓的代数折叠:

tailsA

使用这些代数,我们可以进一步组合它们:

inits :: [a] -> [[a]]
inits = foldr initsA [[]]

initsA :: a -> [[a]] -> [[a]]
initsA x xss = [] : map (x:) xss

tails :: [a] -> [[a]]
tails = foldr tailsA [[]]

tailsA :: a -> [[a]] -> [[a]]
tailsA x xss = (x : head xss) : xss

现在我们将splits :: [a] -> [([a], [a])] splits = foldr splitsA [([], [])] splitsA :: a -> [([a], [a])] -> [([a], [a])] splitsA xy xyss = zip (initsA xy xss) (tailsA xy yss) where (xss, yss) = unzip xyss 定义为单个splits