编辑:
如果您只需要查看导致 的值序列,则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 土地上最接近您想要的方式。
正确的方式
我假设您正在尝试这样做是为了做momoization?如果是,基本上在任何基于列表的实现的引擎盖下已经有一个全局列表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]
您可以调整 和其他组件的定义以适应您的目标fibUnsafe
,fib'
但同样:我强烈建议您不要使用这样的方法,除非它仅用于学习或实验目的。
例如,如果您修改为不这样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
为上面代码的语义提供任何保证。