Understanding map pattern for n-ary operations on n lists in Scheme - scheme

I understand that (map f '(a b c d)) is '(f(a) f(b) f(c) f(d)) by applying function f to each element in the list. But the following seems to be hard to understand for me:
(map * '(1 2) '(1 2))
The output should be '(1 4). How come?
Can anyone explain how map pattern works in Scheme when we apply an n-ary operation to n lists?

map takes the ith elements of the provided lists, passes them to the n-ary operation, and stores the result at ith position of the returned list.
So, (map f '(a b c d) '(A B C D)) will be equal to ((f a A) (f b B) (f c C) (f d D)).
More than two lists are handled similarly.

'(f (a) f (b) f (c) f (d)) is and can only be (f (a) f (b) f (c) f (d)) after evaluation.
map for one list argument can be defined like this:
(define (map1 fn lst)
(if (null? lst)
'()
(cons (fn (car lst))
(map1 fn (cdr lst)))))
and (map add1 '(1 2 3)) can be substituted with
(cons (add1 '1)
(cons (add1 '2)
(cons (add1 '3)
'())))
; ==> (2 . (3 . (4 . ())))
; ==> (2 3 4)
Now map takes at least one list argument and it expects the function passed to take one or each. Thus (map * '(1 2) '(1 2)) is the same as:
(cons (* '1 '1)
(cons (* '2 '2)
'()))
; ==> (1 . (4 . ()))
; ==> (1 4)
I'm not entirely sure if you are having trouble grasping map or how lists are made. If it is the latter you should really make it top priority to be able to see (1 2 (3 4)) and understand that 3 is the caaddr since the pairs are (1 . (2 . ((3 . (4 . ())) . ()))). Read it right to left while looking at the pairs and you see it. If it's map then you need to implement them. It is the best way to learn it.

To truly understand something is best to implement it ourselves.
The map for 1-argument functions is easy, it's
(define (map1 f1 xs)
(cons (f1 (car xs))
(map1 f1
(cdr xs))))
(adding the base case is left as an exercise). Similarly for two,
(define (map2 f2 xs ys)
(cons (f2 (car xs) (car ys))
(map2 f2
(cdr xs) (cdr ys))))
and three,
(define (map3 f3 xs ys zs)
(cons (f3 (car xs) (car ys) (car zs))
(map3 f3
(cdr xs) (cdr ys) (cdr zs))))
We can use the same template for any n-argument function to be mapped over any n lists to take the n arguments from:
map_n:
xs: x1 x2 x3 x4 x5 ...
ys: y1 y2 y3 y4 y5 ...
zs: z1 z2 z3 z4 z5 ...
...........................
↓ ↓ ↓ ↓ ↓
f f f f f
= = = = =
results: r1 r2 r3 r4 r5 ...
related: Matrix multiplication in scheme, List of lists

Related

Map function with index as one of arguments in LISP

Is there any built-in function in LISP languages (or Racket in particular) that would work like map, but pass index of the element as one of the arguments to the mapping function?
Example of such function would be:
(define map-index (lambda (func list)
(map func list (build-list (length list) (lambda (i) i)))))
;usage:
> (map-index cons '(a b c d))
;output:
'((a . 0) (b . 1) (c . 2) (d . 3))
Obviously this is not a very efficient implementation and doesn't support multiple lists as arguments, like regular map does.
Racket
You can write a dead-simple version of map-index using the Racket range procedure and mapping over the result:
(define (map-index-1 f xs)
(map f xs (range (length xs))))
In some situations you might want the indices first:
(define (map-index-2 f xs)
(map f (range (length xs)) xs))
If you want to be able to use map-index over multiple lists, you can pass the list arguments to an optional parameter. Here, apply applies the map procedure to a list constructed from the function f, the input lists, and a range list:
(define (map-index-3 f . xs)
(apply map (cons f
(append xs
(list (range (length (car xs))))))))
But it might make more sense to place the indices first when mapping over multiple lists:
(define (map-index-4 f . xs)
(apply map (cons f
(cons (range (length (car xs)))
xs))))
scratch.rkt> (map-index-1 cons '(a b c d))
'((a . 0) (b . 1) (c . 2) (d . 3))
scratch.rkt> (map-index-2 cons '(a b c d))
'((0 . a) (1 . b) (2 . c) (3 . d))
scratch.rkt> (map-index-3 list '(a b c d) '(one two three four) '(w x y z))
'((a one w 0) (b two x 1) (c three y 2) (d four z 3))
scratch.rkt> (map-index-4 list '(a b c d) '(one two three four) '(w x y z))
'((0 a one w) (1 b two x) (2 c three y) (3 d four z))
Scheme
Standard Scheme does not have a built-in range procedure, but it is easy enough to write a simple version. These solutions will work on any R4RS, R5RS, R6RS, or R7RS Scheme implementation. This version of range does more than is required for the current application, taking a step argument which can be positive or negative:
(define (range start stop step)
(if (or (and (> step 0)
(>= start stop))
(and (<= step 0)
(<= start stop)))
'()
(cons start (range (+ start step) stop step))))
Having defined a range procedure, the same approach used for the Racket solutions above can be used in Scheme:
(define (map-index-5 f xs)
(map f xs (range 0 (length xs) 1)))
(define (map-index-6 f xs)
(map f (range 0 (length xs) 1) xs))
(define (map-index-7 f . xs)
(apply map (cons f
(append xs
(list (range 0 (length (car xs)) 1))))))
(define (map-index-8 f . xs)
(apply map (cons f
(cons (range 0 (length (car xs)) 1)
xs))))
> (map-index-5 cons '(a b c d))
((a . 0) (b . 1) (c . 2) (d . 3))
> (map-index-6 cons '(a b c d))
((0 . a) (1 . b) (2 . c) (3 . d))
> (map-index-7 list '(a b c d) '(one two three four) '(w x y z))
((a one w 0) (b two x 1) (c three y 2) (d four z 3))
> (map-index-8 list '(a b c d) '(one two three four) '(w x y z))
((0 a one w) (1 b two x) (2 c three y) (3 d four z))
A map-range Procedure for Standard Scheme
This method can be extended to use numbers in a more complex range that may not represent indices by taking advantage of the capabilities of a range function. Using the range definition from above, this map-range procedure still works on R4RS to R7RS Scheme implementations:
(define (map-range f start step . xs)
(let ((stop (+ start (* step (length (car xs))))))
(apply map (cons f
(cons (range start stop step)
xs)))))
> (map-range cons 2 2 '(a b c d))
((2 . a) (4 . b) (6 . c) (8 . d))
> (map-range list 5 5 '(a b c d) '(one two three four) '(w x y z))
((5 a one w) (10 b two x) (15 c three y) (20 d four z))
> (map-range cons 2 -2 '(a b c d))
((2 . a) (0 . b) (-2 . c) (-4 . d))
> (map-range list 5 -5 '(a b c d) '(one two three four) '(w x y z))
((5 a one w) (0 b two x) (-5 c three y) (-10 d four z))
Not exactly, but there are other things for similar goals, like for/list with in-naturals or in-indexed.
For example instead of (map-index f lst), the pattern would be
(for/list ([x lst] [i (in-naturals)]) (f x i))
or
(for/list ([(x i) (in-indexed lst)]) (f x i))
And either of those patterns could be used to implement map-index as well as your combination of map and build-list.
Racket's iteration forms like for/list are more flexible than a fixed set of map-like functions.
Concrete Examples:
> (for/list ([x '(a b c d)] [i (in-naturals)]) (cons x i))
'((a . 0) (b . 1) (c . 2) (d . 3))
> (for/list ([(x i) (in-indexed '(a b c d))]) (cons x i))
'((a . 0) (b . 1) (c . 2) (d . 3))
Or if you still want a map-index function you could define it more succinctly using this.
> (define (map-index f lst)
(for/list ([(x i) (in-indexed lst)]) (f x i)))
> (map-index cons '(a b c d))
'((a . 0) (b . 1) (c . 2) (d . 3))

Recursively adding to a list

The problem I have is that I have a list full of coordinates about a polygons corners called p1, p2, p3, p4. I need to recursively add them to a new list like this ((p1,p2) (p2,p3) (p3,p4) (p4,p1). And I have this list of them to start of (p1 p2 p3 p4). How do I recursively add them to a new list?
I am not expert in Racket, so take this solution with grain of salt. You can append the first value of list to end, and compute pairs by recursively.
#lang racket
(define (pair-list l)
(match l
[(cons a (cons b '())) (cons (cons a b) '())]
[(cons a (cons b t)) (cons (cons a b) (pair-list (cons b t)))]))
(define (compute-values l)
(pair-list (append l (cons (first l) '()))))
(compute-values (list 1 2 3 4))
'((1 . 2) (2 . 3) (3 . 4) (4 . 1))

filter function using tail recursion

Currently I have
(define filter
(λ (f xs)
(letrec [(filter-tail
(λ (f xs x)
(if (empty? xs)
x
(filter-tail f (rest xs)
(if (f (first xs))
(cons (first xs) x)
'()
)))))]
(filter-tail f xs '() ))))
It should be have as a filter function
However it outputs as
(filter positive? '(-1 2 3))
>> (3 2)
but correct return should be (2 3)
I was wondering if the code is correctly done using tail-recursion, if so then I should use a reverse to change the answer?
I was wondering if the code is correctly done using tail-recursion.
Yes, it is using a proper tail call. You have
(define (filter-tail f xs x) ...)
Which, internally is recursively applied to
(filter-tail f
(some-change-to xs)
(some-other-change-to x))
And, externally it's applied to
(filter-tail f xs '())
Both of these applications are in tail position
I should use a reverse to change the answer?
Yep, there's no way around it unless you're mutating the tail of the list (instead of prepending a head) as you build it. One of the comments you received alluded to this using set-cdr! (see also: Getting rid of set-car! and set-cdr!). There may be other techniques, but I'm unaware of them. I'd love to hear them.
This is tail recursive, requires the output to be reversed. This one uses a named let.
(define (filter f xs)
(let loop ([ys '()]
[xs xs])
(cond [(empty? xs) (reverse ys)]
[(f (car xs)) (loop (cons (car xs) ys) (cdr xs))]
[else (loop ys (cdr xs))])))
(filter positive? '(-1 2 3)) ;=> '(2 3)
Here's another one using a left fold. The output still has to be reversed.
(define (filter f xs)
(reverse (foldl (λ (x ys) (if (f x) (cons x ys) ys))
'()
xs)))
(filter positive? '(-1 2 3)) ;=> '(2 3)
With the "difference-lists" technique and curried functions, we can have
(define (fold c z xs)
(cond ((null? xs) z)
(else (fold c (c (car xs) z) (cdr xs)))))
(define (comp f g) (lambda (x) ; ((comp f g) x)
(f (g x))))
(define (cons1 x) (lambda (y) ; ((cons1 x) y)
(cons x y)))
(define (filter p xs)
((fold (lambda (x k)
(if (p x)
(comp k (cons1 x)) ; nesting's on the left
k))
(lambda (x) x) ; the initial continuation, IC
xs)
'()))
(display (filter (lambda (x) (not (zero? (remainder x 2)))) (list 1 2 3 4 5)))
This builds
comp
/ \
comp cons1 5
/ \
comp cons1 3
/ \
IC cons1 1
and applies '() to it, constructing the result list in the efficient right-to-left order, so there's no need to reverse it.
First, fold builds the difference-list representation of the result list in a tail recursive manner by composing the consing functions one-by-one; then the resulting function is applied to '() and is reduced, again, in tail-recursive manner, by virtues of the comp function-composition definition, because the composed functions are nested on the left, as fold is a left fold, processing the list left-to-right:
( (((IC+k1)+k3)+k5) '() ) ; writing `+` for `comp`
=> ( ((IC+k1)+k3) (k5 '()) ) ; and `kI` for the result of `(cons1 I)`
<= ( ((IC+k1)+k3) l5 ) ; l5 = (list 5)
=> ( (IC+k1) (k3 l5) )
<= ( (IC+k1) l3 ) ; l3 = (cons 3 l5)
=> ( IC (k1 l3) )
<= ( IC l1 ) ; l1 = (cons 1 l3)
<= l1
The size of the function built by fold is O(n), just like the interim list would have, with the reversal.

Any idea of how to interleave two lists in dr racket?

The problem is when lists have a different length, any idea of how to do it?
I have to use functions like map or something like that
This is the code I wrote so far, it works with lists of the same length but it also needs to work with lists of different lengths. Thank you.
(define (interleave list1 list2)
(flatten [map (lambda (x y) (cons x (cons y null))) list1 list2]))
if lists have different length this is what I get:
map: all lists must have same size; arguments were: # '(1 2 3 4 5) '(a b c)
I'm trying to get (1 a 2 b 3 c 4 5)
#lang racket
(define (weave xs ys)
(match (list xs ys)
[(list (cons x xs) (cons y ys)) (cons x (cons y (weave xs ys)))]
[(list '() ys) ys]
[(list xs '()) xs]))
I'm assuming your desired behavior is that the lists are interleaved for as long as this is possible, and then whatever is left over from the nonempty list is appended to the end. In that case one possible implementation is
(define (interleave a b)
(if (null? a)
b
(cons (car a)
(interleave b (cdr a)))))
I think this is probably the simplest possible way to write what you're looking for.
Neither map nor fold-right would work because they either signal an error when one list is smaller than the other or they tend to stop at the shortest list. eg. SRFI-1's map (interleave '(1 2 3 4) (circular-list 9 8)) ; ==> (1 9 2 8 3 9 4 8). For a different behavior you need to roll your own.
A solution using simple list manipulation functions might be:
(define (interleave list1 list2)
(cond ((empty? list1) list2)
((empty? list2) list1)
(else
(append
(list (car list1) (car list2))
(interleave (cdr list1) (cdr list2))))))
Testing...
> (interleave '(1 2 3 4 5) '(a b c))
(1 a 2 b 3 c 4 5)
> (interleave '(1 2 3 4 5) '())
(1 2 3 4 5)
> (interleave '() '(a b c))
(a b c)
>
I think it is fairly self-documenting.
"There ain't nothin' you can't not do with fold-right and some of them con-tin-uations thingies", said a cowboy to another, spittin' into the campfire and puffin' on his cigar in the evening, sippin' his black coffee from his rugged banged up tin mug. "Yessa, nothin' in the whole darn world."
(define (interleave xs ys)
;; interleave xs ys = foldr g n xs ys
;; where
;; g x r (y:ys) = x : y : r ys
;; g x r [] = x : r []
;; n ys = ys
((foldr
(lambda (x r)
(lambda (ys)
(cond ((null? ys) (cons x (r '())))
(else (apply (lambda (y . ys)
(cons x (cons y (r ys))))
ys)))))
(lambda (ys) ys)
xs)
ys))

Scheme Pairs output

Trying to produce an output of pairs that would look like (1 1) (1 2) (1 3) (2 2) (2 3) (3 3), if taking the first 6 elements of a stream. (The first 6 have 3 columns and it should print the pairs beginning with 1 and then 2 and then 3.) The code I have is:
(define (pairs s t)
(cons-stream (cons (stream-car s) (stream-car t))
(cons-stream
(stream-map (lambda (x) (cons (stream-car s) x))
(stream-cdr t))
(pairs (stream-cdr t) (stream-cdr s)))))
And if I run
(take 6 (pairs integers integers))
where take and integers are defined as follows:
(define (take n s)
(if (= n 0)
'()
(cons (stream-car s) (take (- n 1) (stream-cdr s)))))
(define integers (cons-stream 1 (add-streams ones integers)))
The result I get is:
((1 . 1)
((1 . 2) . #<promise>)
(2 . 2)
((2 . 3) . #<promise>)
(3 . 3)
((3 . 4) . #<promise>))
In Scheme,
(define (interleaved-pairs xs ys . args)
(let ((x (stream-car xs))
(ac (if (null? args) () (car args))))
(stream-append
(stream-map stream-car (list->stream (reverse ac)))
(stream-cons (list x (stream-car ys))
(interleaved-pairs
(stream-cdr xs)
(stream-cdr ys)
(cons
(stream-map (lambda(y)(list x y)) (stream-cdr ys))
(map stream-cdr ac)))))))
This should produce results in the order you wanted: (1 1) (1 2) (2 2) (1 3) (2 3) (3 3) (1 4) ....
You tagged this as racket also. As far as I can see in the Racket documentation, it has stream-first in place of stream-car, etc. For some reason it doesn't seem to have list->stream, which can be defined quite straightforwardly with the apply and stream functions.
Here it is in a shorter notation.
ipairs xs ys = g xs ys [] where
g (x:xs) (y:ys) ac = map head (reverse ac) ++ (x,y) :
g xs ys (map ((,) x) ys : map tail ac)

Resources