Я пытался создать foldl
, который работал бы с бесконечными списками, для ситуаций, когда вы не можете получить защищенную рекурсию, но где в зависимости от первого аргумента второй аргумент может не использоваться.
Например, умножение, где обычно вам нужны оба аргумента и защищенная рекурсия, не работает, но если первый аргумент равен 0, вы можете замкнуться.
Поэтому я написал следующую функцию:
foldlp :: (b -> a -> b) -> (b -> Bool) -> b -> [a] -> b
foldlp f p = go where
go b [] = b
go b (x : xs)
| p b = go (f b x) xs
| otherwise = b
И проверил это с помощью моей пользовательской функции умножения короткого замыкания:
mult :: Integer -> Integer -> Integer
mult 0 _ = 0
mult x y = x * y
main :: IO ()
main = print . <test_function>
Результаты, которые я получил с -prof -fprof-auto -O2
, +RTS -p
, были следующими:
foldlp mult (/= 0) 1 $ replicate (10 ^ 7) 1
total time = 0.40 secs
total alloc = 480,049,336 bytes
foldlp mult (`seq` True) 1 $ replicate (10 ^ 7) 1
total time = 0.37 secs
total alloc = 480,049,336 bytes
foldl' mult 1 $ replicate (10 ^ 7) 1
total time = 0.37 secs
total alloc = 480,049,352 bytes
foldl mult 1 $ replicate (10 ^ 7) 1
total time = 0.74 secs
total alloc = 880,049,352 bytes
foldr mult 1 $ replicate (10 ^ 7) 1
total time = 0.87 secs
total alloc = 880,049,336 bytes
Это было очень многообещающе, так как моя пользовательская функция допускает гибкие типы строгости, а также работает с бесконечными списками.
Первый пример завершится, как только достигнет 0
, как и foldr
, но foldr
намного медленнее.
Это позволяет избежать таких проблем, как переходы внутри кортежей, поскольку ((1 + 2) + 3, (10 + 20) + 30)
технически находится в WHNF, нарушая foldl'
.
Вы можете повторно получить foldl
с помощью flip foldl (const True)
и foldl'
с flip foldl (
seqTrue)
. И рабочие характеристики исходных ограниченных функций, по-видимому, получаются при этом заново.
Так что в качестве примечания я думаю, что foldlp
было бы достойным дополнением к Foldable
.
Но мой настоящий вопрос заключался в том, почему, когда я добавил {-# INLINE foldlp #-}
, производительность функций значительно снизилась, что дало мне:
foldlp mult (/= 0) 1 $ replicate (10 ^ 7) 1
total time = 0.67 secs
total alloc = 800,049,336 bytes
Поэтому мой реальный вопрос заключается в том, почему это происходит. Я думал, что недостатком встраивания является раздувание кода, а не значительное негативное влияние на производительность во время выполнения и увеличение использования памяти.