First things first, I believe your foldl
implementation is a bit off, the usual way to implement it in Scheme would be (but make sure to read @JoshuaTaylor's comment to understand the consequences):
(define (foldl func accum lst)
(if (null? lst)
accum
(foldl func
(func (car lst) accum) ; here's the change
(cdr lst))))
About the first question: both folds consume the input list from left-to-right, but they differ in the way that the output result is built: foldl
accumulates the first input element at the beginning, and then accumulates the second element on top of the result, and so on; if we were building a list as output then the first element would be the last (and test your own implementation of foldl
with this example, to see why I say it's a bit off):
(foldl cons '() '(1 2 3 4 5))
=> '(5 4 3 2 1)
On the other hand, foldr
advances recursively until it reaches the empty list and then, as the recursion starts unwinding the last element gets accumulated first, and then the second-to-last on top of the result, and so on - thus preserving the same order of the input:
(foldr cons '() '(1 2 3 4 5))
=> '(1 2 3 4 5)
Regarding the second question: It'll be simpler to implement map
in terms of foldr
, because as we saw before it preserves input order. This is the same as yours, but with better formatting and variable names:
(define (map func lst)
(foldr (lambda (ele acc)
(cons (func ele) acc))
'()
lst))
But of course, we could use foldl
and reverse the result at the end (or alternatively: reverse the list before passing it to foldl
):
(define (map func lst)
(reverse
(foldl (lambda (ele acc)
(cons (func ele) acc))
'()
lst)))
What's the idea of having both foldl
and foldr
? It's a trade-off (and make sure to read @ChrisJester-Young's comments for a discussion of the implementation concerns from a performance point of view). foldr
will preserve the same order as the input when building the output, but it'll generate a recursive process that runs in space proportional to the size of the input: notice that func
is executed after invoking the recursive call.
On the other hand foldl
will build the output in reverse order, but it'll generate an iterative process that runs in constant space: notice that there is nothing left to do after invoking the recursive call, so it's tail-recursive and Scheme is optimized for efficiently handling this kind of recursion.