I apologize for the unclear topic title.
I have this function in Scheme which is a custom implementation of the map
function. It works fine, but I got lost trying to understand it.
(define (my-map proc . ls)
(letrec ((iter (lambda (proc ls0)
(if (null? ls0)
'()
(cons (proc (car ls0))
(iter proc (cdr ls0))))))
(map-rec (lambda (proc ls0)
(if (memq '() ls0)
'()
(cons (apply proc (iter car ls0))
(map-rec proc (iter cdr ls0)))))))
(map-rec proc ls)))
The problem lays in cons (proc (car ls0))
. If I'm correct, when passing (1 2 3) (4 5 6)
to the ls
parameter the actual value of it will be ((1 2 3) (4 5 6))
. Therefore iter car ls0
in map-rec
will pass (1 2 3)
to iter
. Hence proc (car ls0)
in iter
will have the form: (car (car (1 2 3)))
, but this is impossible, right?
I know my thinking is flawed somewhere, but I can't figure out where.
Here's one way to understand the procedure:
- The
iter
helper is the same as map
, but operating on a single list.
- The
map-rec
helper generalizes iter
, working for a list of lists, stopping when at least one of the lists is empty
- This part:
(apply proc (iter car ls0))
applies the procedure on the first element of each list; the call to iter
creates a list of the car
part of the lists
- And this part:
(map-rec proc (iter cdr ls0))
simultaneously advances the recursion over all the lists; the call to iter
creates a list of the cdr
part of the lists
Perhaps renaming the procedures will make things clear. Here's a completely equivalent implementation, making explicit the fact that map-one
operates on a single list and map-many
operates on a list of lists:
(define (map-one proc lst) ; previously known as `iter`
(if (null? lst)
'()
(cons (proc (car lst))
(map-one proc (cdr lst)))))
(define (map-many proc lst) ; previously known as `map-rec`
(if (memq '() lst)
'()
(cons (apply proc (map-one car lst))
(map-many proc (map-one cdr lst)))))
(define (my-map proc . lst) ; variadic version of `map-many`
(map-many proc lst))
It works just like the original my-map
:
(my-map + '(1 2 3) '(4 5 6) '(7 8 9))
=> '(12 15 18)
And you can check that map-one
is really a map
that works on a single list:
(map-one (lambda (x) (* x x))
'(1 2 3 4 5))
=> '(1 4 9 16 25)
See the effect of (map-one car lst)
on a list of lists:
(map-one car '((1 4 5) (2 6 7) (3 8 9)))
=> '(1 2 3)
Likewise, see how (map-one cdr lst)
works:
(map-one cdr '((1 4 5) (2 6 7) (3 8 9)))
=> '((4 5) (6 7) (8 9))