从配对列表到配对列表的Haskell

时间:2013-12-02 18:01:00

标签: haskell

我想创建一个简单的(涉及集和列表)功能,可以执行以下操作,我不知道从哪里开始。

split:: [(a,b)] -> ([a],[b])

2 个答案:

答案 0 :(得分:6)

让我们一步一步来。该函数的两种情况是:

split [] = ???
split ((a,b):ps) = ???

一个案例很容易。

split [] = ([], [])

对于另一个,我们必须递归地使用该函数,无论如何

split ((a,b):ps) = ???? where
                    (as, bs) = split ps

我认为很容易看出解决方案是

split ((a,b):ps) = (a:as, b:bs) where
                     (as, bs) = split ps

答案 1 :(得分:2)

除了Guido的解决方案之外,还有不止一种方法可以在haskell中完成。

请查看fstsnd,它们分别取出一对中的第一个/第二个元素。

GHCi> :t fst
fst :: (a, b) -> a
GHCi> :t snd
snd :: (a, b) -> b

如果你正在使用函数式编程语言,你应该熟悉map,它需要一个函数和一个列表,将函数应用于该列表的每个元素,并在另一个列表中提供所有结果:

GHCi> :t map
map :: (a -> b) -> [a] -> [b]

给定一对对列表,你需要两个列表,一个按顺序包含所有第一个元素,另一个包含所有第二个元素:

GHCi> let split xs = (map fst xs, map snd xs)
GHCi> split [(1,2),(3,4),(5,6)]
([1,3,5],[2,4,6])
GHCi> 

正如@jozefg在评论中指出的那样,这个方法没有像@Guido那样有效,但我们可以做一些改进来改进它(这正是@Guido的解决方案) :

现在是时候看一下map的实施方式here

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

所以我们可以尝试稍微更改split

我们仍然需要基本情况​​,(例如,如果xs为空):

split [] = ([], [])
split ls = (map fst ls, map snd ls) -- attention!

我们将列表分为头部和尾部,就像map

一样
split (x:xs) = (fst x: map fst xs, snd x: map snd xs)

现在我们可以进行模式匹配(a,b) = x,所以我们不必调用两个单独的函数来将一对分成两个:

split (x:xs) = (a: map fst xs, b: map snd xs)
    where (a,b) = x

将此处的代码与我评论“注意!”的行进行比较,您是否意识到如果我们知道(map fst xs, map snd xs)的结果,我们可以简单地重用该结果以加快速度。幸运的是,我们已经有split ls = (map fst ls, map snd ls)

利用这个事实,我们终于拿出了这个版本:

split [] = ([], [])
split (x:xs) = (a:as , b:bs)
    where (a,b)   = x
          (as,bs) = split xs

所以基本上是一样的! (但正如您所看到的,我们拥有的最后一个版本效率更高。)