Haskell为列表添加了列表的唯一组合

时间:2014-09-17 21:06:29

标签: list haskell list-comprehension

比如说我有一个像这样的列表

list = ["AC", "BA"]

我想将此列表的每个唯一组合添加到元组中,因此结果如下:

[("AC", "AC"),("AC","BA"),("BA", "BA")]

其中("BA","AC")被排除在外。

我的第一种方法是使用这样的列表理解:

ya = [(x,y) | x <- list, y <- list]

但我无法让它发挥作用,无论如何通过使用列表推导来实现我的结果?

3 个答案:

答案 0 :(得分:8)

我首选的解决方案使用列表理解

f :: [t] -> [(t, t)]
f list = [ (a,b) | theTail@(a:_) <- tails list , b <- theTail ]

我觉得这很可读:首先你选择(非确定性地)后缀theTail,从a开始,然后你选择(非确定性地)一个元素{{1}后缀。最后,生成了b对,它明显超出了所需的对。

它也应该是最佳效率的:每当你需要一个元素时,就会产生恒定的时间。

答案 1 :(得分:7)

ThreeFx的答案将起作用,但它会增加您必须可订购元素的约束。相反,您可以使用PreludeData.List中的函数来更有效,更通用地实现此功能:

import Data.List (tails)

permutations2 :: [a] -> [(a, a)]
permutations2 list
    = concat
    $ zipWith (zip . repeat) list
    $ tails list

它不使用列表推导,但它无需执行可能代价高昂的比较,也不会对可以通过它进行何种类型的值进行任何限制。


要了解其工作原理,请考虑如果您拥有列表[1, 2, 3],那么您就拥有了这些组

[(1, 1), (1, 2), (1, 3),
         (2, 2), (2, 3),
                 (3, 3)]

这相当于

[(1, [1, 2, 3]),
 (2,    [2, 3]),
 (3,       [3])]

因为它不包含任何额外或更少的信息。从此表单到我们所需输出的转换是将函数f (x, ys) = map (\y -> (x, y)) ys映射到每个元组,然后将concat映射到一起。现在我们只需要弄清楚如何获得这些元组的第二个元素。很明显,我们看到它所做的一切都是从列表前面删除连续的元素。幸运的是,tails中的Data.List函数已经为我们实现了这一点。每个元组中的第一个元素只构成原始列表,因此我们知道可以使用zip。最初,您可以使用

实现此功能
> concatMap (\(x, ys) -> map (\y -> (x, y)) ys) $ zip list $ tails list

但我个人更喜欢zip,所以我将内部函数转换为不必使用lambdas的函数:

> concatMap (\(x, ys) -> zip (repeat x) ys) $ zip list $ tails list

由于我更喜欢​​zipWith f而不是map (uncurry f) . zip,我将此转为

> concat $ zipWith (\x ys -> zip (repeat x) ys) list $ tails list

现在,我们可以进一步减少这种情况:

> concat $ zipWith (\x -> zip (repeat x)) list $ tails list
> concat $ zipWith (zip . repeat) list $ tails list

感谢eta减少和功能组成。

我们可以把它完全放在哪里
> permutations2 = concat . ap (zipWith (zip . repeat)) tails

但我发现这很难阅读和理解,所以我认为我会坚持使用以前的版本。

答案 2 :(得分:3)

只需使用列表理解:

f :: (Ord a) => [a] -> [(a, a)]
f list = [ (a, b) | a <- list, b <- list, a <= b ]

由于Haskell的String位于Ord类型类中,这意味着它可以被排序,因此首先告诉Haskell获取所有可能的组合,然后排除{{1}中的每个组合。 }大于b,删除所有&#34;重复&#34;组合

示例输出:

a