生成 Haskell 中一组布尔变量的所有组合

Generating all the combinations of a set of boolean variables in Haskell

我正试图在 haskell 中绕过列表单子。给定布尔变量的字符串列表,我试图生成所有可能命题的列表。

例如调用:

mapM_ print $ allPropositions ["a","b"]

会产生以下结果:

[("a",True),("b",True)]
[("a",True),("b",False)]
[("a",False),("b",True)]
[("a",False),("b",False)]

我已经通过以下代码使用列表推导和递归成功做到了

allPropositions :: [String] -> [[(String,Bool)]]
allPropositions [] = [[]]
allPropositions (x:xs) = [(x,True):r | r <- allPropositions xs] ++ [(x,False):r | r <- allPropositions xs]

我一直在寻找一种使用类似于以下代码段但输入数量可变的 do 表示法来完成此操作的方法。有没有办法做到这一点(嵌套的单子,...)?

allPropositions' = do
    a <- [True, False]
    b <- [True, False]
    return([("a",a),("b",b)])

你需要的是sequence :: Monad m => [m a] -> m [a].

特别是,对于 [] monad,sequence 获取 n 个列表的列表,并生成所有 n 长度的列表,从每个列表中抽取一个元素一次。

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

这对您的特定情况有帮助,因为如果您有一个 n 字符串列表,您可以轻松地为每个字符串生成可能性:

map (\s -> [(s,True), (s,False)] ["a", "b", "c"] = 
   [ [("a", True), ("a", False) ]
   , [("b", True), ("b", False) ]
   , [("c", True), ("c", False) ]
   ]

现在你只需要从每个列表中选择一个来让你的命题对每个变量都有一个真值:

sequence (map (\s -> [(s,True), (s,False)] ["a", "b", "c"]) = 
   [ [("a", True), ("b", True), ("c", True)]
   , [("a", True), ("b", True), ("c", False)]
   , [("a", True), ("b", False), ("c", True)]
   , [("a", True), ("b", False), ("c", False)]
   , [("a", False), ("b", True), ("c", True)]
   , [("a", False), ("b", True), ("c", False)]
   , [("a", False), ("b", False), ("c", True)]
   , [("a", False), ("b", False), ("c", False)]
   ]

sequence (map f xs) 经常出现,因此有一个名字:

mapM f xs = sequence (map f xs)
-- or, point-free style
mapM f = sequence . map f

所以你想要的功能就是

allPropositions vs = mapM (\v -> [(v,True),(v,False)]) vs
-- or, equivalently
allPropositions = mapM (\v -> [(v,True),(v,False)])
-- or, equivalently
allPropositions = mapM $ \v -> [(v,True),(v,False)]
-- or, equivalently, with -XTupleSections
allPropositions = mapM $ \v -> map (v,) [True, False]

我会这样做:

allPropositions :: [a] -> [[(a, Bool)]]
allPropositions = foldr (\x xs -> (:) <$> [(x,True),(x,False)] <*> xs) [[]]

您根本不需要 monad 的全部功能。您只需要 applicative functors.

情况如下:

  1. 对于基本情况,结果是 [[]](即 allPropositions [] = [[]])。
  2. 对于归纳情况,结果是 ⟦(:) [(x,True),(x,False)] xs⟧。请注意,双方括号(即 ⟦⟧)表示应用函子的上下文(在本例中为 [])。

虽然 rampion 的答案是正确的,但它使用了 sequencemapM 这两个单子函数。但是,正如我之前所说,在这种情况下您不需要 monad 的全部功能。

如果你不想使用 monad 的全部功能,你仍然可以使用 sequenceA