Creating a Scheme list from a box and pointer - scheme

For anyone who knows Scheme: how would you make a list using this box and pointer image?
I've tried a lot of different things, but I don't think I really understand scheme.

The box and pointer image is simply the visual equivalent of
(cons (cons 1 '())
(cons 2
(cons (cons 3 (cons 4 '()))
(cons 5 '()))))
Knowing that a list is a succession of conses ending in the empty list, e.g.,
(list 1 2 3) would return the same as
(cons 1 (cons 2 (cons 3 '())))
namely the list '(1 2 3)
the structure in the box and pointer diagram can also be seen (and created) as a list of lists:
(list (list 1)
2
(list 3 4)
5)

Related

Scheme: Update a list without using imperative features like set

I have only just started to learn scheme and were asked to implement the quick-sort algorithm in scheme, but we are not allow to use any imperative features like set! and we are not allowed to use filter
I have tried to come up with my own algorithm with this in mind, but I can't seem to avoid the use of set! for updating a list:
(define quicksort
(lambda (L)
(cond
[(or (null? L) (null? (cdr L))) L]
[else (let ((pivot (car L)) (small '()) (large '()))
(do ((i (- (length L) 1) (- i 1)))
((= i 0))
(if (< (list-ref L i) pivot)
(set! small (cons (list-ref L i) small))
(set! large (cons (list-ref L i) large))))
(append (quicksort small) (list pivot) (quicksort large)))])))
This code works, but is there any way of updating the lists small and large without the use of set?
If you cannot use set!, you cannot mutate lists. You are being asked to do this functionally, without any mutation. Instead of modifying a list in-place, build up smaller, partially-sorted lists and then combine them while preserving the sort.
To be fair Quicksort is an in place algorithm and many would state replacing the vectors with lists will change it enough to no longer bee quicksort. I'll ignore this at the moment.
Split large stuff in smaller chunks of code. This way you can test each step:
(segment 5 '(5 1 1 3 8 6 4 9 8 5 3 5 3 8 6))
; ==> ((1 1 3 4 3 3) (5 5) (8 6 9 8 8 6))
Of course you make the simple tests first:
(segment 5 '(5)) ; ==> (() (5) ())
(segment 5 '(5 1)) ; ==> ((1) (5) ())
(segment 5 '(5 8)) ; ==> (() (5) (8))
(segment 5 '(5 8 1 5)) ; ==> ((1) (5 5) (8))
The order of the results in the sub list is not important. eg. the result ((3 3 4 3 1 1) (5 5) (6 8 8 9 6 8)) is equally as sufficient and most probably easier to do.
With that made quicksort by first checking if it is a less than 2 element list eg. (or (null? lst) (null? (cdr lst))) and return the argument or create the 3 segments using the first element as pivot and then append the recursion of the lesser and the higher and then appending the 3 lists together and you have them in order.
As inspiration here is a procedure that splits on odd? and not:
(define (split-odd lst)
(let loop ((lst lst) (odd '()) (even '()))
(cond
((null? lst) (list odd even))
((odd? (car lst)) (loop (cdr lst) (cons (car lst) odd) even))
(else (loop (cdr lst) odd (cons (car lst) even))))))
(split-odd '(1 2 3 4 5 6))
; ==> ((5 3 1) (6 4 2))

Racket accumulator list function

I'm working on a specific step in creating the 2048 game which you may have played. It's on a bunch of websites online.
Basically all this function does is:
1) All blank spaces move to the back
and 2) if the first two numbers are equal then it doubles and checks every two numbers
These are the instructions for the step I'm stuck on:
Design a slide-left function so that it runs a single pass over the given row using an APS (accumulator passing style) helper. You will need to maintain two accumulators. The first accumulator remembers the last unmerged number. Initially, this value is false, meaning that we have not yet seen a number. The second accumulator is where we pile up the blanks as we come across them (a blank is '-) The easiest thing to do is to use a list for this purpose and, thus, its initial value is empty.
Do it in a single pass: You might think, at first, that it would be a good idea to use an accumulator for the solution. But then there is a problem with order. You could add new elements to the end of the current solution using something like (append solution (list number)), but that append operation is recursive and takes time proportional to the length of the solution list. We definitely want to avoid non-trivial operations during an APS recursion if we can. You could, instead, decide to add new numbers to the front of the current solution (using cons), with the intention of reversing the solution at the end. This is certainly better than the append approach. The drawback is that it requires a second pass over the data to do the reversal. We want to do this in one pass, and we can. So, the easiest and fastest thing to do is to just construct the solution, in the right order, as you back out of the recursion.
I added a bunch of check-expects here so you can see what it's doing:
(check-expect (slide-row-left '(2 2 4 -))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 2 - 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 - 2 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(- 2 2 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 2 2 2))(list 4 4 '- '-))
(check-expect (slide-row-left '(4 2 2 2))(list 4 4 2 '-))
(check-expect (slide-row-left '(2 4 2 2))(list 2 4 4 '-))
(check-expect (slide-row-left '(2 2 4 2))(list 4 4 2 '-))
(check-expect (slide-row-left '(2 2 4 4))(list 4 8 '- '-))
Alright, so here is what I have:
(define (blank? item) (equal? item '-))
(define (slide-row-left b)
(blank-help (slide-row-left/help b false) '()))
(define (slide-row-left/help ls acc)
(cond [(empty? ls) acc]
[(not (equal? (first ls) (first (rest ls))))
(slide-row-left/help (rest (rest ls))
(cons (first (rest ls)) (cons (first ls) acc)))]
[else (slide-row-left/help (rest (rest ls)) acc)]))
(define (blank-help ls acc)
(cond [(empty? ls) acc]
[(blank? (first ls)) (blank-help (rest ls) (cons (first ls) acc))]
[else (cons (first ls) (blank-help (rest ls) acc))]))
The first accumulator slide-row-left/help creates a list of the numbers that are not going to be merging. It checks that the first number and second are not equal and adds them to the list. If they are equal (which means they merge to double the original amount) then it just recurs. The second accumulator blank-help pushes all of the blank spaces '- to the end of the list, so all the numbers move left.
The problem is that I don't know how to make the pieces merge using these, especially in a single pass.
I'm about to head off for the night so hopefully you guys respond by tomorrow. Any help would be so great. Also this is for ISL+
I think I found your problem. You were not quite there yet frankly. The assignment clearly states that you have to build up the result while coming back from your recursion. This is because you don't want to reverse at the end of your program, or do an append while doing tail recursion.
Below are a few examples that explain each approach:
;; Using an accumulator
(define (double ls acc)
(if (empty? ls)
acc
(double (rest ls) (cons (* 2 (first ls)) acc))))
(double '(1 2 3) '())
;; = '(6 4 2)
;; So you could reverse the result: (reverse '(1 2 3) '()))
;; but this requires looping over the list again!
;; Using append
(define (double2 ls acc)
(if (empty? ls)
acc
(double2 (rest ls) (append acc (list (* 2 (first ls)))))))
(double2 '(1 2 3) '())
;; = '(2 4 6)
;; But the append operation is very expensive and you don't want that!
(define (double3 ls)
(if (empty? ls)
'()
(cons (* 2 (first ls)) (double3 (rest ls)))))
(double3 '(1 2 3))
;; Cons *after* the recursive call.
;; Requires more memory though, becuase you can not do tail call optimisation!
;; Info: http://c2.com/cgi/wiki?TailRecursion
So for different inputs to the function slide-row-left/help:
Case 1: First two numbers are equal
Input: '(2 2 4 -)
Result: '(4 4 '- '-)
What you want to do here is sum both first elements (4). Then you want to calculate the rest of the list. The rest of the list should now also contain an extra blank, because summing two elements makes the list one element shorter. So you pass a new blank to the acc value in your recursive call. So what you want to do here first is calculate the rest of the list. Hence, call recursively with (drop ls 2) and (cons '- acc). This drops the first 2 elements form the list. Once you get that result back (result will be '(4 '- '-), you just cons your sum in front of it. This results in a total result of '(4 4 '- '-).
Case 2: First two numbers differ
Input: '(4 2 2 2)
Result: '(4 4 2 '-)
If the first two numbers differ we can not do anything. We take the first number off of the list (4), which leaves you with '(2 2 2). You can not drop the first two because the second and third element might be able to be summed. You remember the first element and call the function recursively to calculate the rest of the result. The function returns and gives you '(4 2 '-). Now all you need to do is cons the first element back in front of it, yielding '(4 4 2 '-).
Case 3: First element is a blank
Input: '(- 2 2 4)
Result: '(4 4 '- '-)
If the first number is a blank you can not do anything with it. You might think that in this case case 2 applies but it doesn't. A blank should be put in the back of your solution. But how can you do that? Simple, you put the blank in your accumulator. As you will soon see, the accumulator is basically the end of your list. So, take the blank away from the list, which leaves you with '(2 2 4). Put '- in front of the accumulator, which makes the accumulator equal to '('- <rest of acc>) and do your recursive call. So you call it with the list '(2 2 4) and the accumulator '('- <rest of acc>). Your recursive call will yield '(4 4 '- '-). Ergo, you don't have to cons anything in front of it anymore, it is just your result.
Case 4: Second element is a blank
Input: '(2 - 2 4)
Result: '(4 4 '- '-)
If the second number is a blank, the situation is a bit more tricky. You will have to cut out the blank from the list. So you would want (2 2 4). To do this you can do (cons (first ls) (rest (rest ls))). The blank can again, not be discarded. You will need it to put it at the end of the list. Where is the end of the list? Indeed, the accumulator. So you cons the element you want at the back of the solution (the blank) to the acc as such: (cons (second ls) acc). Again, you have put the blank at the end and you make your recursive call. There is no element that has to be put in front of of the solution of the recursive call so that call gives you the total answer.
Case 5: Input is empty
Input: '()
Result: acc
If your input is empty you need to return the acc value. Since your input is empty, you need to return the end of the list, which we know is the acc value.
Case 6: Input is length 1
Input: '(4)
Result: (cons 4 acc)
If the input is only one element you can not apply any sum or something to it. You only have one element. So, the result is that element, consd in front of the acc.
Source
#lang racket
;; A shorthand for (first (rest ls))
(define (second ls) (first (rest ls)))
(define (blank? item) (equal? item '-))
(define (slide-row-left b)
(blank-help (slide-row-left/help b '()) '()))
(define (slide-row-left/help ls acc)
(cond [(empty? ls) acc]
[(= (length ls) 1) (cons (first ls) acc)]
;; When the first element is blank (e.g., '(- 2 4))
;; -> Discard the blank
;; -> call the function on the rest of the list.
[(blank? (first ls))
(slide-row-left/help (rest ls) (cons (first ls) acc))]
;; When the second element is blank (e.g., '(2 - 2 4))
;; -> Discard the second element
;; -> Run the function again with the entire list (without the blank)
[(blank? (second ls))
(slide-row-left/help (cons (first ls) (drop ls 2)) (cons (second ls) acc))]
;; If the first two elements are not equal:
;; -> drop 1 element from the list
;; -> cons this element to the result of the recursive call
[(not (equal? (first ls) (second ls)))
(let ([fst (first ls)]
[snd (second ls)]
[rst (rest ls)])
(cons fst (slide-row-left/help rst acc)))]
;; If the first two elements are the same:
;; -> drop 2 elements from the list
;; -> Sum them
;; -> cons the sum in front of the recursive call
[else
(let ([fst (first ls)]
[snd (second ls)]
[rst (drop ls 2)])
(cons (* 2 snd) (slide-row-left/help rst (cons '- acc))))]))
(define (blank-help ls acc)
(cond [(empty? ls) acc]
[(blank? (first ls)) (blank-help (rest ls) (cons (first ls) acc))]
[else (cons (first ls) (blank-help (rest ls) acc))]))
(define (check-expect x y)
(display x)
(display y)
(equal? x y))
(check-expect (slide-row-left '(2 2 4 -))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 2 - 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 - 2 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(- 2 2 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 2 2 2))(list 4 4 '- '-))
(check-expect (slide-row-left '(4 2 2 2))(list 4 4 2 '-))
(check-expect (slide-row-left '(2 4 2 2))(list 2 4 4 '-))
(check-expect (slide-row-left '(2 2 4 2))(list 4 4 2 '-))
(check-expect (slide-row-left '(2 2 4 4))(list 4 8 '- '-))
Edit
The drop function is used to remove the n first elements of a list. It can be implemented as shown below. However, you can just as well use (rest (rest ls)) instead. I used it for brevity.
drop
(define (drop ls n)
(cond [(empty? ls)
ls]
[(>= 0 n)
ls]
[else
(drop (rest ls) (- n 1))]))

What is the difference between `(mcons (mcons '() 25) 16)` and `(mcons 25 (mcons 16 `()))`

I am busy with Structure and Interpretation of Computer Programs exercise 2.18. Here we have to define a procedure reverse to reverse a list. It should do the following:
(reverse (list 1 4 9 16 25))
;; => (25 16 9 4 1)
I came up with the following definition:
(define (reverse list)
(if (null? list)
list
(cons (reverse (cdr list)) (car list))))
;; => (mcons (mcons (mcons (mcons (mcons '() 25) 16) 9) 4) 1).
Then in a solution In found something similar as follows:
(define (reverse items)
(if (null? (cdr items))
items
(append (reverse (cdr items))
(cons (car items) nil))))
;; => (mcons 25 (mcons 16 (mcons 9 (mcons 4 (mcons 1 '()))))).
There's a difference between append and cons here where I cannot put my finger on.
My question: what is the difference and why are the results not displayed as (25 16 9 4 1)?
Short answer: the first version of reverse is incorrectly building an improper list, the second version is inefficiently building a proper list. We can do better as long as we understand the difference between append and cons.
append concatenates two lists. If we use it for just adding an element at the end of one list, we'll be doing a lot more of work than needed: we'll have to traverse the whole list each time just to put one last element (see: Schlemiel the Painter's algorithm). Hence a reverse implementation that uses append can be as bad as O(n^2) in complexity.
On the other hand, cons adds a single element to the head of a list, for an O(n) complexity in the implementation of reverse. In general, in Scheme you should try to avoid using append to build a new output list, always prefer cons. Now let's see what's being returned by your algorithm using cons:
(reverse '(1 2 3 4 5))
=> '(((((() . 5) . 4) . 3) . 2) . 1)
Why is that? because to build a proper list with cons the second argument must be another proper list, but you're passing a single element. A correct implementation requires an accumulator parameter - and incidentally, this is more efficient, because it uses tail recursion, a concept that you should already be familiar with, as it was introduced in section 1.2 of the book. Try this:
(define (reverse lst acc)
(if (null? lst)
acc
(reverse (cdr lst) (cons (car lst) acc))))
(reverse '(1 2 3 4 5) '())
=> '(5 4 3 2 1)
For the last part of the question: the list is being displayed with mcons (the m means that the cons cells are mutable) because of the language you're using, try switching to #lang racket, which uses immutable cons cells by default, and will get printed as you expect.

Modify part of list using set?

Using set! I want to be able to modify a local state list variable lst, but only a part of it
For example, I want to insert values inside an inner list:
((1 2) (4 5))
becomes
((1 2 3) (4 5))
I want to be able to do something like set! (car lst) (append (car lst) 3)
But that seems to only modify the temporary variable generated by (car lst).
The only way I can think of is to create a new list with the new desired values and set lst to be the new list, but that seems wasteful and unnecessary. Is there a better way to do it?
try this:
(define lst (list (list 1 2) (list 4 5)))
lst
> ((1 2) (4 5))
(set-cdr! (cdar lst) (list 3))
lst
> ((1 2 3) (4 5))
when modifying cons/lists you should use set-cdr! and set-car!
EDIT: for racket
use mutable list:
(require racket/mpair)
(define lst (mlist (mlist 1 2) (mlist 4 5)))
lst
> (mcons (mcons 1 (mcons 2 '())) (mcons (mcons 4 (mcons 5 '())) '()))
(set-mcdr! (mcdr (mcar lst)) (list 3))
> (mcons (mcons 1 (mcons 2 #<promise:temp2>)) (mcons (mcons 4 (mcons 5 '())) '()))
lst
Depending on which interpreter of Scheme you're using, you might need to do a bit more of work. For instance, in Racket the list primitives are not mutable by default, and you'll have to use the mutable version of the procedures:
(require scheme/mpair)
(define lst (mlist (mlist 1 2) (mlist 4 5)))
lst
(set-mcdr! (mcdr (mcar lst)) (mlist 3))
lst
The standard idiom in Scheme for creating a list piecemeal is to prepend elements to the front, using cons, and not trying to set-cdr! the last cons cell in the list. At the end, when your list is done, you can then use reverse to get the elements in the correct order. This way, no list mutation is necessary per se.
So if you're trying to create the list (1 2 3) piecemeal:
Start off with the empty list, (), and cons 1 to it. This gives you (1).
Then cons 2 to the list: (2 1)
Then cons 3 to the list: (3 2 1)
Finally, when you're done building the list, reverse it: (1 2 3)
You may ask why this is "better". That's because accessing the last pair to set-cdr! is an O(n) operation; with linked lists, elements are not random-access, but are linear on the element position being accessed. Whereas, cons is always O(1).
reverse is an O(n) operation, but as long as you're doing it only at the end (when you are ready to build the list in the correct order), rather than calling it all the time, that won't detrimentally affect performance.

Are pair and list different in Scheme?

I wonder whether '(1 . 2) and '(1 2) mean the same data (equal to each other) in Scheme or not? I think they are the same thing, is this correct?
No, they are not the same.
'(1 . 2) means (cons 1 2)
whereas
'(1 2) means (cons 1 (cons 2 nil))
(1 . 2) is sometimes called an improper list, because it is not NIL terminated. (1 2) represented in dot form may be written (1 2 . NIL), but you should not write something like this.
dr rackect explains it much more clearer:
"A pair joins two arbitrary values.....The cons procedure constructs pairs"
(cons 1 2)
'(1 . 2)
(pair? (cons 1 2))
#t
on the other hand
"A list is a combination of pairs that creates a linked list. More precisely, a list is either the empty list null, or it is a pair whose first element is a list element and whose second element is a list."
(cons 0 (cons 1 (cons 2 null)))
'(0 1 2)
http://docs.racket-lang.org/guide/pairs.html
please LISP has been around since the 50's for accurate answers look at their documentation and example they area around for more than 60 years some people was not even born there.
Yes!
Pairs: (cons y z) creates a pair between the values y and z. Likewise, the (more complicated) expression (cons x (cons y z)) creates a pair between x and the pair (y . z). You can also represent these pairs as '(y . z) and '(x . (y . z))
Lists: A list is just a special type of pair. It's the case where a value is paired onto an already-existing list. Since the very first list has to start somewhere, we always have the null list '() (sometimes called the 'empty list') ready to be paired. So (cons y '()) pairs y with the null list to become the one-item list '(y). Likewise, (cons x '(y)) and (cons x (cons y '())) pair x to the list '(y) to become the list '(x y).
listspairs
List must end with empty list (also termed as null). Below sample code on repl illustrates the difference.
> (cons 1 (cons 2 '())) ; I am a pair as well as list
'(1 2)
(pair? (cons 1 (cons 2 '())))
#t
> (list? (cons 1 (cons 2 '())))
#t
> (cons 1 (cons 2 3)) ;I am a pair but but not list
'(1 2 . 3)
> (pair? (cons 1 (cons 2 3)))
#t
> (list? (cons 1 (cons 2 3)))
#f

Resources