我想创建一个简单的(涉及集和列表)功能,可以执行以下操作,我不知道从哪里开始。
split:: [(a,b)] -> ([a],[b])
答案 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中完成。
请查看fst
和snd
,它们分别取出一对中的第一个/第二个元素。
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
所以基本上是一样的! (但正如您所看到的,我们拥有的最后一个版本效率更高。)