可以退出发电机吗?

时间:2015-01-08 22:32:46

标签: haskell list-comprehension

请考虑以下事项:

list = [1,3..]

generate n = [compute y | y <- list , (compute y) < n ] 

compute a = ... whatever ...

是否可以在到达列表的最后一个元素之前退出生成器 (例如,如果(计算y> 20)? 我想节省计算能力。我只需要小于n的元素。

我是Haskell的新手。一个简单的答案可能是最好的答案。

1 个答案:

答案 0 :(得分:8)

关于Haskell的奇妙之处在于它很懒惰。如果你说

> let x = generate 100000

然后Haskell没有立即计算generate 100000,它只是创建了一个开始计算它的承诺(我们通常将其称为thunk)。

如果您只想要compute y > 20之前的元素,那么您可以

> takeWhile (<= 20) (generate 100000)

这与您可以执行类似

的语义相同
> let nums = [1..] :: [Integer]

这使得对从1到无穷大的所有Integer值的延迟引用。然后你可以做像

这样的事情
> take 10 $ map (* 10) $ drop 12345 $ map (\x -> x ^ 2 + x ^ 3 + x ^ 4) $ filter even nums
[3717428823832552480,3718633373599415160,3719838216073150080,3721043351301172120,3722248779330900000,3723454500209756280,3724660513985167360,3725866820704563480,3727073420415378720,3728280313165051000]

虽然这似乎很多工作,但它只计算返回你请求的10个元素所需的最低限度。在这个例子中take 10的参数仍然是一个无限列表,我们首先抓住所有的平均值,然后将代数表达式映射到它,然后删除前12345个元素,然后将所有剩余(无限)元素乘以10在Haskell中使用无限结构是非常常见的并且通常是有利的。

作为旁注,您当前对generate的定义会做额外的工作,您需要的更多内容

generate n = [compute_y | y <- list, let compute_y = compute y, compute_y < n]

这种方式compute y只计算一次,并且您的过滤器compute_y < n与理解中|的左侧共享值。另请注意,当您理解条件时,会将其转换为filter,而不是takeWhile

> -- filter applies the predicate to all elements in the list
> filter (\x -> x `mod` 5 == 0) [5,10,15,21,25]
[5,10,15,20]
> -- takeWhile pulls values until the predicate returns False
> takeWhile (\x -> x `mod` 5 == 0) [5,10,15,21,25]
[5,10,15]