Powerset功能1-Liner

Kev*_*ith 25 haskell

了解一下Haskell演示了这个powerset功能:

powerset某组是一组一组的所有子集.

powerset :: [a] -> [[a]]  
powerset xs = filterM (\x -> [True, False]) xs
Run Code Online (Sandbox Code Playgroud)

运行它:

ghci> powerset [1,2,3]                    
[[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[]]
Run Code Online (Sandbox Code Playgroud)

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

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

请告诉我这个powerset功能.

Wil*_*ess 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 ~ []
Run Code Online (Sandbox Code Playgroud)

所以这是filter"在"非确定性(列表)monad中.

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

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

这是一个特权.


另一个例子(在另一个monad中),建立在评论中提到的Brent Yorgey的博客文章中,

  >> 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]
Run Code Online (Sandbox Code Playgroud)

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

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 )
Run Code Online (Sandbox Code Playgroud)

写出名单单子的定义,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 ]
Run Code Online (Sandbox Code Playgroud)

因此,

-- 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] ]
Run Code Online (Sandbox Code Playgroud)

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

通过谓词是常量(const [True, False])的事实使得处理的翻转顺序成为可能.否则,将对相同的输入值反复评估测试,我们可能不希望这样.


Vũ *_* Tô 6

让我帮你解决这个问题:

  • 第一:你必须了解monad列表.如果你还记得,我们有:

    do
      n  <- [1,2]  
      ch <- ['a','b']  
      return (n,ch)
    
    Run Code Online (Sandbox Code Playgroud)

    结果将是: [(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')]
    
    Run Code Online (Sandbox Code Playgroud)
  • 第二:我们有filterM的实现:

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

    让我们举个例子让您更容易理解这个想法:

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

    filterM的最内层递归:x = 3

    do
      flg <- [True, False]
      ys  <- [ [] ]
      return (if flg then 3:ys else ys)
    
    Run Code Online (Sandbox Code Playgroud)

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

    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], [] ]
    
    Run Code Online (Sandbox Code Playgroud)

    同样:

    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], [] ]
    
    Run Code Online (Sandbox Code Playgroud)
  • 从理论上说:它毕竟是链接列表monad:

    filterM :: (a -> m Bool) -> [a] -> m [a]
               (a -> [Bool]) -> [a] -> [ [a] ]
    
    Run Code Online (Sandbox Code Playgroud)

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