尽管这不一定是您要寻找的,但我们可以使用 hylomorphism 对惰性技巧进行编码:
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
import Data.Functor.Foldable
import Data.Functor.Foldable.TH
data CappedList c a = Cap c | CCons a (CappedList c a)
deriving (Eq, Show, Ord, Functor, Foldable, Traversable)
makeBaseFunctor ''CappedList
-- The seq here has no counterpart in the implementation in the question.
-- It improves performance quite noticeably. Other seqs might be added for
-- some of the other "s", as well as for the percentage; the returns, however,
-- are diminishing.
toPercents :: Floating a => [a] -> [a]
toPercents = snd . hylo percAlg sumCal . (0,)
where
sumCal = \case
(s, []) -> CapF s
(s, a : as) -> s `seq` CConsF a (s + a, as)
percAlg = \case
CapF s -> (s, [])
CConsF a (s, as) -> (s, (a * 100 / s) : as)
这与惰性技巧相对应,因为由于 hylo fusion,中间层CappedList
实际上永远不会被构建,并且toPercents
会一次性消耗输入列表。正如moonGoose 所说,使用的重点CappedList
是将总和放在(虚拟)中间结构的底部,以便正在完成的列表重建可以从一开始就可以访问它。percAlg
(也许值得注意的是,即使它是一次性完成的,似乎很难从这个技巧中获得良好且恒定的内存使用,无论是我的版本还是你的版本。欢迎在这方面提出建议。 )