What is the difference between map and apply in scheme? - scheme

I am trying to learn Scheme and I am having a hard time understanding the difference between map and apply.
As I understand, map applies the function to each element of the list, and apply applies something to the arguments of a procedure.
Can they be used interchangeably?

They are not the same! Their names can actually help remember which does what.
map will take as argument one procedure and one or more lists.
The procedure will be called once for each position of the lists, using as arguments the list of elements at that position:
(map - '(2 3 4))
; => (-2 -3 -4)
map called (- 2), (- 3), (- 4) to build the list.
(map + '( 1 2 3)
'(10 20 30))
; => (11 22 33)
map called (+ 1 10) (+ 2 20) (+ 3 30) to build the list.
(map * '(2 2 -1)
'(0 3 4)
'(5 4 2))
; => (0 24 -8)
map called (* 2 0 5) (* 2 3 4) (* -1 4 2) to build the list.
map has that name because it implements a "map" (function) on a set of values (in the lists):
(map - '(2 3 4))
arguments mapping "-" result
2 === (- 2) ===> -2
3 === (- 3) ===> -3
4 === (- 4) ===> -4
(map + '( 1 2 3)
'(10 20 30))
arguments mapping "+" result
1 10 === (+ 1 10) ===> 11
2 20 === (+ 2 20) ===> 22
3 30 === (+ 3 30) ===> 33
apply will take at least two arguments, the first of them being a procedure and the last a list. It will call the procedure with the following arguments, including those inside the list:
(apply + '(2 3 4))
; => 9
This is the same as (+ 2 3 4)
(apply display '("Hello, world!"))
; does not return a value, but prints "Hello, world!"
This is the same as (display "Hello, world!").
apply is useful when you have arguments as a list,
(define arguments '(10 50 100))
(apply + arguments)
If you try to rewrite the last line without using apply, you'll realize that you need to loop over the list summing each element...
apply may also be used with more than those two arguments. The first argument must be a callable object (a procedure or a continuation). The last one must be a list. The others (between the first and the last) are objects of any type. So calling
(apply PROC a b c ... y z '(one two ... twenty))
is the same as calling
(PROC a b c ... y z one two ... twenty)
Here's a concrete example:
(apply + 1 -2 3 '(10 20))
; => 32
This is the same as (+ 1 -2 3 10 20)
apply has that name because it allows you to "apply" a procedure to several arguments.

No, apply calls its first argument as a procedure, with all the rest as its arguments, with the last one -- list -- opened up, i.e. its contents "spliced in":
(apply f a b (list c d e)) == (f a b c d e)
E.g.:
(apply + 1 2 (list 3 4 5))
;Value: 15
It is just one call; whereas map is indeed calling its first argument for each member element of its second argument.
One combined use of map and apply is the famous transpose trick:
(apply map list '((1 2 3) (10 20 30)))
;Value: ((1 10) (2 20) (3 30))

As the top answer suggested, map
The procedure will be called once for each position of the lists, using as arguments the list of elements at that position
In contrast, apply
(apply function argument-list)
pass arguments in argument-list to function all at once. So function is called only once.

Related

Looking for clarification on 'map' in Racket

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.
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.
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.
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))
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))

Matrix Manipulation in Racket

I need to write a function that produces the nth column of a matrix in Racket without using recursion. For example (list (list 1 2 3) (list 2 3 4) (list 6 7 9)) if I wanted the 2nd column I would receive (list 2 3 7).
I tried (append (map (lambda (n) (list-ref (list-ref M) n) n)) M)), but it keeps showing me an error.
The attempted solution uses list-ref twice, which is not needed, and one of those calls does not have enough arguments: (list-ref M) has the function taking only one argument, but it requires two arguments -- a list and an integer.
The list-ref function will return an indexed element from an input list, so (list-ref '(1 2 3) 1) will return 2. Consider what map will do here: the matrix is represented as a list of lists (a list of rows), i.e., as ((1 2 3) (2 3 4) (6 7 9)). The map function will act on the members of the input list, which are the lists (1 2 3), (2 3 4), and (6 7 9). By mapping the list-ref function over that input, you can take whichever element you want from the sublists. So, (map (lambda (row) (list-ref row 1)) '((1 2 3) (2 3 4) (6 7 9))) would evaluate to (2 3 7), as desired.
Here is a function that lets you take any column from a matrix:
(define (nth-column M n)
(map (lambda (row) (list-ref row n)) M))
Sample interactions:
scratch.rkt> (define M '((1 2 3)
(2 3 4)
(6 7 9)))
scratch.rkt> (nth-column M 1)
'(2 3 7)
scratch.rkt> (nth-column M 0)
'(1 2 6)
scratch.rkt> (nth-column M 2)
'(3 4 9)

Adding Elements In a List Scheme

In Scheme, I am trying to iterate through a list adding each element from a portion of the list.
For example, if I had ((1 2 5) (1 2) (1 5) (1) (2 5) (2) (5) ()) for a list, I am trying to add the first part ( 1 2 5 ) and then the second part ( 1 2 ) and so on in order to see if each element adds up to a specific number.
Hope this makes sense, tried my best to explain this.
If someone could help me figure this problem out, I would really appreciate it.
To add the numbers of a (small) list you can use apply:
> (apply + '(1 2 3))
6
which is the same as
> (+ 1 2 3)
6
In order to apply this to a list of sublists, use map:
(define (f lst)
(map (lambda (sublst) (apply + sublst))
lst))
> (f '((1 2 5) (1 2) (1 5) (1) (2 5) (2) (5) ()))
'(8 3 6 1 7 2 5 0)
For larger list you might want to replace apply by foldl or equivalent.

How to use foldr in scheme?

When you use foldr, the procedure you use has 2 arguments, the current value of the list and the accumulator. Let's say the list you iterate over is a list of list of numbers, all the same length. Then as you iterate through them, you want to multiply the numbers of the same index and store it as the accumulator.
If you use lambda (x acc) (map * x acc) inside the foldr, this fails because acc I believe is an empty list in the beginning. How can you handle the base case like this?
This can be solved using foldr all right, the trick is to correctly initialize the accumulated value at the beginning. No need to do fancy stuff (like macros) here!
(define lst '((1 2 3) (2 3 5) (3 5 7)))
(foldr (lambda (x acc) (map * x acc))
(car lst)
(cdr lst))
=> '(6 30 105)
Of course, if the list is empty (car lst) will fail. So you might want to handle the empty list as a separate case before invoking foldr.
Say you have a list of lists as follows:
((1 2 3) (2 3 5) (3 5 7))
You want to reduce it to:
(6 30 105)
I would simple do:
(define-syntax mul
(syntax-rules ()
((_ (lists ...)) (map * 'lists ...))))
The you can use it as follows:
(mul ((1 2 3) (2 3 5) (3 5 7))) ; => (6 30 105)
The above code simply expands to:
(map * '(1 2 3) '(2 3 5) '(3 5 7))
Then you can fold the resulting list. For example:
(foldr + 0 (mul ((1 2 3) (2 3 5) (3 5 7)))) ; => 141

Applying apply in Scheme

What am I missing here? I was playing with apply in Scheme, and wrote:
(apply apply '(+ (1 2 3)))
The way I understand it, the first apply should do:
(apply + '(1 2 3))
and the second should do:
(+ 1 2 3)
But both Ypsilon and Gauche give about the same error (this is Ypsilon's):
error: attempt call non-procedure: (+ 1 2 3)
backtrace:
0 (apply apply '(+ (1 2 3)))
..."/dev/stdin" line 1
What have I failed to understand?
The problem with '(+ (1 2 3)) is that the + is quoted and thus interpreted as a symbol.
You would have to use eval to get a value for the + symbol.
In other words, what you are trying to do, is not going to work.
Edit: Another option is quasiquote. Eg:
(apply apply `(,+ (1 2 3))) ; => 6
Or (without quasiquote)
(apply apply (list + '(1 2 3))); => 6

Resources