Я тестировал производительность функции partition
для списков и получал некоторые странные результаты, я думаю.
У нас есть partition p xs == (filter p xs, filter (not . p) xs)
, но мы выбрали первую реализацию, потому что она выполняет только один обход по списку. Тем не менее, результаты, которые я получил, говорят, что, возможно, лучше использовать реализацию, которая использует два обхода.
Вот минимальный код, который показывает, что я вижу
import Criterion.Main
import System.Random
import Data.List (partition)
mypartition :: (a -> Bool) -> [a] -> ([a],[a])
mypartition p l = (filter p l, filter (not . p) l)
randList :: RandomGen g => g -> Integer -> [Integer]
randList gen 0 = []
randList gen n = x:xs
where
(x, gen') = random gen
xs = randList gen' (n - 1)
main = do
gen <- getStdGen
let arg10000000 = randList gen 10000000
defaultMain [
bgroup "filters -- split list in half " [
bench "partition100" $ nf (partition (>= 50)) arg10000000
, bench "mypartition100" $ nf (mypartition (>= 50)) arg10000000
]
]
Я провел тесты как с -O
, так и без него, и оба раза я получаю, что двойные обходы лучше.
Я использую ghc-7.10.3
с criterion-1.1.1.0
Мои вопросы:
-
Ожидается ли это?
-
Правильно ли я использую Критерий? Я знаю, что лень может быть сложным, и
(filter p xs, filter (not . p) xs)
будет выполнять только два обхода, если используются оба элемента кортежа. -
Должно ли это что-то делать с тем, как списки обрабатываются в Haskell?
Спасибо большое!