Ответ 1
Массивы имеют 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
создает массив указателей на thunks, которые в конечном итоге будут вычислены для получения значения. В первый раз, когда вы оцениваете стоимость, вы оплачиваете эту стоимость. Вот первые несколько расширений thunk для оценки 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
, но это, казалось, привело к бесконечному циклу.
Общим для этого является использование изменяемого массива, такого как STArray. Элементы могут обновляться, поскольку они доступны во время создания массива, а конечный результат заморожен и возвращен. В векторном пакете функции create
и constructN
предоставляют простые способы сделать это.
-- 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
работает только с незанятыми векторами. Регулярные векторы (и массивы) недостаточно строгие, что приводит к той же проблеме, которую вы уже нашли. И, к сожалению, для Integer
нет экземпляра Unboxed, поэтому, если вам нужны неограниченные целые типы (этот fibVec
уже переполнен в этом тесте), вы застряли в создании изменяемого массива в IO
или ST
чтобы обеспечить необходимую строгость.