Can someone explain the difference between Cons and Append in scheme? - scheme

I read both of them and they seem to both construct a single list, what's their difference?

cons is the constructor for all pairs.
A proper list is () (the empty list, aka nil) or a pair where the cdr is a proper list. Any chain of pairs where the last one has () as it's cdr is a proper list (in addition to the empty list itself).
A dotted list is a pair that does not have a proper list as it's cdr. Thus a chain of pairs where the last cdr is not () matches this.
;; dotted lists
(cons 1 2) ; ==> (1 . 2)
(cons 1 (cons 2 3)) ; ==> (1 2 . 3) or (1 . (2 . 3))
;; proper lists
(cons 1 '()) ; ==> (1) or (1 . ())
(cons 1 (cons 2 '())) ; ==> (1 2) or (1 . (2 . ()))
append is a procedure that uses cons to make a list with all the elements of the argument lists left to right. A common implementation of append for just two lists would be:
(define (append lst tail)
(if (null? lst)
tail
(cons (car lst)
(append (cdr lst)
tail))))
append will fail if one of the arguments except the last is not a proper list. Tail and can be any value:
(append '(1 2 3) '(4 5)) ; ==> (1 2 3 4 5) or (1 . (2 . (3 . (4 . (5 . ())))))
(append '(1 2 3) '(4 5 . 6)) ; ==> (1 2 3 4 5 . 6) or (1 . (2 . (3 . (4 . (5 . 6)))))
(append '(1 2 3) #f) ; ==> (1 2 3 . #f) or (1 . (2 . (3 . #f)))
(append '(1 2 . 3) '(4 5 . 6)) ; ==> error `car` of number not allowed

Related

Is append the identity function for map?

In doing some tests I've noticed that append always gives me the same output as input when using map:
#lang sicp
(map append '(1 2 3 4 5))
(map (lambda (x) x) '(1 2 3 4 5))
; (1 2 3 4 5)
; (1 2 3 4 5)
(map append '((1 2)(3 4)))
(map (lambda (x) x) '((1 2)(3 4)))
; ((1 2) (3 4))
; ((1 2) (3 4))
That seems pretty neat/unexpected to me. Is this in fact the case? If so, how does the append work to give the identity property?
The append procedure takes zero or more list arguments, and a final argument that can be any object. When the final argument is a list, the result of appending is a proper list. When the final argument is not a list, but other list arguments have been provided, the result is an improper list. When only one argument is provided, it is just returned. This behavior with one argument is exactly the behavior of an identity procedure.
> (append '(1 2) '(3))
(1 2 3)
> (append '(1 2) 3)
(1 2 . 3)
> (append '(1 2))
(1 2)
> (append 3)
3
The call (map append '(1 2 3 4 5)) is equivalent to:
> (list (append 1)
(append 2)
(append 3)
(append 4)
(append 5))
(1 2 3 4 5)
Here, append is just acting as an identity procedure, as described above.

Why is my Reverse Function Reversing All my Pairs BESIDES One?

i.e. I am given this (all possible combinations to make change for 11):
(list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25)
My code should return:
((7 . 1) (4 . 5) (3 . 1) (3 . 10) (2 . 1) (6 . 25))
so it is more readable.
However, it is returning:
((7 . 1) (4 . 5) (1 . 3) (3 . 10) (2 . 1) (6 . 25))
I don't know how to reverse the order of ALL my pairs.
This is my code:
(define (rev pair) ;; function reverses a pair, not a list.
(map (lambda (e) (cons (cdr e) (car e))) pair))
(define (rle coins)
(if (null? coins)
'()
(let loop ((firsts (list (car coins)))
(but-firsts (cdr coins)))
(if (or (null? but-firsts)
(not (equal? (car firsts) (car but-firsts))))
(rev (cons (cons (car firsts) (length firsts))
(rle but-firsts)))
(rev (loop (cons (car but-firsts) firsts) (cdr but-firsts)))))))
This is my test:
(rle (list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25))
Why not writing a classical tail-call recursive function?
Because my opinion is that named-loops are horrible constructs - unclear for the reader.
(define (rle coins (acc '()))
(if (null? coins)
(reverse acc)
(rle (cdr coins)
(if (and (not (null? acc)) (equal? (cdar acc) (car coins)))
(cons (cons (+ 1 (caar acc))
(cdar acc))
(cdr acc))
(cons (cons 1 (car coins))
acc)))))
It does correctly:
(rle (list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25))
;; '((7 . 1) (4 . 5) (3 . 1) (3 . 10) (2 . 1) (6 . 25))
First, some systematic testing (test cases should be systematic and as small as possible, not large and arbitrary):
> (rle '(2))
'((1 . 2))
> (rle '(2 3))
'((1 . 2) (3 . 1))
> (rle '(2 3 4))
'((1 . 2) (3 . 1) (1 . 4))
> (rle '(2 3 4 5))
'((1 . 2) (3 . 1) (1 . 4) (5 . 1))
> (rle '(2 3 4 5 6))
'((1 . 2) (3 . 1) (1 . 4) (5 . 1) (1 . 6))
When all the elements are unique, it looks like every other element is backwards.
> (rle '(5 5 6))
'((5 . 2) (1 . 6))
> (rle '(5 5 6 6))
'((5 . 2) (6 . 2))
> (rle '(5 5 6 6 6))
'((5 . 2) (3 . 6))
> (rle '(5 6 6 6))
'((1 . 5) (6 . 3))
When elements are repeated, the order seems a bit unpredictable.
Let's count how many times you rev each element by using triple elements with the counter as the car:
;; Flip the order of a pair.
(define (flip x)
(cons (cdr x) (car x)))
;; Add one to the car of each element and flip its cdr.
(define (rev list)
(map (lambda (x) (cons (+ 1 (car x)) (flip (cdr x)))) list))
(define (rle coins)
(if (null? coins)
'()
(let loop ((firsts (list (car coins)))
(but-firsts (cdr coins)))
(if (or (null? but-firsts)
(not (equal? (car firsts) (car but-firsts))))
(rev (cons (cons 0 (cons (car firsts) (length firsts)))
(rle but-firsts)))
(rev (loop (cons (car but-firsts) firsts) (cdr but-firsts)))))))
This creates a list that says how many times rev has been applied to each element in the result.
> (rle '(99))
'((1 1 . 99))
> (rle '(99 99))
'((2 99 . 2))
> (rle '(99 99 99))
'((3 3 . 99))
> (rle '(99 99 99 100))
'((3 3 . 99) (4 100 . 1))
> (rle '(99 99 99 100 100))
'((3 3 . 99) (5 2 . 100))
As you can see, each element has been "flipped" many times - once for every time you recurse.
This happens because you apply rev to every partial result, and not just on the final list.
The elements that have been flipped an even number of times are "backwards".
Now, your example input:
> (rle (list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25))
'((7 7 . 1) (11 4 . 5) (14 1 . 3) (17 3 . 10) (19 2 . 1) (25 6 . 25))
where we can see that the third element has been flipped an even number of times, and this is what's causing it to be backwards.
The best solution is to build your pairs in the order you want them, so you don't need to adjust them afterwards; the implementation is left as an exercise.
(The implementation is a very minor change in your procedure, and the removal of rev.)

how can i append to a list without creating a dotted pair

how do i append (1 2 3) to the end of () to make ((1 2 3))
how do i append (4 5 6) to the end of that to make ((1 2 3) (4 5 6))
how do i append "|" to the end of that to make ((1 2 3) (4 5 6) "|")
with NO dotted pairs.
I'm working with Chicken Scheme but I'll take an answer from any scheme at this point. Note that any of these lists could also be nested lists of who knows what... i'm just writing a trivial example.
note: #sjamaan shows a solution using append that involves wrapping everything in another list to compensate for append doing things OTHER than what the name says.
(append (list 1 2 3) "|" ) ;=> (1 2 3 . "|")
;^^ didn't actually append, created a dotted pair
(append '(1 2 3) (list 4 5 6)) ;=> (1 2 3 4 5 6) ; don't want unwrapped list
;^^ didn't actually append the list i gave it but appended the contents of the list.
Basically I'm hoping for an append method that actually appends what you give it, not appends the contents of it, or takes it and makes a dotted pair. Maybe i'm just a dreamer... I can write a "no really append" method that just takes whatever params you give it and wraps them in an outer list to compensate but that's just silly... Surely scheme has some way to append without this crazyness.
Here is how append is made:
(define (append2 lst1 lst2)
(if (null? lst1)
lst2 ; the second list is unaltered
(cons (car lst1)
(append2 (cdr lst1) lst2))))
makes a pair chain consisting of all the elements in lst1 and lst2. It does not make a pair where there is nont in lst2 so:
(append2 '(1 2 3) '(4 5)) ; ==> (1 2 3 4 5)
(append2 '(1 2 3) '()) ; ==> (1 2 3) and not (1 2 3 ())
(append2 '(1 2 3) '5) ; ==> (1 2 3 . 5)
Note that every list like (1 2 3) actually is (1 2 3 . ()) or even more correctly (1 . (2 . (3 . ())))
how do i append (1 2 3) to the end of () to make ((1 2 3))
(define (insert-last e lst)
(let helper ((lst lst))
(if (pair? lst)
(cons (car lst)
(helper (cdr lst)))
(cons e '()))))
(insert-last '(1 2 3) '())
; ==> ((1 2 3))
how do i append (4 5 6) to the end of that to make ((1 2 3) (4 5
6))
(insert-last '(4 5 6) '((1 2 3)))
; ==> ((1 2 3) (4 5 6))
how do i append "|" to the end of that to make ((1 2 3) (4 5 6)
"|")
(insert-last "|" '((1 2 3) (4 5 6)))
; ==> ((1 2 3) (4 5 6) "|")
Know that this is very much like append. These are the worst way to make that list since you are making a new list every time. It's O(n) for each insert and O(n^2) for n elements. If you could do this in reverse order you get something that do this O(1) instead of O(n) for each insert. Instead of insert-last you use cons:
(cons '"|" '()) ; ==> ("|")
(cons '(4 5 6) '("|")) ; ==> ((4 5 6) "|")
(cons '(1 2 3) '((4 5 6) "|") ; ==> ((1 2 3) (4 5 6) "|")
This is O(1), O(n) for n elements processed. If you need to do it in the original order you can accumulate, then reverse..
(cons '(1 2 3) '()) ; ==> ((1 2 3))
(cons '(4 5 6) '((1 2 3))) ; ==> ((4 5 6) (1 2 3))
(cons '"|" '((4 5 6) (1 2 3))) ; ==> ("|" (4 5 6) (1 2 3))
(reverse '("|" (4 5 6) (1 2 3)) ; ==> ((1 2 3) (4 5 6) "|")
This is O(1), then O(n) for the reverse but it still is O(1) amortized. O(n) for n elements you process.
append doesn't append atoms to lists. It concatenates lists. You have to lift the atom up to a list before concatenation makes sense.
(append xs (list y))
But it makes sense to point out (reverse (cons y (reverse xs))) which has the same result. reverse suggests that you might be building up your list backwards if you need to append atoms to the end.
The procedure you're looking for is unsurprisingly called append (from SRFI-1). It appends a list of things onto another list. This does mean that if you want to add just one item, you'll need to make a list out of it:
(append '() '((1 2 3))) => ((1 2 3))
(append '((1 2 3)) '((4 5 6))) => ((1 2 3) (4 5 6))
(append '((1 2 3) (4 5 6)) '("|") ) => ((1 2 3) (4 5 6) "|")
It accepts multiple arguments, which will all be appended to eachother in that order, so you can also do:
(append '() '((1 2 3)) '((4 5 6)) '("|")) => ((1 2 3) (4 5 6) "|")
Hope this helps!
Whether you want it or not, cons cells will be created, since lists consist of cons cells.
how do i append (1 2 3) to the end of () to make ((1 2 3))
CL-USER 24 > (list '(1 2 3))
((1 2 3))
how do i append (4 5 6) to the end of that to make ((1 2 3) (4 5 6))
CL-USER 25 > (append '((1 2 3)) (list '(4 5 6)))
((1 2 3) (4 5 6))
how do i append "|" to the end of that to make ((1 2 3) (4 5 6) "|")
CL-USER 26 > (append '((1 2 3) (4 5 6)) (list "|"))
((1 2 3) (4 5 6) "|")

Why the same data is printed differently when built with list or cons? [duplicate]

This question already has answers here:
Dot notation in scheme
(3 answers)
Closed 5 years ago.
I tried to reproduce the same data layout by using different combinations of list and cons. In MIT Scheme, the same data layout seems to be printed differently depending on how it was built.
In example,
> (cons 1 (cons 2 3))
(1 2 . 3)
> (list 1 (cons 2 3))
(1 (2 . 3))
> (cons 1 (list 2 3))
(1 2 3)
> (list 1 (list 2 3))
(1 (2 3))
The underlying data should always be the same. Why does MIT Scheme represents it differently? Is the underlying arrangements of pairs really the same every time?
Each of the inputs is building a different output, so they're printed differently. Notice that the following expressions are equivalent:
(cons 1 (cons 2 3))
; (1 2 . 3)
(list 1 (cons 2 3))
(cons 1 (cons (cons 2 3) '()))
; (1 (2 . 3))
(cons 1 (list 2 3))
(cons 1 (cons 2 (cons 3 '())))
; (1 2 3)
(list 1 (list 2 3))
(cons 1 (cons (cons 2 (cons 3 '())) '()))
; (1 (2 3))
Remember: a list is defined if and only if the cdr part of each cons is also a cons cell, or an empty list.

cons return a list and pair in scheme

I just started to learn scheme. My question is why does (cons 3 (4)) return a list (3 4) but (cons (4) 3) returns a pair ((4) . 3)? should (cons 3 (4)) returns (3 . (4)) as well?
Both (3 4) and (3. (4))are the list (cons 3 (cons 4 '()))), and if you enter '(3 . (4)) in your REPL, you will most likely see '(3 4) as the result.
The difference you're seeing is just an output convention; a pair where the cdr is a list is not printed with dot notation.

Resources