根据输出功能设置输入设置的上限

时间:2011-02-07 22:27:06

标签: haskell list-comprehension

我目前停留在列表推导中设置上限。

我要做的是找到低于一百万的所有斐波纳契数。 为此我设计了一个相当简单的递归Fibonacci函数

fib :: Int -> Integer
fib n
    n == 0    = 0
    n == 1    = 1
    otherwise = fib (n-1) + fib (n-2)

我坚持的是定义一百万部分。我现在得到的是:

[ fib x | x <- [0..35], fib x < 1000000 ]

这是因为我知道Fibonacci序列中的第35个数字足够高。 但是,我想要的是通过一个函数找到这个限制并设置它。

[ fib x | x <- [0..], fib x < 1000000 ]

这确实给了我数字,但它根本不会停止。这导致Haskell试图在序列中找到进一步低于一百万的斐波纳契数,这是相当无效的。

有人可以帮我解决这个问题吗?非常感谢!

4 个答案:

答案 0 :(得分:10)

列表推导中的检查fib x < 1000000过滤掉小于1000000的fib x值;但是列表理解无法知道x的更大值意味着更大fib x的值,因此必须继续,直到检查了所有x

改为使用takeWhile

takeWhile (< 1000000) [ fib x | x <- [0..35]]

答案 1 :(得分:3)

保证列表理解能够查看列表的每个元素。你想要takeWhile :: (a -> Bool) -> [a] -> [a]。有了它,您的列表只是takeWhile (< 1000000) $ map fib [1..]takeWhile函数只返回满足给定谓词的列表的前导部分;还有一个类似的dropWhile函数,它删除了满足给定谓词的列表的前导部分,以及span :: (a -> Bool) -> [a] -> ([a], [a]),它只是(takeWhile p xs, dropWhile p xs),类似break当谓词为真时,它将列表分为两部分(相当于span (not . p)。因此,例如:

  • takeWhile (< 3) [1,2,3,4,5,4,3,2,1] == [1,2]
  • dropWhile (< 3) [1,2,3,4,5,4,3,2,1] == [3,4,5,4,3,2,1]
  • span (< 3) [1,2,3,4,5,4,3,2,1] == ([1,2],[3,4,5,4,3,2,1])
  • break (> 3) [1,2,3,4,5,4,3,2,1] == ([1,2,3],[4,5,4,3,2,1])

答案 2 :(得分:1)

应该提到的是,对于这样的任务,“规范”(和更快)的方式是将数字定义为无限流,例如。

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

takeWhile (<100) fibs
--[0,1,1,2,3,5,8,13,21,34,55,89]

起初的定义可能看起来很可怕(甚至是“魔法”),但如果你“懒惰”,它就会有意义。

定义这样一个无限列表的“循环”(在某种意义上说,更具“命令性”)方式是:

fibs = map fst $ iterate (\(a,b) -> (b,a+b)) (0,1) 

<强> [编辑]

对于有效的直接计算(没有无限列表),您可以使用matrix multiplication

fib n = second $ (0,1,1,1) ** n where
   p ** 0 = (1,0,0,1)
   p ** 1 = p
   p ** n | even n = (p `x` p) ** (n `div` 2)
          | otherwise = p `x` (p ** (n-1))
   (a,b,c,d) `x` (q,r,s,t) = (a*q+b*s, a*r+b*t,c*q+d*s,c*r+d*t)
   second (_,f,_,_) = f

(这写起来真的很有趣,但我总是很感激建议)

答案 3 :(得分:0)

我能想到的最简单的事情是:

[ fib x | x <- [1..1000000] ] 

所有fib n > n的{​​{1}}。