Хотя это не обязательно то, что вы искали, мы можем закодировать уловку лени с помощью 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
использует список ввода за один проход.Смысл использования CappedList
заключается в том, что , как MoonGoose помещает его , помещая сумму в конец (виртуальной) промежуточной структуры, так что восстановление списка, выполняемое с помощью percAlg
, может иметь к нему доступ.с самого начала.
(Возможно, стоит отметить, что, несмотря на то, что это делается за один проход, кажется, трудно получить хорошее и постоянное использование памяти из этого трюка, будь то с моей версией илис вашими. Предложения на этом фронте приветствуются.)