配列にはO(1)インデックスがあります。問題は、各要素が遅延して計算されることです。したがって、これをghciで実行すると次のようになります。
*Main> :set +s
*Main> let t = 100000
(0.00 secs, 556576 bytes)
*Main> let a = fibArray t
Loading package array-0.4.0.0 ... linking ... done.
(0.01 secs, 1033640 bytes)
*Main> a!t -- result omitted
(1.51 secs, 570473504 bytes)
*Main> a!t -- result omitted
(0.17 secs, 17954296 bytes)
*Main>
一度ルックアップした後は、ルックアップが非常に高速であることに注意してください。このarray
関数は、サンクへのポインターの配列を作成します。この配列は、最終的に計算されて値を生成します。初めて値を評価するときに、このコストを支払います。これが評価のためのサンクの最初のいくつかの拡張ですa!t
:
a!t -> a!(t-1)+a!(t-2)-> a!(t-2)+a!(t-3)+a!(t-2) -> a!(t-3)+a!(t-4)+a!(t-3)+a!(t-2)
高価なのは計算自体のコストではなく、この非常に大きなサンクを作成してトラバースする必要があります。
に渡されたリストの値を厳密化しようとしましたarray
が、無限ループが発生したようです。
これを回避する一般的な方法の1つは、STArrayなどの可変配列を使用することです。要素は、配列の作成中に利用可能になったときに更新でき、最終結果は凍結されて返されます。ベクトルパッケージでは、create
andconstructN
関数はこれを行う簡単な方法を提供します。
-- constructN :: Unbox a => Int -> (Vector a -> a) -> Vector a
import qualified Data.Vector.Unboxed as V
import Data.Int
fibVec :: Int -> V.Vector Int64
fibVec n = V.constructN (n+1) c
where
c v | V.length v == 0 = 0
c v | V.length v == 1 = 1
c v | V.length v == 2 = 1
c v = let len = V.length v
in v V.! (len-1) + v V.! (len-2)
ただし、このfibVec
関数はボックス化されていないベクトルでのみ機能します。通常のベクトル(および配列)は十分に厳密ではないため、すでに見つけたのと同じ問題が発生します。残念ながら、のUnboxedインスタンスはありませんInteger
。そのため、無制限の整数型が必要な場合(これfibVec
はこのテストではすでにオーバーフローしています)、IO
またはST
で必要な厳密性を有効にするために可変配列を作成することに固執しています。