Обычно -качественный фолд с досрочным завершением равен foldr
с функцией объединения, которая не является строгой во втором аргументе. Но его информационный поток направлен справа налево (если есть), а вы хотите, чтобы он был слева направо.
Возможное решение состоит в том, чтобы foldr
функционировал как left fold, который затем можно сделать так, чтобы он остановился рано:
foldlWhile :: Foldable t
=> (a -> Bool) -> (r -> a -> r) -> r
-> t a -> r
foldlWhile t f a xs = foldr cons (\acc -> acc) xs a
where
cons x r acc | t x = r (f acc x)
| otherwise = acc
Вам нужно настроить это на t
, чтобы проверить acc
вместо x
, чтобы соответствовать вашим целям.
Эта функция foldlWhile
из https://wiki.haskell.org/Foldl_as_foldr_alternative, немного переписана. foldl'Breaking
оттуда может соответствовать требованиям немного лучше.
foldr
с функцией ленивого редуктора может express corecursion прекрасно, как и unfoldr
.
И ваш код уже ленивый: terminatingFold (\acc x -> Left acc) [1..]
=> []
. Вот почему я не уверен, является ли этот ответ «более умным», как вы и просили.
edit: после комментария @danidiaz, чтобы сделать его ленивым, вы должны будете его кодировать, например,
first3above5 :: (Foldable t, Ord a, Num a)
=> t a -> [a]
first3above5 xs = foldr cons (const []) xs 0
where
cons x r i | x > 5 = if i==2 then [x]
else x : r (i+1)
| otherwise = r i
Это может быть далее обобщается путем абстрагирования теста и подсчета.
Конечно, это просто переопределение take 3 . filter (> 5)
, но показывает, как это сделать в целом с foldr
.