Зачем нужен второй аргумент для f
? На мой взгляд, обе эти альтернативы более читабельны и не оказывают существенного влияния на производительность ...
...
let g y = f y && y `mod` x > 0 in
x : mkPrimes g xs
...
import Control.Arrow -- instance Monad (-> r)
import Control.Monad -- liftM2
(.&&.) = liftM2 (&&)
...
let g y = y `mod` x > 0 in
x : mkPrimes (f .&&. g) xs
...
Во всяком случае, вернемся к вопросу. Иногда использование функций в качестве структур данных является лучшим представлением для определенной задачи, а иногда нет. «Лучший» с точки зрения простоты кодирования и «лучший» с точки зрения производительности не всегда одно и то же. Техника «функции как структуры данных» необходима для компиляции во время выполнения , но, как предупреждает эта страница,
Компиляция во время выполнения может иногда принести вам значительный прирост эффективности, но зачастую может принести вам почти ничто за счет увеличения стресса и снижения производительности.
В вашем случае вполне вероятно, что накладные расходы на создание каждого f :: Integer -> ... -> Bool
значительно выше накладных расходов на построение каждого ps :: [Integer]
, с небольшой разницей или без различий при вызове f ... x
по сравнению с all ... ps
.
Чтобы выжать циклы из бесконечного первичного сита, избавьтесь от звонков на mod
! Целочисленное умножение, деление и модуль намного медленнее, чем сложение и вычитание целых чисел. На моей машине эта реализация работает на 40% быстрее при вычислении первых 1000 простых чисел (GHC 6.10.3 -O2
).
import qualified Data.Map as M
primes' :: [Integer]
primes' = mkPrimes 2 M.empty
where
mkPrimes n m = case (M.null m, M.findMin m) of
(False, (n', skips)) | n == n' ->
mkPrimes (succ n) (addSkips n (M.deleteMin m) skips)
_ -> n : mkPrimes (succ n) (addSkip n m n)
addSkip n m s = M.alter (Just . maybe [s] (s:)) (n+s) m
addSkips = foldl' . addSkip
В действии (используя немного синтаксиса JSON-ish),
mkPrimes 2 {}
=> 2 : mkPrimes 3 {4: [2]}
=> 2 : 3 : mkPrimes 4 {4: [2], 6: [3]}
=> 2 : 3 : mkPrimes 5 {6: [2, 3]}
=> 2 : 3 : 5 : mkPrimes 6 {6: [2, 3], 10: [5]}
=> 2 : 3 : 5 : mkPrimes 7 {8: [2], 9: [3], 10: [5]}
=> 2 : 3 : 5 : 7 : mkPrimes 8 {8: [2], 9: [3], 10: [5], 14: [7]}
=> 2 : 3 : 5 : 7 : mkPrimes 9 {9: [3], 10: [2, 5], 14: [7]}
=> 2 : 3 : 5 : 7 : mkPrimes 10 {10: [2, 5], 12: [3], 14: [7]}
=> 2 : 3 : 5 : 7 : mkPrimes 11 {12: [2, 3], 14: [7], 15: [5]}
...
карта отслеживает будущие мультипликаторы, используя только сложение.