Reputation: 243
If I want to reverse a list in MIT Scheme, I can do it with (fold cons '() list)
such that if list is (define lis '(1 2 3 4))
, then (fold cons '() lis)
gives (4 3 2 1)
. There are two kinds of folds, left and right folds, but if I use (fold-right cons '() lis)
, I get (1 2 3 4)
so the plain fold can't be that one. Also if I use (fold-left cons '() lis)
I get ((((() . 1) . 2) . 3) . 4)
so that also cannot be the fold in the original example. what kind of fold is needed to reverse a list?
I ask because I want to be able to make a generic fold such that:
(define ls '((1 2 3) (4 5 6)))
(gen-fold cons '() ls)
=> ((6 5 4) (3 2 1))
(define l2 '(((1 2) (2 3)) ((3 4) (4 5))))
(gen-fold cons '() ls)
=> (((5 4) (4 3)) ((3 2) (2 1)))
Edit: Inorder to better present the questions, these diagrams depict the rotation of the list that I believe to be happening when '(fold cons '() '(1 2 3))
is called assuming that fold
is a modified fold-left
as per alexis king:
(define lis (list '(1 2 3)) cons / \ 1 cons / \ 2 cons / \ 3 '() (cons lis '()) cons / \ cons '() / \ 1 cons / \ 2 cons / \ 3 '() (fold-left (cons lis '())) cons / \ cons cons / \ / \ 2 cons 1 '() / \ 3 '() cons / \ cons cons / \ / \ 3 '() 2 cons / \ 1 '() cons / \ 3 cons / \ 2 cons / \ 1 '()
Upvotes: 1
Views: 360
Reputation: 43872
MIT Scheme does not include a fold
function in its base library, only fold-left
and fold-right
. However, there does exist a fold
function as declared by SRFI-1, which is supported by MIT Scheme and exhibits the behavior you describe.
The fold
function is a left fold—that is, it accumulates the list by left-to-right iteration—but it differs from MIT Scheme's fold-left
function in that the argument order of the accumulator procedure is flipped. That is, fold
applies the supplied procedure with the accumulator argument last, while fold-left
applies the procedure with the accumulator argument first.
To illustrate that point, this is how one could define fold
in terms of fold-left
:
(define (fold proc init lst)
(fold-left (lambda (x acc) (proc acc x)) init lst))
In my experience, the fold
argument order tends to be more common among Lisp implementations, while the fold-left
argument order tends to be more common among other functional languages. The reason for the acc
argument to be supplied last is for precisely the reason you discovered: it makes it easier to use fold
with cons
-like procedures that accept the accumulated value as their second argument.
As an aside, you appear to have mixed up fold-left
and fold-right
in your original question: it is fold-right
that returns the list unchanged and fold-left
that returns the inverted set of pairs. If you understand how fold-left
and fold-right
are defined, this makes sense.
In a certain sort of way, both fold-left
and fold-right
fold over lists starting from the left, since Scheme lists are singly-linked and cannot be read from the right. The difference is that left folding is iterative while right folding is recursive.
A left fold applies a procedure to each element one by one, then threads the result into the next application. This ends up reversing the order of the elements when viewed as nested applications:
(proc eN ... (proc e1 (proc e0 init)) ...)
In contrast, a right fold applies the procedure recursively, keeping the order consistent in nested applications:
(proc e0 (proc e1 ... (proc eN init) ...))
When used with cons
, the left fold reverses, but the right fold is just the identity.
(This is potentially more interesting in lazy languages like Haskell because right folding does not depend on the entire list despite supposedly starting "from the right", so it can operate on infinite streams... however, this is much less relevant in Scheme.)
Upvotes: 5