我目前停留在列表推导中设置上限。
我要做的是找到低于一百万的所有斐波纳契数。 为此我设计了一个相当简单的递归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试图在序列中找到进一步低于一百万的斐波纳契数,这是相当无效的。
有人可以帮我解决这个问题吗?非常感谢!
答案 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}}。