Powerset功能1-Liner

时间:2014-08-24 20:54:21

标签: haskell

Learn You a Haskell演示了powerset函数:

  

某些集合的powerset是该集合的所有子集的集合。

powerset :: [a] -> [[a]]  
powerset xs = filterM (\x -> [True, False]) xs

运行它:

ghci> powerset [1,2,3]                    
[[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[]]

这里发生了什么?我看到filterM的签名(如下所示),但我不明白它是如何执行的。

filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]

请引导我完成此powerset功能。

3 个答案:

答案 0 :(得分:10)

powerset ::                                    [a] -> [[a]]  
powerset xs = filterM (\x -> [True, False])    xs
                             -------------            -----
filterM :: Monad m => (a  -> m Bool       ) -> [a] -> m [a]
-- filter  ::         (a ->    Bool       ) -> [a] ->   [a]   (just for comparison)
                             -------------            -----
                             m Bool ~ [Bool]          m ~ []

所以这是“非确定性(列表)monad中的filter”。

通常,过滤器仅保留谓词所在的输入列表中的那些元素。

不确定性地,我们得到了保留非确定性谓词可能包含的元素的所有可能性,并删除了它可能不存在的元素。在这里,任何元素都是如此,因此我们可以保留或删除元素。

哪个是powerset。


另一个例子(在另一个monad中),建立在评论中提到的Brent Yorgey's blog post中的那个

  >> filterM (\x-> if even x then Just True else Nothing) [2,4..8]
Just [2,4,6,8]
  >> filterM (\x-> if even x then Just True else Nothing) [2..8]
Nothing
  >> filterM (\x-> if even x then Just True else Just False) [2..8]
Just [2,4,6,8]

让我们看看如何通过代码实现这一目标。我们将定义

filter_M :: Monad m => (a -> m Bool) -> [a] -> m [a]
filter_M p []     = return []
filter_M p (x:xs) = p x >>= (\b ->
                if b
                    then filter_M p xs >>= (return . (x:))
                    else filter_M p xs )

写出列表monad的return定义和bind(>>=)(即return x = [x]xs >>= f = concatMap f xs),这就变成了

filter_L :: (a -> [Bool]) -> [a] -> [[a]]
filter_L p [] = [[]]
filter_L p (x:xs) -- = (`concatMap` p x) (\b->
                  --     (if b then map (x:) else id) $ filter_L p xs )
                  -- which is semantically the same as
                  --     map (if b then (x:) else id) $ ... 
   = [ if b then x:r else r | b <- p x, r <- filter_L p xs ]

因此,

-- powerset = filter_L    (\_ -> [True, False])
--            filter_L :: (a  -> [Bool]       ) -> [a] -> [[a]]
powerset :: [a] -> [[a]]
powerset [] = [[]]
powerset (x:xs) 
   = [ if b then x:r else r | b <- (\_ -> [True, False]) x, r <- powerset xs ]
   = [ if b then x:r else r | b <- [True, False], r <- powerset xs ]
   = map (x:) (powerset xs) ++ powerset xs    -- (1)
   -- or, with different ordering of the results:
   = [ if b then x:r else r | r <- powerset xs, b <- [True, False] ]
   = powerset xs >>= (\r-> [True,False] >>= (\b-> [x:r|b] ++ [r|not b]))
   = powerset xs >>= (\r-> [x:r,r])
   = concatMap (\r-> [x:r,r]) (powerset xs)   -- (2)
   = concat [ [x:r,r] | r <- powerset xs  ]
   = [ s | r <- powerset xs, s <- [x:r,r] ]

因此我们得到了powerset函数的两个常用实现。

由于谓词是常数(const [True, False]),因此可以实现处理的翻转顺序。否则,将对相同的输入值反复评估测试,我们可能不希望这样。

答案 1 :(得分:6)

让我帮你解决这个问题:

  • 首先:您必须了解列表monad 。如果你还记得,我们有:

    do
      n  <- [1,2]  
      ch <- ['a','b']  
      return (n,ch)
    

    结果将是:[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

    因为:xs >>= f = concat (map f xs)return x = [x]

    n=1: concat (map (\ch -> return (n,ch)) ['a', 'b'])
         concat ([ [(1,'a')], [(1,'b')] ]
         [(1,'a'),(1,'b')]
    and so forth ...
    the outermost result will be:
         concat ([ [(1,'a'),(1,'b')], [(2,'a'),(2,'b')] ])
         [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
    
  • :我们实现了filterM:

    filterM _ []     =  return []
    filterM p (x:xs) =  do
        flg <- p x
        ys  <- filterM p xs
        return (if flg then x:ys else ys)
    

    让我们举个例子让您更轻松地理解这个想法:

    filterM (\x -> [True, False]) [1,2,3]
    p is the lambda function and (x:xs) is [1,2,3]
    

    filterM的最内层递归:x = 3

    do
      flg <- [True, False]
      ys  <- [ [] ]
      return (if flg then 3:ys else ys)
    

    你会看到相似之处,就像我们上面的例子一样:

    flg=True: concat (map (\ys -> return (if flg then 3:ys else ys)) [ [] ])
              concat ([ return 3:[] ])
              concat ([ [ [3] ] ])
              [ [3] ]
    and so forth ...
    the final result: [ [3], [] ]
    

    同样地:

    x=2:
      do
        flg <- [True, False]
        ys  <- [ [3], [] ]
        return (if flg then 2:ys else ys)
    result: [ [2,3], [2], [3], [] ]
    x=1:
      do
        flg <- [True, False]
        ys  <- [ [2,3], [2], [3], [] ]
        return (if flg then 1:ys else ys)
    result: [ [1,2,3], [1,2], [1,3], [1], [2,3], [2], [3], [] ]
    
  • 理论上:它毕竟是链接列表monad:

    filterM :: (a -> m Bool) -> [a] -> m [a]
               (a -> [Bool]) -> [a] -> [ [a] ]
    

这就是全部,希望你喜欢:D

答案 2 :(得分:1)

了解列表monad的filterM的最佳方法(如您的示例所示)是考虑以下filterM

伪代码的替代定义。
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
filterM p [x1, x2, .... xn] = do
                  b1 <- p x1
                  b2 <- p x2
                  ...
                  bn <- p xn
                  let element_flag_pairs = zip [x1,x2...xn] [b1,b2...bn]
                  return [ x | (x, True) <- element_flag_pairs]

使用filterM的定义,您可以轻松了解为什么在示例中生成了幂集。

出于完整性考虑,您可能还对如何如上定义foldMmapM感兴趣

mapM :: Monad m => (a -> m b) -> [a] -> m [ b ]
mapM f [x1, x2, ... xn] = do
                   y1 <- f x1
                   y2 <- f x2
                   ...
                   yn <- f xn
                   return [y1,y2,...yn]

foldM :: Monad m => (b -> a -> m b) -> b -> [ a ] -> m b
foldM _ a [] = return a
foldM f a [x1,x2,..xn] = do
               y1 <- f a x1
               y2 <- f y1 x2
               y3 <- f y2 x3
               ...
               yn <- f y_(n-1) xn
               return yn

希望这会有所帮助!