使用列表在Haskell中实现不同的fibonacci

时间:2015-10-26 11:49:40

标签: haskell functional-programming state fibonacci imperative-programming

我在博客上搜索了一个与我想要的类似的Fibonacci实现。尽管有很多关于Haskell的Fibonacci实现的问题,但没有出现任何问题。

我想要一个递归的Fibonacci函数(速度无关紧要),它会在每次调用fib函数时将每个Fibonacci数附加到全局列表中。我是Haskell的新手,来自一个势在必行的背景。这是我认为可行的,但没有达到目的。

fibos = []

fib 0 = 1
fib 1 = 1
fib n = (fib(n-1) + fib(n-2)):fibos


main = do 
    putStrLn "Please enter a number:"
    n <- readLn
    fib n
    print fibos

我知道Haskell中已经存在的Fibonacci列表,但是我的赋值规范要求我这样做。我试图将每个Fibonacci数字提升到用户定义的数字n到空的全局fibos列表中。任何帮助或指示将不胜感激。

编辑:

我现在有一个工作函数,它返回一个包含n个元素的斐波纳契数列表,其中n是用户输入,这里是代码:

fib::Int->Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
fibList n = map fib [0..n]

main :: IO ()
main = do 
    putStrLn "Please enter a positive integer:"
    n <- readLn 
    print (fibList n)

我需要的是一个函数,如果用户输入2则输出[0,1,1,2],如果用户输入8或8之间的任何内容则输出[0,1,1,3,3,5,8]和12(因为13是一个纤维数)。

4 个答案:

答案 0 :(得分:2)

import Data.List

fibo :: Int -> Int
fibo 0 = 0
fibo 1 = 1
fibo n = fibo (n-1)+fibo(n-2)


fiboList x =map fibo [1..x]

main = do
    print ( fiboList 35 )

答案 1 :(得分:1)

修改

如果只需要查看导致fib n的值序列,您应该使用以下内容:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fib n = (take n fibs, fibs !! n)

的工作原理如下:

*Main> fib 0
([],0)
*Main> fib 1
([0],1)
*Main> fib 10
([0,1,1,2,3,5,8,13,21,34],55)

如果需要,可以使用State monad来分离计算的列表方面,但它只是指在有限范围内“全局”的列表(例如main函数)。这是你住在适当的Haskell土地时最接近你想要的。

正确的方式

我假设你是为了模仿的目的而尝试这个?如果是,基本上已经存在fib的任何基于列表的实现的全局列表 - 任何fib的调用者使用与Haskell(例如GHC)运行时使用的相同的延迟评估列表引擎盖;该列表中已经计算过的任何元素都不会被重新计算。

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

- 这里,fibs 一个全局列表,而你无法通过访问其中的单个元素(fibs !! n)来显式地/手动地改变它,你当运行时向其添加新值时间接导致底层列表发生变异。

错误的方式(但如果您不打算在实际代码中使用此功能,请继续使用)

另一方面,如果您只是为了拥有该列表而需要列表,例如出于调试或其他一些奇怪的原因,您可以使用非常丑陋且不安全的黑客,例如以下基于IORef的解决方案,但请记住坚持以结尾的函数名称Unsafe ,本着stdlib标记所有不安全呼叫的精神,以便使用它们的人意识到危险

import Data.IORef
import System.IO.Unsafe (unsafePerformIO)

fibsStorageUnsafe :: IORef [Integer]
fibsStorageUnsafe = unsafePerformIO $ newIORef []

-- this is one one that uses the pure definition
-- above to compute the value and then store it
-- in a global, mutable IORef
fibUnsafe n = unsafePerformIO (updateFibs ret) `seq` ret
  where
    ret = fib' n

    updateFibs x = do
      old <- readIORef fibsStorageUnsafe
      writeIORef fibsStorageUnsafe (x:old)

    fib' :: Int -> Integer
    fib' 0 = 0
    fib' 1 = 1
    -- you can recursively call fib' instead of fibUnsafe
    -- to only log the "top level" (non-recursive)
    -- calls to fibUnsafe and not all recursive calls
    fib' n = fibUnsafe (n-1) + fibUnsafe (n-2)

的工作原理如下:

*Main> readIORef fibsStorageUnsafe
[]
*Main> fibUnsafe 0
0
*Main> readIORef fibsStorageUnsafe
[0]
*Main> fibUnsafe 1
1
*Main> readIORef fibsStorageUnsafe
[1,0]
*Main> fibUnsafe 3
2
*Main> readIORef fibsStorageUnsafe
[1,0,1,1,2,1,0]
*Main> fibUnsafe  10
55
*Main> readIORef fibsStorageUnsafe
[1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,21,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,34,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,21,55,1,0,1,1,2,1,0]

您可以调整fibUnsafefib'和其他组件的定义以符合您的目标,但同样:我强烈建议不要使用这样的方法,除非它仅用于学习或实验目的。

例如,如果您修改fib'来调用自己而不是fibUnsafe,请执行以下操作:

fib' n = fib' (n-1) + fib' (n-2)

然后调用fibUnsafe 10将产生fibStorageUnsafe的以下内容,而不是您在上面看到的内容:

*Main> fibUnsafe  10
55
*Main> readIORef fibsStorageUnsafe
[55]

免责声明:我甚至不知道上述基于IORef的代码在所有情况下是否都能正常运行 - 我承认我对IORef s不太熟悉以及unsafePerformIO如何工作以保证上述代码的语义。

答案 2 :(得分:1)

如果可以获得列表,你可以按照自己的意愿做一些事情:

  if ($_SERVER['REQUEST_URI'] == '/?Über-uns.php'){
  $html.= 'hover.navbutton-ueber-uns';
 } else {
 $html.= 'navbutton-ueber-uns';
 }

然后fibs 0 = [0] fibs 1 = [0,1] fibs n = let fs@(f1:f2:_) = fibs (n-1) in (f1+f2) : fs 将返回fibs 10。请注意,以相反的顺序构建列表可能会浪费或更复杂,因为Haskell列表仅在“前端”上运行。对于奇怪的[55,34,21,13,8,5,3,2,1,1,0],请参阅https://en.wikibooks.org/wiki/Haskell/Pattern_matchingfs@(f1:f2:_)是“as” - 模式)

对于“真正的”斐波那契函数,你只需要返回头部:

@

我认为这个版本的意图非常接近,而且它的性能也不错。但是,这段代码不是很惯用。这里的典型方法是无限列表(如其他一些答案所示)。

如果你允许我个人发表评论:我发现“必要”知识在学习Haskell时比帮助更有刺激性。我认为你应该有意识地“忘记”或“忘掉”你对(命令式)编程的了解,以便“理解”函数式编程。

答案 3 :(得分:0)

在纯函数式编程中,不能使用全局的可变变量。因此,如果您定义全局值fibos = [],那么该列表将永远不会是空列表。如果您想要一个不可变的顶级定义,请将其修改为 n 第一个Fibonacci数字,其中 n 在运行时确定,fibs没有参数变得困难。

另一种选择是Haskell的懒惰评估,它使您能够为所有斐波那契数字创建顶级定义,但它们只会在请求的范围内进行计算。

fibs :: [Integer]
fibs = 1:1:zipWith (+) fibs (tail fibs)

或者不那么花哨:

fibs :: [Integer]
fibs = fib 0 1
  where fib a b = b : fib b (a+b)

然后,您可以根据需要提取尽可能多的斐波那契数字:

main :: IO ()
main = do
  putStr "n: "
  n <- readLn
  putStrLn $ "Fibs: " ++ show (take n fibs)