在列表理解中"takeWhile"

PyR*_*lez 4 syntax haskell list-comprehension list infinite

我有以下内容:

[bla z|n<-[0..], let z = foo n, z < 42]
Run Code Online (Sandbox Code Playgroud)

问题是,我希望列表理解能够在z < 42失败时立即结束,就好像它是一个takeWhile.我知道我可以将它重构成一堆过滤器和地图,但是通过列表理解它会更加优雅.

结合列表理解和takeWhile的最优雅的方法是什么?

chi*_*chi 6

由于列表推导不允许这样做,我使用monad理解和定义自定义monad进行了一些攻击.结果是以下工作:

example :: [Int]
example = toList [1000 + n 
                 | n <- fromList [0..]
                 , _ <- nonStopGuard (n > 1)
                 , let z = 10*n
                 , _ <- stopGuard (z < 42) ]

-- Output: [1002,1003,1004]
Run Code Online (Sandbox Code Playgroud)

以上作为正常的列表理解,但有两种不同的守卫.nonStopGuard除了需要奇怪的语法外,A还可以作为常规防护.一stopGuard,而不是做更多的东西:一旦它变成假的,停止进一步的选择在以前的发电机(如<-[0..])加以考虑.

我写的小库如下所示:

{-# LANGUAGE DeriveFunctor, MonadComprehensions #-}
import Control.Monad
import Control.Applicative

data F a = F [a] Bool
  deriving (Functor, Show)
Run Code Online (Sandbox Code Playgroud)

Bool上面的是一个停止位,信号必须停止进一步考虑的选择.

instance Applicative F where pure = return; (<*>) = ap
instance Monad F where
   return x = F [x] False
   F [] s      >>= _ = F [] s
   F (x:xs) sx >>= f = F (ys ++ zs) (sx || sy || sz)
      where 
      F ys sy = f x
      F zs sz = if sy then F [] False else F xs sx >>= f
Run Code Online (Sandbox Code Playgroud)

当信号停止时,最后一个if将丢弃该xs部分f x.

nonStopGuard :: Bool -> F ()
nonStopGuard True  = F [()] False
nonStopGuard False = F []   False
Run Code Online (Sandbox Code Playgroud)

一名常规警卫从未发出停止信号.它只提供一个或零选择.

stopGuard :: Bool -> F ()
stopGuard True  = F [()] False
stopGuard False = F [] True
Run Code Online (Sandbox Code Playgroud)

一个停止警卫反而发出信号,一旦它变为假就停止.

fromList :: [a] -> F a
fromList xs = F xs False

toList :: F a -> [a]
toList (F xs _) = xs
Run Code Online (Sandbox Code Playgroud)

最后的警告:我不完全确定我的monad实例定义了一个实际的monad,即它是否满足monad定律.


根据@icktoofay的建议,我写了几个quickcheck测试:

instance Arbitrary a => Arbitrary (F a) where
   arbitrary = F <$> arbitrary <*> arbitrary

instance Show (a -> b) where
   show _ = "function"

prop_monadRight :: F Int -> Bool
prop_monadRight m =
   (m >>= return) == m

prop_monadLeft :: Int -> (Int -> F Int) -> Bool
prop_monadLeft x f =
   (return x >>= f) == f x

prop_monadAssoc :: F Int -> (Int -> F Int) -> (Int -> F Int) -> Bool
prop_monadAssoc m f g =
   ((m >>= f) >>= g)
   ==
   (m >>= (\x -> f x >>= g))
Run Code Online (Sandbox Code Playgroud)

运行100000次测试没有发现任何反例.所以,上面可能F是一个真正的monad.

  • 那完全疯了.+1. (2认同)