Haskell 如何在列表理解中有多个无限范围?

Haskell 如何在列表理解中有多个无限范围?,haskell,Haskell,在haskell中,我有这样一个列表: sq = [(x,y,z) | x <- v, y <- v, z <- v, x*x + y*y == z*z, x < y, y < z] where v = [1..] sq=[(x,y,z)|x这是可能的,但您必须给出生成数字的顺序。下面生成您想要的数字;请注意,xx的y来代替,类似地,z(一旦绑定了x和y后确定): [(x,y,z)| total您的代码冻结,因为您的谓词永远不会得到满足。 为什么? 让我们

在haskell中,我有这样一个列表:

sq = [(x,y,z) | x <- v, y <- v, z <- v, x*x + y*y == z*z, x < y, y < z]
    where v = [1..]

sq=[(x,y,z)|x这是可能的,但您必须给出生成数字的顺序。下面生成您想要的数字;请注意,
x
测试可以通过只生成
>x
y
来代替,类似地,
z
(一旦绑定了
x
y
后确定):


[(x,y,z)| total您的代码冻结,因为您的谓词永远不会得到满足。
为什么?

让我们举一个没有任何谓词要理解的例子

>>> let v = [1..] in take 10 $ [ (x, y, z) | x <- v,  y <- v, z <- v ] 
[(1,1,1),(1,1,2),(1,1,3),(1,1,4),(1,1,5),(1,1,6),(1,1,7),(1,1,8),(1,1,9),(1,1,10)]

对列表理解的查找被转换为
concatMap
函数的嵌套应用程序:

concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)

concat :: [[a]] -> [a]
concat [] = []
concat (xs:xss) = xs ++ concat xss

-- Shorter definition:
--
-- > concat = foldr (++) []
您的示例与此等效:

sq = concatMap (\x -> concatMap (\y -> concatMap (\z -> test x y z) v) v) v
    where v = [1..]
          test x y z = 
              if x*x + y*y == z*z
              then if x < y
                   then if y < z
                        then [(x, y, z)]
                        else []
                   else []
              else []
要理解这段代码(如果我弄错了,请修复!),请看第四张图,尤其是第四张图,该函数是基于“之字形”的算法

我刚刚尝试了一个简单版本的
sq
,它几乎可以立即找到
(3,4,5)
,但需要很长时间才能找到任何其他组合(至少在GHCI中)。但我认为从中吸取的关键教训是:

  • 列表理解对于嵌套的无限列表来说效果不好
  • 不要花太多时间在列表理解上。它们能做的一切,像
    map
    filter
    concatMap
    这样的函数都可以做,而且还有许多其他有用的函数,所以请集中精力

  • 除了解释该问题的其他答案外,这里还有一个替代解决方案,它是通用的,适用于在无限搜索空间上进行搜索(它也与列表单子和兼容,但这些不能很好地处理无限搜索空间,正如您已经发现的):

    或者:

    *Triples> :m +Control.Monad.Levels
    *Triples Control.Monad.Levels> take 5 $ bfs sq   -- larger memory requirements
    [(3,4,5),(6,8,10),(5,12,13),(9,12,15),(8,15,17)]
    *Triples Control.Monad.Levels> take 5 $ idfs sq  -- constant space, slower, lazy
    [(3,4,5),(5,12,13),(6,8,10),(7,24,25),(8,15,17)]
    

    输出不应该是这样的,约束是x^2+y^2=z^2,没有相同的集合,甚至不管顺序如何。我认为这缺少了一些保护或其他东西,根据问题中的代码,我认为他们只需要毕达哥拉斯的三元组。@omega:重写整个内容。
    concatMap :: (a -> [b]) -> [a] -> [b]
    concatMap f xs = concat (map f xs)
    
    concat :: [[a]] -> [a]
    concat [] = []
    concat (xs:xss) = xs ++ concat xss
    
    -- Shorter definition:
    --
    -- > concat = foldr (++) []
    
    sq = concatMap (\x -> concatMap (\y -> concatMap (\z -> test x y z) v) v) v
        where v = [1..]
              test x y z = 
                  if x*x + y*y == z*z
                  then if x < y
                       then if y < z
                            then [(x, y, z)]
                            else []
                       else []
                  else []
    
    -- | Take the Cartesian product of two lists, but in an order that guarantees
    -- that all combinations will be tried even if one or both of the lists is 
    -- infinite:
    cartesian :: [a] -> [b] -> [(a, b)]
    cartesian [] _ = []
    cartesian _ [] = []
    cartesian (x:xs) (y:ys) = 
        [(x, y)] ++ interleave3 vertical horizontal diagonal
            where 
              -- The trick is to split the problem into these four pieces:
              --
              -- |(x0,y0)| (x0,y1) ... horiz
              -- +-------+------------
              -- |(x1,y0)| .
              -- |   .   |  .
              -- |   .   |   .
              -- |   .   |    . 
              --   vert         diag
              vertical = map (\x -> (x,y)) xs
              horizontal = map (\y -> (x,y)) ys
              diagonal = cartesian xs ys
    
    
    interleave3 :: [a] -> [a] -> [a] -> [a]
    interleave3 xs ys zs = interleave xs (interleave ys zs)
    
    interleave :: [a] -> [a] -> [a]
    interleave xs [] = xs
    interleave [] ys = ys
    interleave (x:xs) (y:ys) = x : y : interleave xs ys
    
    {-# LANGUAGE MonadComprehensions #-}
    
    module Triples where
    
    import Control.Monad
    
    sq :: MonadPlus m => m (Int, Int, Int)
    sq = [(x, y, z) | x <- v, y <- v, z <- v, x*x + y*y == z*z, x < y, y < z]
        where v = return 0 `mplus` v >>= (return . (1+))
    
    *Triples> :m +Control.Monad.Stream
    *Triples Control.Monad.Stream> take 10 $ runStream sq
    [(3,4,5),(6,8,10),(5,12,13),(9,12,15),(8,15,17),(12,16,20),(7,24,25),
    (15,20,25),(10,24,26),(20,21,29)]
    
    *Triples> :m +Control.Monad.Levels
    *Triples Control.Monad.Levels> take 5 $ bfs sq   -- larger memory requirements
    [(3,4,5),(6,8,10),(5,12,13),(9,12,15),(8,15,17)]
    *Triples Control.Monad.Levels> take 5 $ idfs sq  -- constant space, slower, lazy
    [(3,4,5),(5,12,13),(6,8,10),(7,24,25),(8,15,17)]