Ключевая проблема в том, что вы знаете, что step x xss
всегда будет давать результат формы (x:_):_
, но вы «скрываете» это за совпадениями шаблона, поэтому Haskell вынужден сначала оценить их, чтобы определить, в каком случаеиз step
, чтобы выбрать до того, как он увидит эти конструкторы.
В общем, для foldr f x
, чтобы иметь возможность производить любой вывод до достижения конца списка, f
должен быть в состоянии произвести некоторыевывод перед проверкой второго аргумента.
Мы можем исправить это, разбив step
на два, чтобы мы могли создать два конструктора (:)
перед выполнением сопоставления с шаблоном для второго аргумента.
groupBy' f = foldr step []
where step x xss = let (ys, yss) = step' x xss in (x:ys):yss
step' x [] = ([], [])
step' x (xs:xss) | f x (head xs) = (xs, xss)
| otherwise = ([], xs:xss)
Это примерно так же лениво, как вы можете его получить.
*Main> groupBy' (<) [1, 2, 3, 2, 3, 4, 1, undefined]
[[1,2,3],[2,3,4],[1*** Exception: Prelude.undefined