Does "map" necessarily produce an additional level of nesting? - scheme

Does using nested maps automatically create another level of nesting? Here is a basic example that I used:
; One level
(map (lambda (x1)
"Hi")
'(1))
; Two levels
(map (lambda (x1)
(map (lambda (x2)
"Hi")
'(1)))
'(1))
; Three levels
(map (lambda (x1)
(map (lambda (x2)
(map (lambda (x3)
"Hi")
'(1)))
'(1)))
'(1))
; Four levels
(map (lambda (x1)
(map (lambda (x2)
(map (lambda (x3)
(map (lambda (x4)
"Hi")
'(1)))
'(1)))
'(1)))
'(1))
("Hi")
(("Hi"))
((("Hi")))
(((("Hi"))))
Or, are there any counterexamples where adding another map will not produce additional nesting? I'm having trouble 'getting' how map adds another level. For example, for the first level:
(map (lambda (x1)
"Hi")
'(1))
I understand that there is one element in the list, and 'for each element' in the list -- we will return the element "Hi"at that position in the list, so for the first level we get ("Hi").
But then how, for example, from the list ("Hi") do we get a nested list at the second level? I know I've asked a lot of questions related to map and nesting, but hopefully if I can just understand the going from 1 level to 2 I can figure out the rest...

map collects the return values of all the function calls, and wraps them in another list.
If the function returns a list, you'll get a list of lists.
So if you have nested maps, you always get nested lists as the final result. And each level of mapping adds another level of nesting.
Note that this is only true if you're actually returning the value of map at each level. You could do other things with it, e.g.
;; Two levels
(map (lambda (x1)
(length (map (lambda (x2)
"Hi")
'(1))))
'(1))
This will return (1)

one-to-one
Your examples include (map (lambda (x) x) ...)
(map (lambda (ignore-me)
(map (lambda (x) x) ; <- no-op
'(1 2 3)))
'(1))
((1 2 3))
It's "hard to see" because the inner map is essentially a no-op. map creates a 1:1 relationship between the input and the output, with no opinions about what the mapping procedure does. If the mapping procedure returns a single element, a list, or a very nested list, it doesn't matter -
(map (lambda (ignore-me)
'(1 2 3))
'(foo))
((1 2 3))
(map (lambda (_)
'(((((really nested))))))
'(1 2 3))
((((((really nested)))))
(((((really nested)))))
(((((really nested))))))
Or with some ascii visualization -
(define (fn x) (+ x x))
(map fn '(1 2 3))
; \ \ \
; \ \ (fn 3)
; \ \ \
; \ (fn 2) \
; \ \ \
; (fn 1) \ \
; \ \ \
; output: '(2 4 6)
Should you choose to map inside of the mapping procedure to another map call -
(map (lambda (...)
(map ...)) ; <- nested map
...)
The same rules apply. Each map output has a 1:1 relationship with its input list. We can imagine map's type for more insight -
map : ('a -> 'b, 'a list) -> 'b list
-------- ------- -------
\ \ \_ returns a list of type 'b
\ \
\ \__ input list of type 'a
\
\__ mapping procedure, maps
one element of type 'a
to one element of type 'b
implementing map
As a learning exercise, it's useful to implement map to gain an intimate understanding of how it works -
(define (map fn lst)
(if (null? lst)
'()
(cons (fn (car lst))
(map fn (cdr lst)))))
(map (lambda (x)
(list x x x x x))
'(1 2 3))
((1 1 1 1 1)
(2 2 2 2 2)
(3 3 3 3 3))
As you can see, map does not have an opinion on the type of elements in lst or what the return value of fn is. map plainly passes the car of the list to fn and cons'es it onto the recursive result of the cdr of the list.
append-map
Another relevant procedure we should look at is append-map -
append-map : ('a -> 'b list, 'a list) -> 'b list
------------- ------- -------
\ \ \_ returns a list of type 'b
\ \_ input list of type 'a
\
\_ mapping function, maps
one element of type 'a
to a LIST of elements of type 'b
The only difference here is the mapping procedure is expected to return a list of elements, not just a single element. In this way, append-map creates a 1:many relationship between in the input list and the output list.
(append-map (lambda (x)
(list x x x x x))
'(1 2 3))
(1 1 1 1 1 2 2 2 2 2 3 3 3 3 3)
This characteristic of append-map is why it is sometimes called flatmap, as it "flattens" a level of nesting -
(append-map (lambda (x)
(map (lambda (y)
(list x y))
'(spade heart club diamond)))
'(J Q K A))
((J spade)
(J heart)
(J club)
(J diamond)
(Q spade)
(Q heart)
(Q club)
(Q diamond)
(K spade)
(K heart)
(K club)
(K diamond)
(A spade)
(A heart)
(A club)
(A diamond))
Follow-up exercises for the reader:
What would the output be if we traded append-map for map in the example above?
Define map in another way or two. Verify the correct behaviour
Define append-map in terms of map. Define it again without using map.
loose typing
Scheme is an untyped language and thus a list's contents are not required to be homogenous. Still, it's useful to think about map and append-map in this way as the type helps communicate how the function will behave. Here are more accurate type definitions provided by Racket -
(map proc lst ...+) → list?
proc : procedure?
lst : list?
(append-map proc lst ...+) → list?
proc : procedure?
lst : list?
These are much looser and do reflect the loose programs you can actually write. For example -
(append-map (lambda (x)
(list 'a-symbol "hello" 'float (* x 1.5) 'bool (> x 1)))
'(1 2 3))
(a-symbol "hello" float 1.5 bool #f a-symbol "hello" float 3.0 bool #t a-symbol "hello" float 4.5 bool #t)
variadic map and append-map
Do you see those ...+ in the types above? For the sake of simplicity, I've been hiding an important detail up until this point. The variadic interface of map means that it can accept 1 or more input lists -
(map (lambda (x y z)
(list x y z))
'(1 2 3)
'(4 5 6)
'(7 8 9))
((1 4 7) (2 5 8) (3 6 9))
(append-map (lambda (x y z)
(list x y z))
'(1 2 3)
'(4 5 6)
'(7 8 9))
(1 4 7 2 5 8 3 6 9)
Follow-up exercises for the reader:
Define variadic map
Define variadic append-map
a touch of lambda calculus
You are aware of lambda calculus. Did you know (lambda (x y z) (list x y z) is the same as list? This is known as Eta reduction -
(map list '(1 2 3) '(4 5 6) '(7 8 9))
((1 4 7) (2 5 8) (3 6 9))
(append-map list '(1 2 3) '(4 5 6) '(7 8 9))
(1 4 7 2 5 8 3 6 9)
delimited continuations
Remember when we talked about delimited continuations and operators shift and reset? After learning about append-map, let's see amb, the ambiguous choice operator -
(define (amb . lst)
(shift k (append-map k lst)))
We can use amb like this -
(reset (list (amb 'J 'Q 'K 'A) (amb '♡ '♢ '♤ '♧)))
(J ♡ J ♢ J ♤ J ♧ Q ♡ Q ♢ Q ♤ Q ♧ K ♡ K ♢ K ♤ K ♧ A ♡ A ♢ A ♤ A ♧)
(reset (list (list (amb 'J 'Q 'K 'A) (amb '♡ '♢ '♤ '♧))))
((J ♡) (J ♢) (J ♤) (J ♧) (Q ♡) (Q ♢) (Q ♤) (Q ♧) (K ♡) (K ♢) (K ♤) (K ♧) (A ♡) (A ♢) (A ♤) (A ♧))
In a more useful example, we use the pythagorean theorem to find-right-triangles -
(define (pythag a b c)
(= (+ (* a a) (* b b)) (* c c))) ; a² + b² = c²
(define (find-right-triangles . side-lengths)
(filter ((curry apply) pythag)
(reset (list
(list (apply amb side-lengths)
(apply amb side-lengths)
(apply amb side-lengths))))))
(find-right-triangles 1 2 3 4 5 6 7 8 9 10 11 12)
((3 4 5) (4 3 5) (6 8 10) (8 6 10))
Delimited continuations effectively turn your program inside-out, allowing you to focus on declarative structure -
(define (bit)
(amb #\0 #\1))
(define (4-bit)
(reset (list (string (bit) (bit) (bit) (bit)))))
(4-bit)
("0000" "0001" "0010" "0011" "0100" "0101" "0110" "0111" "1000" "1001" "1010" "1011" "1100" "1101" "1110" "1111")
amb can be used anywhere in any expression to evaluate the expression once per supplied value. This limitless use can yield extraordinary results -
(reset
(list
(if (> 5 (amb 6 3))
(string-append "you have "
(amb "dark" "long" "flowing")
" "
(amb "hair" "sentences"))
(string-append "please do not "
(amb "steal" "hurt")
" "
(amb "any" "other")
" "
(amb "people" "animals")))))
("please do not steal any people"
"please do not steal any animals"
"please do not steal other people"
"please do not steal other animals"
"please do not hurt any people"
"please do not hurt any animals"
"please do not hurt other people"
"please do not hurt other animals"
"you have dark hair"
"you have dark sentences"
"you have long hair"
"you have long sentences"
"you have flowing hair"
"you have flowing sentences")

The map will return a list for each element in that list. Let us go through a multi-map example where we can show how nesting comes into play.
First, let's do a single map on a sequence of numbers and "do nothing" to them. That is, just return the element from the input:
(map (lambda (x) x)
'(1 2 3))
; (1 2 3)
At this point, it may be easy to think that another level of nesting should not occur if we add on another map since here we have not 'nested' the input list. It has simply 'stayed' at (1 2 3).
But if we add an outer level, we can see how this may occur:
(map (lambda (ignore-me)
(map (lambda (x) x)
'(1 2 3)))
'(1))
; (( 1 2 3 ))
With just one 'outer-element' it's hard to see this, but instead, let's add 5 outer elements and make it much easier to spot:
(map (lambda (ignore-me)
(map (lambda (x) x)
'(1 2 3)))
'(5 5 5 5 5))
;(
; (1 2 3)
; (1 2 3)
; (1 2 3)
; (1 2 3)
; (1 2 3)
;)
Here we can see for each outer element -- and there are five of them -- we are returning the "inner-result" -- which is (1 2 3) in the above case. If we did the same thing again, this time producing two additional outer loops, we can see a similar result occurring:
(map (lambda (ignore-me1)
(map (lambda (ignore-me2)
(map (lambda (x) x)
'(1 2 3)))
'(5 5 5 5 5)))
'(2 2))
; (
; ((1 2 3) (1 2 3) (1 2 3) (1 2 3) (1 2 3))
; ((1 2 3) (1 2 3) (1 2 3) (1 2 3) (1 2 3))
; )

Related

Combinations with pairs

I am trying to combine a list of pairs in scheme to get all possible combinations. For example:
((1 2) (3 4) (5 6)) --> ((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))
I've been able to solve it (I think) using a "take the first and prepend it to the cdr of the procedure" with the following:
(define (combine-pair-with-list-of-pairs P Lp)
(apply append
(map (lambda (num)
(map (lambda (pair)
(cons num pair)) Lp)) P)))
(define (comb-N Lp)
(if (null? Lp)
'(())
(combine-pair-with-list-of-pairs (car Lp) (comb-N (cdr Lp)))))
(comb-N '((1 2)(3 4)(5 6)))
; ((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))
However, I've been having trouble figuring out how I can use a procedure that only takes two and having a wrapper around it to be able to define comb-N by calling that function. Here it is:
(define (combinations L1 L2)
(apply append
(map (lambda (L1_item)
(map (lambda (L2_item)
(list L1_item L2_item))
L2))
L1)))
(combinations '(1) '(1 2 3))
; ((1 1) (1 2) (1 3))
I suppose the difficulty with calling this function is it expects two lists, and the recursive call is expecting a list of lists as the second argument. How could I call this combinations function to define comb-N?
difficulty? recursion? where?
You can write combinations using delimited continuations. Here we represent an ambiguous computation by writing amb. The expression bounded by reset will run once for each argument supplied to amb -
(define (amb . lst)
(shift k (append-map k lst)))
(reset
(list (list (amb 'a 'b) (amb 1 2 3))))
((a 1) (a 2) (a 3) (b 1) (b 2) (b 3))
how it works
The expression is evaluated through the first amb where the continuation is captured to k -
k := (list (list ... (amb 1 2 3)))
Where applying k will supply its argument to the "hole" left by amb's call to shift, represented by ... above. We can effectively think of k in terms of a lambda -
k := (lambda (x) (list (list x (amb 1 2 3)))
amb returns an append-map expression -
(append-map k '(a b))
Where append-map will apply k to each element of the input list, '(a b), and append the results. This effectively translates to -
(append
(k 'a)
(k 'b))
Next expand the continuation, k, in place -
(append
(list (list 'a (amb 1 2 3))) ; <-
(list (list 'b (amb 1 2 3)))) ; <-
Continuing with the evaluation, we evaluate the next amb. The pattern is continued. amb's call to shift captures the current continuation to k, but this time the continuation has evolved a bit -
k := (list (list 'a ...))
Again, we can think of k in terms of lambda -
k := (lambda (x) (list (list 'a x)))
And amb returns an append-map expression -
(append
(append-map k '(1 2 3)) ; <-
(list (list 'b ...)))
We can continue working like this to resolve the entire computation. append-map applies k to each element of the input and appends the results, effectively translating to -
(append
(append (k 1) (k 2) (k 3)) ; <-
(list (list 'b ...)))
Expand the k in place -
(append
(append
(list (list 'a 1)) ; <-
(list (list 'a 2)) ; <-
(list (list 'a 3))) ; <-
(list (list 'b (amb 1 2 3))))
We can really start to see where this is going now. We can simplify the above expression to -
(append
'((a 1) (a 2) (a 3)) ; <-
(list (list 'b (amb 1 2 3))))
Evaluation now continues to the final amb expression. We will follow the pattern one more time. Here amb's call to shift captures the current continuation as k -
k := (list (list 'b ...))
In lambda terms, we think of k as -
k := (lambda (x) (list (list 'b x)))
amb returns an append-map expression -
(append
'((a 1) (a 2) (a 3))
(append-map k '(1 2 3))) ; <-
append-map applies k to each element and appends the results. This translates to -
(append
'((a 1) (a 2) (a 3))
(append (k 1) (k 2) (k 3))) ; <-
Expand k in place -
(append
'((a 1) (a 2) (a 3))
(append
(list (list 'b 1)) ; <-
(list (list 'b 2)) ; <-
(list (list 'b 3)))) ; <-
This simplifies to -
(append
'((a 1) (a 2) (a 3))
'((b 1) (b 2) (b 3))) ; <-
And finally we can compute the outermost append, producing the output -
((a 1) (a 2) (a 3) (b 1) (b 2) (b 3))
generalizing a procedure
Above we used fixed inputs, '(a b) and '(1 2 3). We could make a generic combinations procedure which applies amb to its input arguments -
(define (combinations a b)
(reset
(list (list (apply amb a) (apply amb b)))))
(combinations '(a b) '(1 2 3))
((a 1) (a 2) (a 3) (b 1) (b 2) (b 3))
Now we can easily expand this idea to accept any number of input lists. We write a variadic combinations procedure by taking a list of lists and map over it, applying amb to each -
(define (combinations . lsts)
(reset
(list (map (lambda (each) (apply amb each)) lsts))))
(combinations '(1 2) '(3 4) '(5 6))
((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))
Any number of lists of any length can be used -
(combinations
'(common rare)
'(air ground)
'(electric ice bug)
'(monster))
((common air electric monster)
(common air ice monster)
(common air bug monster)
(common ground electric monster)
(common ground ice monster)
(common ground bug monster)
(rare air electric monster)
(rare air ice monster)
(rare air bug monster)
(rare ground electric monster)
(rare ground ice monster)
(rare ground bug monster))
related reading
In Scheme, we can use Olivier Danvy's original implementation of shift/reset. In Racket, they are supplied via racket/control
(define-syntax reset
(syntax-rules ()
((_ ?e) (reset-thunk (lambda () ?e)))))
(define-syntax shift
(syntax-rules ()
((_ ?k ?e) (call/ct (lambda (?k) ?e)))))
(define *meta-continuation*
(lambda (v)
(error "You forgot the top-level reset...")))
(define abort
(lambda (v)
(*meta-continuation* v)))
(define reset-thunk
(lambda (t)
(let ((mc *meta-continuation*))
(call-with-current-continuation
(lambda (k)
(begin
(set! *meta-continuation* (lambda (v)
(begin
(set! *meta-continuation* mc)
(k v))))
(abort (t))))))))
(define call/ct
(lambda (f)
(call-with-current-continuation
(lambda (k)
(abort (f (lambda (v)
(reset (k v)))))))))
For more insight on the use of append-map and amb, see this answer to your another one of your questions.
See also the Compoasable Continuations Tutorial on the Scheme Wiki.
remarks
I really struggled with functional style at first. I cut my teeth on imperative style and it took me some time to see recursion as the "natural" way of thinking to solve problems in a functional way. However I offer this post in hopes to provoke you to reach for even higher orders of thinking and reasoning. Recursion is the topic I write about most on this site but I'm here saying that sometimes even more creative, imaginative, declarative ways exist to express your programs.
First-class continuations can turn your program inside-out, allowing you to write a program which manipulates, consumes, and multiplies itself. It's a sophisticated level of control that's part of the Scheme spec but only fully supported in a few other languages. Like recursion, continuations are a tough nut to crack, but once you "see", you wish you would've learned them earlier.
As suggested in the comments you can use recursion, specifically, right fold:
(define (flatmap foo xs)
(apply append
(map foo xs)))
(define (flatmapOn xs foo)
(flatmap foo xs))
(define (mapOn xs foo)
(map foo xs))
(define (combs L1 L2) ; your "combinations", shorter name
(flatmapOn L1 (lambda (L1_item)
(mapOn L2 (lambda (L2_item) ; changed this:
(cons L1_item L2_item)))))) ; cons NB!
(display
(combs '(1 2)
(combs '(3 4)
(combs '(5 6) '( () )))))
; returns:
; ((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))
So you see, the list that you used there wasn't quite right, I changed it back to cons (and thus it becomes fully the same as combine-pair-with-list-of-pairs). That way it becomes extensible: (list 3 (list 2 1)) isn't nice but (cons 3 (cons 2 (cons 1 '()))) is nicer.
With list it can't be used as you wished: such function receives lists of elements, and produces lists of lists of elements. This kind of output can't be used as the expected kind of input in another invocation of that function -- it would produce different kind of results. To build many by combining only two each time, that combination must produce the same kind of output as the two inputs. It's like +, with numbers. So either stay with the cons, or change the combination function completely.
As to my remark about right fold: that's the structure of the nested calls to combs in my example above. It can be used to define this function as
(define (sequence lists)
(foldr
(lambda (list r) ; r is the recursive result
(combs list r))
'(()) ; using `()` as the base
lists))
Yes, the proper name of this function is sequence (well, it's the one used in Haskell).

The apply function in SICP/Scheme

I've asked a few questions here about Scheme/SICP, and quite frequently the answers involve using the apply procedure, which I haven't seen in SICP, and in the book's Index, it only lists it one time, and it turns out to be a footnote.
Some examples of usage are basically every answer to this question: Going from Curry-0, 1, 2, to ...n.
I am interested in how apply works, and I wonder if some examples are available. How could the apply procedure be re-written into another function, such as rewriting map like this?
#lang sicp
(define (map func sequence)
(if (null? sequence) nil
(cons (func (car sequence)) (map func (cdr sequence)))))
It seems maybe it just does a function call with the first argument? Something like:
(apply list '(1 2 3 4 5)) ; --> (list 1 2 3 4 5)
(apply + '(1 2 3)) ; --> (+ 1 2 3)
So maybe something similar to this in Python?
>>> args=[1,2,3]
>>> func='max'
>>> getattr(__builtins__, func)(*args)
3
apply is used when you want to call a function with a dynamic number of arguments.
Your map function only allows you to call functions that take exactly one argument. You can use apply to map functions with different numbers of arguments, using a variable number of lists.
(define (map func . sequences)
(if (null? (car sequences))
'()
(cons (apply func (map car sequences))
(apply map func (map cdr sequences)))))
(map + '(1 2 3) '(4 5 6))
;; Output: (5 7 9)
You asked to see how apply could be coded, not how it can be used.
It can be coded as
#lang sicp
; (define (appl f xs) ; #lang racket
; (eval
; (cons f (map (lambda (x) (list 'quote x)) xs))))
(define (appl f xs) ; #lang r5rs, sicp
(eval
(cons f (map (lambda (x) (list 'quote x))
xs))
(null-environment 5)))
Trying it out in Racket under #lang sicp:
> (display (appl list '(1 2 3 4 5)))
(1 2 3 4 5)
> (display ( list 1 2 3 4 5 ))
(1 2 3 4 5)
> (appl + (list (+ 1 2) 3))
6
> ( + (+ 1 2) 3 )
6
> (display (appl map (cons list '((1 2 3) (10 20 30)))))
((1 10) (2 20) (3 30))
> (display ( map list '(1 2 3) '(10 20 30) ))
((1 10) (2 20) (3 30))
Here's the link to the docs about eval.
It requires an environment as the second argument, so we supply it with (null-environment 5) which just returns an empty environment, it looks like it. We don't actually need any environment here, as the evaluation of the arguments has already been done at that point.

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

Creating an evaluate function in racket

Example of what function should do:
(list 3 4 6 9 7) ←→ 3x^4 + 4x^3 + 6x^2 + 9x + 7
What I have so far:
(define (poly-eval x numlist)
(compute-poly-tail x numlist 0 0))
(define (compute-poly-tail xn list n acc)
(cond
[(null? list) acc]
[else (compute-poly-tail (first list) (rest list)
(+ acc (* (first list) (expt xn n))) (+ n 1))]))
(check-expect(poly-eval 5 (list 1 0 -1)) 24)
(check-expect(poly-eval 0 (list 3 4 6 9 7)) 7)
(check-expect(poly-eval 2 (list 1 1 0 1 1 0)) 54)
Expected results:
(check-expect(poly-eval 5(list 1 0 -1)) 24)
(check-expect(poly-eval 0 (list 3 4 6 9 7))7)
(check-expect(poly-eval 2 (list 1 1 0 1 1 0)) 54)
I am getting a run-time error. Can someone spot what I am doing wrong. I don't know why I am getting these results.
There are a couple of errors in the code:
You need to process the coefficient's list in the correct order, corresponding to their position in the polynomial! you can either:
reverse the list from the beginning and process the coefficients from right to left (simpler).
Or start n in (sub1 (length numlist)) and decrease it at each iteration (that's what I did).
The order and value of the arguments when calling the recursion in compute-poly-tail is incorrect, check the procedure definition, make sure that you pass along the values in the same order as you defined them, also the first call to (first list) doesn't make any sense.
You should not name list a parameter, this will clash with the built-in procedure of the same name. I renamed it to lst.
This should fix the issues:
(define (poly-eval x numlist)
(compute-poly-tail x numlist (sub1 (length numlist)) 0))
(define (compute-poly-tail xn lst n acc)
(cond
[(null? lst) acc]
[else (compute-poly-tail xn
(rest lst)
(- n 1)
(+ acc (* (first lst) (expt xn n))))]))
It works as expected:
(poly-eval 5 (list 1 0 -1))
=> 24
(poly-eval 0 (list 3 4 6 9 7))
=> 7
(poly-eval 2 (list 1 1 0 1 1 0))
=> 54
Build power coefficient and unknown list than use map function.
; 2*3^1+4*3^0
; input is 3 and '(2 4)
; we need '(3 3) '(2 4) '(1 0)
; use map expt build '(3^1 3^0)
; use map * build '(2*3^1 4*3^0)
; use foldr + 0 sum up
(define (poly-eval x coefficient-ls)
(local ((define power-ls (reverse (build-list (length coefficient-ls) values)))
(define unknown-ls (build-list (length coefficient-ls) (λ (i) x))))
(foldr + 0 (map * coefficient-ls (map expt unknown-ls power-ls)))))

Insert-everywhere procedure

I am trying to write a procedure that takes a a symbol and a list and inserts the symbol at every possible position inside the given list (thus generating a list of lists). I have coded the following definitions, which I must implement:
1
(define (insert-at pos elmt lst)
(if (empty? lst) (list elmt)
(if (= 1 pos)
(cons elmt lst)
(cons (first lst)
(insert-at (- pos 1) elmt (rest lst))))))
2
(define (generate-all-pos start end)
(if (= start end)
(list end)
(cons start (generate-all-pos (+ start 1) end))))
1 takes a position in a list (number), a symbol and the list itself and inserts the symbol at the requested position.
2 takes a start and a target position (numbers) and creates a sorted list with numbers from start to target.
So far I have got this:
(define (insert-everywhere sym los)
(cond
[(empty? los) (list sym)]
[else (cons (insert-at (first (generate-all-pos (first los)
(first (foldl cons empty los)))) sym los) (insert-everywhere sym (rest los)))
]
)
)
Which results in
> (insert-everywhere 'r '(1 2 3))
(list (list 'r 1 2 3) (list 2 'r 3) (list 3 'r) 'r)
so I actually managed to move the 'r' around. I'm kind of puzzled about preserving the preceding members of the list. Maybe I'm missing something very simple but I've stared and poked at the code for quite some time and this is the cleanest result I've had so far. Any help would be appreciated.
Óscar López's answer shows how you can do this in terms of the procedures that you've already defined. I'd like to point out a way to do this that recurses down the input list. It uses an auxiliary function called revappend (I've taken the name from Common Lisp's revappend). revappend takes a list and a tail, and efficiently returns the same thing that (append (reverse list) tail) would.
(define (revappend list tail)
(if (null? list)
tail
(revappend (rest list)
(list* (first list) tail))))
> (revappend '(3 2 1) '(4 5 6))
'(1 2 3 4 5 6)
The reason that we're interested in such a function is that as we recurse down the input list, we can build up a list of the elements we've already seen, but it's in reverse order. That is, as we walk down (1 2 3 4 5), it's easy to have:
rhead tail (revappend rhead (list* item tail))
----------- ----------- -----------------------------------
() (1 2 3 4 5) (r 1 2 3 4 5)
(1) (2 3 4 5) (1 r 2 3 4 5)
(2 1) (3 4 5) (1 2 r 3 4 5)
(3 2 1) (4 5) (1 2 3 r 4 5)
(4 3 2 1) (5) (1 2 3 4 r 5)
(5 4 3 2 1) () (1 2 3 4 5 r)
In each of these cases, (revappend rhead (list* item tail)) returns a list with item inserted in one of the positions. Thus, we can define insert-everywhere in terms of rhead and tail, and revappend, if we build up the results list in reverse order, and reverse it at the end of the loop.
(define (insert-everywhere item list)
(let ie ((tail list)
(rhead '())
(results '()))
(if (null? tail)
(reverse (list* (revappend rhead (list* item tail)) results))
(ie (rest tail)
(list* (first tail) rhead)
(list* (revappend rhead (list* item tail)) results)))))
(insert-everywhere 'r '(1 2 3))
;=> '((r 1 2 3) (1 r 2 3) (1 2 r 3) (1 2 3 r))
What's interesting about this is that the sublists all share the same tail structure. That is, the sublists share the structure as indicated in the following “diagram.”
;=> '((r 1 2 3) (1 r 2 3) (1 2 r 3) (1 2 3 r))
; ----- +++ o
; +++ o
; o
The insert-everywhere procedure is overly complicated, a simple map will do the trick. Try this:
(define (insert-everywhere sym los)
(map (lambda (i)
(insert-at i sym los))
(generate-all-pos 1 (add1 (length los)))))
Also notice that in Racket there exists a procedure called range, so you don't need to implement your own generate-all-pos:
(define (insert-everywhere sym los)
(map (lambda (i)
(insert-at i sym los))
(range 1 (+ 2 (length los)))))
Either way, it works as expected:
(insert-everywhere 'r '(1 2 3))
=> '((r 1 2 3) (1 r 2 3) (1 2 r 3) (1 2 3 r))

Resources