Речь идет о явном и неявном совместном использовании. Когда вы явно называете вещь, она, естественно, может использоваться совместно, то есть существовать как отдельная сущность в памяти и использоваться повторно. (Конечно, совместное использование не является частью языка как такового, мы можем лишь немного подтолкнуть компилятор к совместному использованию некоторых вещей).
Но когда вы пишете одно и то же выражение дважды или трижды, вы полагаетесь на то, что компилятор заменит общие подвыражения одной явно общей сущностью. Это может случиться, а может и не случиться.
Ваш первый вариант эквивалентен
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!) where
fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
Здесь вы конкретно называете сущность и обращаетесь к ней по этому имени. Но это функция. Чтобы сделать повторное использование еще более определенным, мы можем указать фактический список значений, которые передаются здесь, явно:
memoized_fib :: Int -> Integer
memoized_fib = (fibs !!) where
fibs = map fib [0 ..]
fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
Последнюю строку можно сделать еще более наглядной, добавив явную ссылку на реальную сущность, которой мы делимся здесь, — список fibs
, который мы только что назвали на шаге выше:
fib n = fibs !! (n-2) + fibs !! (n-1)
Ваш второй вариант эквивалентен этому:
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!) where
fib 0 = 0
fib 1 = 1
fib n = (map fib [0 ..] !!) (n-2) + (map fib [0 ..] !!) (n-1)
Здесь у нас есть три, казалось бы, независимых выражения map
, которые могут быть разделены или не разделены компилятором. Компиляция с помощью ghc -O2
, по-видимому, снова вводит совместное использование, а вместе с ним и скорость.
18.08.2012
fib n = fibs !! (n-2) + fibs !! (n-1)
. Я не уверен, противоречит ли это вашей точке зрения, но я также не уверен, что версия, которую вы написали, разделяет. 19.08.2012fibs
(что я и хотел сказать). Если 1-я версия OP была общей, то и моя переписка тоже должна быть общей, потому что она явно называет выражение, которое разделяет компилятор, даже если оно не имеет имени. 19.08.2012fibs !! (n-2) + ...
, потому что там мы видим явную ссылку на список, который на самом деле является общим объектом - спискомfibs
. Просто постарался не делать слишком длинный пост. Отредактирую это в. 19.08.2012