【发布时间】:2019-06-16 08:20:02
【问题描述】:
我正在尝试获取列表的所有组合。 [1,2,3] 的结果应该是 [(1,2),(1,3),(2,3)]。然而,我在下面的实现给出了[(1,2),(2,3)]。
parings [d] = []
parings (y:ys) = (y, head ys): parings ys
【问题讨论】:
我正在尝试获取列表的所有组合。 [1,2,3] 的结果应该是 [(1,2),(1,3),(2,3)]。然而,我在下面的实现给出了[(1,2),(2,3)]。
parings [d] = []
parings (y:ys) = (y, head ys): parings ys
【问题讨论】:
9000 的回答中提到的列表理解可以很容易地分解为map 调用。
pairwise :: [a] -> [(a, a)]
pairwise [] = []
pairwise (x:xs) = map (\y -> (x, y)) xs ++ pairwise xs
每个列表理解都可以分解为map、filter 和concatMap 的某种组合,可能还散布着一些let 绑定。这样做是学习如何操作函数的好练习。
【讨论】:
这是使用来自Data.List 的tails 的一种实现:
import Data.List
pairings :: [a] -> [(a, a)]
pairings = concatMap makePairs . tails
where
makePairs [] = []
makePairs (x:xs) = fmap ((,) x) xs
注意事项:
我不知道tails 对你来说算不算“特殊导入”——虽然它不在 Prelude 中,但可以在 base 库中找到,随时可用。
要查看tails 的作用,请尝试tails [1..3]。
((,) x) 只是写(\y -> (x, y)) 的简洁方式。如果你觉得它难看,你可以写更长的版本,或者启用TupleSections扩展并将其拼写为(x,)。
makePairs 可能在没有显式递归的情况下写成...
makePairs = maybe [] (\(x, xs) -> fmap ((,) x) xs) . uncons
...uncons 也可以在 Data.List 中找到。
此处答案中的所有实现都有一个不小的问题:它们将消耗的列表段保留在内存中。要了解我在说什么,请在 GHCi 中运行 head . drop 8000000 $ pairings [1..] 时观察内存使用情况。我对是否有解决方法没有信心——例如,一个简单的concat . tails 似乎遇到了同样的问题,而fmap makePairs . tails 没有(即使head . drop (10^9) . head . drop (10^9) . fmap makePairs . tails $ [1..] 也不会吃掉你所有的记忆)。
【讨论】:
我不知道你为什么反对列表推导;有了它们,解决方案就很简单了:
pairwise :: [a] -> [(a, a)]
pairwise [] = []
pairwise (x:xs) = [(x, y) | y <- xs] ++ pairwise xs
如果需要,您可以通过显式尾递归将理解分解为单独的函数。
(也可以通过保留++ 两边的参数并拥有一个累加器参数来使整个事情成为尾递归。)
【讨论】: