笛卡尔积在Haskell中的列表列表中

时间:2015-09-09 09:37:59

标签: haskell functional-programming combinatorics

给定长度为x的列表列表,其中所有子列表具有相同的长度y,输出长度为y^x的{​​{1}}列表,每个列表包含一个项目子列表。

示例(xx = 3):

y = 2

输出([ [1, 2], [3, 4], [5, 6] ] 不同的输出):

2^3 == 8

我的研究/工作

红宝石

我编写了实际代码来执行此任务,但在Ruby中,因为它是我最熟悉的语言。

[ [1, 3, 5], [1, 4, 5], [1, 3, 6], [1, 4, 6],
  [2, 3, 5], [2, 4, 5], [2, 3, 6], [2, 4, 6] ]

类型

输入是包含类型a的项目的列表列表,输出也是如此。

def all_combinations(lst)
   lst.inject {|acc, new| acc.product(new).map(&:flatten) }
end

笛卡尔积,扁平化和折叠

查看我的Ruby解决方案让我觉得充分利用这些功能可能足以解决问题。问题是,虽然笛卡尔积输出了一个元组列表,但我需要一个列表列表。

1 个答案:

答案 0 :(得分:12)

注意:这篇文章是用文字Haskell编写的。安全吧 * .lhs并将其加载到GHCi中。

> -- remove this line if you don't have QuickCheck installed
> import Test.QuickCheck 

一个简单的递归变体

让我们从allProduct的简单变体开始:

> allProductFirst :: [[a]] -> [[a]]
> allProductFirst []     = [[]]
> allProductFirst (x:xs) =

现在x本身又是一个列表。让我们说allProduct xs会给我们其他列表的产品。

>    let rest = allProductFirst xs

我们需要做什么?我们需要为x中的每个元素创建一个新列表,并将它们连接在一起:

>    in concatMap (\k -> map (k:) rest) x

请注意,此变体不是100%正确,因为allProduct [][[]]

一元变体

如果我们要使用Monad的{​​{1}}实例,这会是什么样子?

使用[]表示法

do

我们想要采用> allProduct' :: [[a]] -> [[a]] > allProduct' [] = [[]] > allProduct' (x:xs) = do

的每个元素
x

并将其列入我们列表可能具有的所有可能后缀:

>      elementOfX <- x

这意味着我们基本上会评估列表monad中的每个操作。但是有一个功能:> rest <- allProduct' xs > return $ elementOfX : rest 。如果我们使用sequence :: Monad m => [m a] -> m [a],则其类型可以专门用于m ~ []

使用sequence :: [[a]] -> [[a]]

我们最终得到了我们的最后一个变体:

sequence

测试结果

我们使用QuickCheck测试它可能与> allProduct :: [[a]] -> [[a]] > allProduct = sequence 相同 和allProductFirst

allProduct'

在GHCi中使用> main :: IO () > main = do > quickCheck $ > forAll (choose (1,8)) $ \n -> -- number of lists > forAll (choose (1,4)) $ \s -> -- number of elements per list > forAll (vectorOf n $ vector s) $ \xs -> > allProduct' xs === allProduct (xs :: [[Integer]]) .&. > allProductFirst xs === allProduct xs :main或编译并运行您的程序 最终应该通过100次测试。