Looking for clarification on 'map' in Racket
Asked Answered
W

4

6

new to stackoverflow and new to racket. I've been studying racket using this documentation: https://docs.racket-lang.org/reference/pairs.html

This is my understanding of map:

(map (lambda (number) (+ 1 number))'(1 2 3 4))

this assigns '(1 2 3 4) to variable number ,then map performs (+ 1 '(1 2 3 4)).

but when I see things like:

(define (matrix_addition matrix_a matrix_b)
  (map (lambda (x y) (map + x y)) matrix_a matrix_b))

I get very lost. I assume we're assigning two variables x and y, then performing (map + x y),but I don't understand what or how (map + x y) works.

Another one I'm having trouble with is

(define (matrix_transpose matrix_a)
  (apply map (lambda x x) matrix_a))

what does (lambda x x) exactly do?

Thank you so much for clarifying. As you can see I've been working on matrix operations as suggested by a friend of mine.

Wilber answered 11/2, 2021 at 5:29 Comment(3)
this assigns '(1 2 3 4) to variable number No it doesn't, where did you get that idea?Boone
I guess I don't understand map at all then :(Wilber
you got great answers explaining all but the one last bit you asked about. that you can find explained in e.g. this answer (disclaimer - it is by me) and the one linked in it.Nervy
B
2

this assigns '(1 2 3 4) to variable number ,then map performs (+ 1 '(1 2 3 4)).

No, that's not what it does. map is a looping function, it calls the function separately for each element in the list, and returns a list of the results.

So first it binds number to 1 and performs (+ 1 number), which is (+ 1 1). Then it binds number to 2 and performs (+ 1 number), which is (+ 1 2). And so on. All the results are collected into a list, so it returns (2 3 4 5).

Getting to your matrix operation, the matrix is represented as a list of lists, so we need nested loops, which are done using nested calls to map.

(map (lambda (x y) (map + x y)) matrix_a matrix_b)

The outer map works as follows: First it binds x and y to the first elements of matrix_a and matrix_b respectively, and performs (map + x y). Then it binds x and y to the second elements of matrix_a and matrix_b, and performs (map + x y). And so on for each element of the two lists. Finally it returns a list of all these results.

The inner (map + x y) adds the corresponding elements of the two lists, returning a list of the sums. E.g. (map + '(1 2 3) '(4 5 6)) returns (5 7 9).

So all together this creates a list of lists, where each element is the sum of the corresponding elements of matrix_a and matrix_b.

Finally,

what does (lambda x x) exactly do?

It binds x to the list of all the arguments, and returns that list. So ((lambda x x) 1 2 3 4) returns the list (1 2 3 4). It's basically the inverse of apply, which spreads a list into multiple arguments to a function.

(apply (lambda x x) some-list)

returns a copy of some-list.

Boone answered 11/2, 2021 at 6:29 Comment(1)
Thank you so much! your explanation of how map works clarified everything for me :)Wilber
A
5

Here is one way to think about map:

(map f (list 1 2 3)) 
; computes 
(list (f 1) (f 2) (f 3))

and

(map f (list 1 2 3) (list 11 22 33)) 
; computes 
(list (f 1 11) (f 2 22) (f 3 33))

So your example with + becomes:

(map + (list 1 2 3) (list 11 22 33)) 
; computes 
(list (+ 1 11) (+ 2 22) (+ 3 33))

which is (list 12 24 36).

In the beginning it with be clearer to write

(define f (lambda (x y) (+ x y)))
(map f (list 1 2 3) (list 11 22 33)))

but when you can get used to map and lambda, the shorthand

(map (lambda (x y) (+ x y)) (list 1 2 3) (list 11 22 33)))

is useful.

Acme answered 11/2, 2021 at 7:55 Comment(0)
B
2

this assigns '(1 2 3 4) to variable number ,then map performs (+ 1 '(1 2 3 4)).

No, that's not what it does. map is a looping function, it calls the function separately for each element in the list, and returns a list of the results.

So first it binds number to 1 and performs (+ 1 number), which is (+ 1 1). Then it binds number to 2 and performs (+ 1 number), which is (+ 1 2). And so on. All the results are collected into a list, so it returns (2 3 4 5).

Getting to your matrix operation, the matrix is represented as a list of lists, so we need nested loops, which are done using nested calls to map.

(map (lambda (x y) (map + x y)) matrix_a matrix_b)

The outer map works as follows: First it binds x and y to the first elements of matrix_a and matrix_b respectively, and performs (map + x y). Then it binds x and y to the second elements of matrix_a and matrix_b, and performs (map + x y). And so on for each element of the two lists. Finally it returns a list of all these results.

The inner (map + x y) adds the corresponding elements of the two lists, returning a list of the sums. E.g. (map + '(1 2 3) '(4 5 6)) returns (5 7 9).

So all together this creates a list of lists, where each element is the sum of the corresponding elements of matrix_a and matrix_b.

Finally,

what does (lambda x x) exactly do?

It binds x to the list of all the arguments, and returns that list. So ((lambda x x) 1 2 3 4) returns the list (1 2 3 4). It's basically the inverse of apply, which spreads a list into multiple arguments to a function.

(apply (lambda x x) some-list)

returns a copy of some-list.

Boone answered 11/2, 2021 at 6:29 Comment(1)
Thank you so much! your explanation of how map works clarified everything for me :)Wilber
I
1

this assigns '(1 2 3 4) to variable number ,then map performs (+ 1 '(1 2 3 4)).

If it was that simple why the need for map. You could just do (+ 1 '(1 2 3 4)) directly. Here is an implementation of map1 which is map that can only have one list argument:

(define (map1 fn lst)
  (if (empty? lst)
      empty
      (cons (fn (first lst))
            (map1 f (rest lst)))))

And what it does:

(map1 add1 '(1 2 3))
; ==> (cons (add1 1) (cons (add1 2) (cons (add1 3) empty)))
; same as (list (add1 1) (add1 2) (add1 3))

The real map accepts any number of list arguments and then expect the element function to take as many elements that there are list arguments. eg.

(map (lambda (l n s) (list l n s)) '(a b c) '(1 2 3) '($ % *))
; ==> ((a 1 $) (b 2 %) (c 3 *))

A very cool way to do this without knowing the number of elements is unzip

(define (unzip . lst)
  (apply map list lst))

(unzip '(a b c) '(1 2 3) '($ % *))
; ==> ((a 1 $) (b 2 %) (c 3 *))

So apply flattens the call to (map list '(a b c) '(1 2 3) '($ % *)) and list takes arbitrary elements so it ends up working the same way as th eprevious example, but it will also work for other dimentions:

(unzip '(a b c d) '(1 2 3 4))
; ==> ((a 1) (b 2) (c 3) (d 4))
Ious answered 11/2, 2021 at 16:15 Comment(0)
D
1

The first argument of map is a function. This function can require one or more arguments. Followed by the function in the arguments lists are one or more lists.

map loops from first to the last element of the lists in parallel. And feeds therefore the i-th position of each list as arguments to the function and collects the result into a results list which it returns.

Now three short examples which would make it clear to you how map goes through the lists:

(map list '(1 2 3)) 
;; => '((1) (2) (3))

(map list '(1 2 3) '(a b c)) 
;; => '((1 a) (2 b) (3 c))

(map list '(1 2 3) '(a b c) '(A B C))
;; => '((1 a A) (2 b B) (3 c C))
Dead answered 12/2, 2021 at 9:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.