Haskell 如何在列表理解中有多个无限范围?
在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您的代码冻结,因为您的谓词永远不会得到满足。 为什么? 让我们
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)]