Это прекрасная возможность использовать технику преобразователей.
Вычисление суммы списка — это свертка. Карта и фильтр тоже складываются. Объединение нескольких сверток во вложенном виде, как в (sum...(filter...(map...sqr...)))
, приводит к многократному (здесь трем) обходам списка.
Но когда вложенные складки объединяются, их редуцирующие функции объединяются вложенным образом, что дает нам один обход fold вместо этого с помощью одной комбинированной функции редуктора:
(define (((mapping f) kons) x acc) (kons (f x) acc)) ; the "mapping" transducer
(define (((filtering p) kons) x acc) (if (p x) (kons x acc) acc)) ; the "filtering" one
(define (sum-of-positive-squares n)
(foldl ((compose (mapping sqr) ; ((mapping sqr)
(filtering (lambda (x) (> x 0)))) ; ((filtering {> _ 0})
+) 0 (range (+ 1 n)))) ; +))
; > (sum-of-positive-squares 3)
; 14
Конечно, ((compose f g) x)
такое же, как (f (g x))
. Комбинированная / «составная» (каламбур) функция редуктора создается просто заменой аргументов в определения, как
((mapping sqr) ((filtering {> _ 0}) +))
=
( (lambda (kons)
(lambda (x acc) (kons (sqr x) acc)))
((filtering {> _ 0}) +))
=
(lambda (x acc)
( ((filtering {> _ 0}) +)
(sqr x) acc))
=
(lambda (x acc)
( ( (lambda (kons)
(lambda (x acc) (if ({> _ 0} x) (kons x acc) acc)))
+)
(sqr x) acc))
=
(lambda (x acc)
( (lambda (x acc) (if (> x 0) (+ x acc) acc))
(sqr x) acc))
=
(lambda (x acc)
(let ([x (sqr x)] [acc acc])
(if (> x 0) (+ x acc) acc)))
который выглядит почти как что-то, что написал бы программист. В качестве упражнения,
((filtering {> _ 0}) ((mapping sqr) +))
=
( (lambda (kons)
(lambda (x acc) (if ({> _ 0} x) (kons x acc) acc)))
((mapping sqr) +))
=
(lambda (x acc)
(if (> x 0) (((mapping sqr) +) x acc) acc))
=
(lambda (x acc)
(if (> x 0) (+ (sqr x) acc) acc))
Таким образом, вместо того, чтобы самим писать определения объединенных функций редуктора, что, как и любая человеческая деятельность, подвержена ошибкам, мы можем составлять эти функции редуктора из более атомарных «преобразований», например, преобразователей.
Работает в DrRacket.
08.02.2018