I am currently working my way through Berkely's summer 2011 CS3L course and am struggling to understand Box and Pointer Diagrams. How to build them and how to interpret them.
The instructions provided are here.
However, I am still not "getting it."
I understand that lists are combinations of pairs and that the cdr of a pair may point to another pair. I also understand that the pair that the cdr points to may be another list. I just don't understand how to draw it all out in a diagram.
For reference, here is an example of a problem I am being given is:
(define cal
(list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7))
Given a code like the one above, I am suppose to draw the box and pointer diagram and then be able to say what combination of car and cdr is necessary to get any given number in the list.
Again, for reference, below is the diagram I should have been able to come up with:
To reiterate, what I'm looking for is a video or article that may explain the building of box and pointer diagrams more clearly.
Thank you in advance for anyone willing to point me in the right direction.
[Note that this answer is not encouraging you to cheat: if you are doing a course which requires you to be able to draw box & pointer diagrams then you should be able to do that, not have a program do it for you. But the program can help you learn.]
One good approach to learning how box & pointer diagrams work is to be able to talk to a program which knows how to draw them. In Lisp's long ago golden age we had wonderful conversational interfaces on our Lisp machines which let graphics and text be intermingled, along with nice graph-drawing programs from which tools could easily be built to do this. Using such tools you could construct various structures out of conses and get the program to draw diagrams for you, and thus you got a good handle on how the structures work.
Well ... it turns out Lisp's golden age is now. If you use Racket (and you can use Racket if you are not already using it) then there is a very wonderful package called sdraw which does this. It's not bundled with the Racket distribution, but to install it you can either use DrRacket's package manager or just do
raco pkg install --auto sdraw
which will install it. Now you can (in a DrRacket window, this won't work in a terminal session) simply talk to the Racket REPL and get it to draw cons trees for you:
By simply interacting with the language and letting it draw things for you then you can get a really good feel for how the various structures hang together.
This answer uses Common Lisp for its examples, but it is not fundamentally different in Scheme. Note also that you can play with sdraw.lisp in Common Lisp (e.g. with CCL or SBCL programs) if you want to see how printing the diagram can actually be implemented.
You first have to be clear about the result you want to print, which might be difficult when the source contains cons/list/append operations. Also, since the source code is also a tree of cons-cells, you have to take care not to mix the source with the value obtained after its evaluation.
So it all starts with evaluating the form correctly.
Evaluate cal
Let's first evaluate the expression. Below, I also mention drawing the boxes directly from the input expression, but IMO it helps to detail that intermediate step.
The result is the same in Scheme and Common Lisp, after recursively evaluating all expressions:
((((1 2) 3) (4 5)) 6 7)
Here is how you could ask the system to trace the computation, in Common Lisp. First of all, know that you cannot trace standard functions like list, etc. So let's shadow them in a custom package with simple wrappers:
(defpackage :so
(:use :cl)
(:shadow #:list #:cons #:append))
(in-package :so)
(defun list (&rest args) (apply #'cl:list args))
(defun cons (&rest args) (apply #'cl:cons args))
(defun append (&rest args) (apply #'cl:append args))
Then, in the REPL, go to that package:
CL-USER> (in-package :so)
#<PACKAGE "SO">
Ask to trace those functions:
SO> (trace list append cons) ;; the shadowing ones
(LIST CONS APPEND)
Now, you can enter the value of cal directly, but this time the symbols being used are the one we asked to trace.
SO> (list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7)
The environment then evaluates the form and prints how each function is called and what results it returns.
0: (SO::LIST 1 2)
0: LIST returned (1 2)
0: (SO::CONS 3 NIL)
0: CONS returned (3)
0: (SO::CONS (1 2) (3))
0: CONS returned ((1 2) 3)
0: (SO::LIST ((1 2) 3))
0: LIST returned (((1 2) 3))
0: (SO::CONS 5 NIL)
0: CONS returned (5)
0: (SO::CONS 4 (5))
0: CONS returned (4 5)
0: (SO::LIST (4 5))
0: LIST returned ((4 5))
0: (SO::APPEND (((1 2) 3)) ((4 5)))
0: APPEND returned (((1 2) 3) (4 5))
0: (SO::LIST (((1 2) 3) (4 5)) 6 7)
0: LIST returned ((((1 2) 3) (4 5)) 6 7)
((((1 2) 3) (4 5)) 6 7)
Vizualize as cons cells
It can be helpful to view lists as chains of cons-cells, i.e. turn (a b c) into (a . (b . (c . nil))). Let's define a helper function:
(defun consprint (x)
(if (consp x)
(format nil
"(~a . ~a)"
(consprint (car x))
(consprint (cdr x)))
(prin1-to-string x)))
Here is the result:
SO> (consprint '((((1 2) 3) (4 5)) 6 7))
"((((1 . (2 . NIL)) . (3 . NIL)) . ((4 . (5 . NIL)) . NIL)) . (6 . (7 . NIL)))"
Draw: a term rewriting approach
Use a recursive, bottom-up approach to draw it.
Definition.: Here I define a leaf as a cons-cell that has atoms in both its CAR and its CDR slots: e.g. (0 . NIL) and (X . Y) are both leaves, but not ((0 . 1) . 2). Note that this includes improper lists, something I am relying on to explain the drawing method when I replace subterms by symbols.
((((1 . (2 . NIL)) . (3 . NIL)) . ((4 . (5 . NIL)) . NIL)) . (6 . (7 . NIL)))
^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^
Above I underlined all leaves: you can easily draw those boxes, and label them with symbols (A, B, ...).
Here below I replace the original cells with the names of their associated boxes, and again underline the new leaves:
((((1 . A) . B) . ((4 . C) . NIL)) . (6 . D))
^^^^^^^ ^^^^^^^ ^^^^^^^
Then, when there is a symbol representing a box, draw an arrow to that box. For example, you define a box named E that corresponds to (1 . A), so you draw [1/x] and connect the x to box A.
You obtain:
(((E . B) . (F . NIL)) . G)
Now, consider (E . B): its car is a symbol, so the box you need to draw has no value, but an outgoing arrow from the CAR slot that points to the E cell (just like its CDR points to B).
You repeat this process until termination. The rest is visual layout, where typically boxes that are just linked lists of atoms are laid out horizontally.
Directly drawing from expressions
Presumably the original exercise wants you to draw boxes right from the original expression. The same can be done as above, working upward from leaf expressions and replacing their values by symbols denoting existing boxes.
A cons maps directly to a box.
a list is just a repeated application of cons, and you can go quickly by drawing as many boxes as there are elements.
append in practice makes a copy of all but the last list in arguments, but when drawing you can "mutate" the existing boxes. For each existing box, keep following the CDR until you reach a box with no arrow in cdr, and link that box to the next one in the arguments, thus chaining the different boxes together.
It can be interesting to also draw the actual, purely functional version of append to see how structure sharing and garbage collection work.
Starting from the code, you can work from the top level down. Given:
(define cal
(list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7))
you can see that the first level is a list containing three elements: a complex object, a 6, and a 7. This can be represented by three cons cells:
Now you just need to figure out what the car of the first cons cell points to. Looking back at the code, this points to a list that is comprised of two lists, appended together. If you look at the parentheses, you can see that the first call to list is taking only one argument, so this will create a list containing one element.
When two lists are appended, the nil that closes the first list is replaced by a pointer to the front of the second list so:
The first list of the two to be appended is created by this code:
(list (cons (list 1 2) (cons 3 '())))
Here, the list (1 2) is consed to the front of the list (3) to create a new list. This is a list of two elements where the car of the list points to the list (1 2) and the cdr of the list is (3). We can go ahead and draw the boxes for this layer:
And since the car of the last layer just points to the list (1 2), we can fill that in:
Now, the second argument to the append function is also a list containing only one element, so:
That single element is the result of (cons 4 (cons 5 '())), which is just the list (4 5), so we can finally finish our box diagram by pointing to this list from the car of the last cons cell:
Try starting from the inside out, just as the interpreter will. Draw the diagram for, say, (cons 3 '()) - pretty simple, right? Now, should anything point to it? Yes, it is the cdr of (cons (list 1 2) (cons 3 '())). So, when you draw the diagram for that larger expression, make sure its cdr points to the first sub-diagram you drew. To finish drawing this larger expression, you'll also need to draw a diagram for (list 1 2) - just as easy as where you started.
Work your way outwards from there - the append operation is the trickiest part, but the instructions you linked explained how append works.
Forget lists. There are no lists. There are only pairs.
(define cal
(list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL))) ; (list 1 2)
(define B (cons 3 NIL)) ; (cons 3 '())
(define C (cons 5 NIL)) ; (cons 5 '())
(define cal
(list (append (list (cons A B))
(list (cons 4 C)))
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL)))
(define B (cons 3 NIL))
(define C (cons 5 NIL))
(define D (cons A B))
(define E (cons 4 C))
(define cal
(list (append (list D)
(list E))
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL)))
(define B (cons 3 NIL))
(define C (cons 5 NIL))
(define D (cons A B))
(define E (cons 4 C))
(define F (list D E)) ; (append (list D) (list E))
(define cal
(list F
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL)))
(define B (cons 3 NIL))
(define C (cons 5 NIL))
(define D (cons A B))
(define E (cons 4 C))
(define F (cons D (cons E NIL))) ; (list D E)
(define cal
(cons F
(cons 6
(cons 7 NIL))))
Each cons is a box. Each name is a pointer.
And that's all.
Related
I need some help understanding the syntax on how to append a number into a list, i'm doing this from user input via the console so this elements have to be entered in recursively. So for ever number that is entered, the list must grow for each element (only numbers) added.
Here is the code I am working with, the problem lies in the second conditional. Right now this works but only creates an empty list of each number I put in, so the results would be
>12
>202
>30
()()()
zero input: stopping list
(define (inputlist)
(let ((applist list))
(let ((inpt (read)))
(cond
((= inpt 0)(newline) (display "zero input: stopping list"))
;;OLD((number? inpt) (cons inpt applist) (display (applist))(inputlist))
((number? inpt) (append (applist)(list inpt)) (display (applist))(inputlist))
(else
display "Not a number")))))
I understand why cons is not doing what I need it to be doing, but is there a similar functionality to appending each read in element to a pre-existing list?
EDIT: I've gotten closer what i've needed to do but still with the same results, i am now appending upon my applist with a list i create via every input, though it is still resulting in as many empty lists as I input.
SECOND EDIT: I've realized why it's printing multiple ()'s is because it's being called off the stack when 0 is entered, so i'm sure it's not working because the appending isn't working as intended, i've displayed the applist on the 0 conditional and it returns one null list.
A simple way to append an element to the end of a list while looping would be to do call append and update the reference to the list afterwards:
(set! applist (append applist (list inpt)))
Notice that you have several misplaced parentheses - in your code some are missing, some are unnecessary. In Scheme () means function application, and you have to be careful where you put those brackets.
Also, be aware that append doesn't modify the initial list, it creates a new one, and if you need to refer to it, you have to store it somewhere (that's why I'm doing a set! above).
There are more serious errors with you logic. The conditions are in the wrong order (you have to test if the input is a number before asking if it's zero), and you forgot to loop if something other than a number is entered. Also, if we pass along the list as a parameter to the loop, we won't have to do an ugly set!. Try this instead, it's closer to what you were aiming for:
(define (inputlist)
(let loop ((applist '()))
(let ((inpt (read)))
(cond ((not (number? inpt))
(display "not a number")
(newline)
(loop applist))
((zero? inpt)
(display "zero input: stopping list"))
(else
(let ((new-applist (append applist (list inpt))))
(display new-applist)
(newline)
(loop new-applist)))))))
As mentioned in the comments, bear in mind that appending at the end of a list inside a loop in general is a bad idea. It's ok for learning purposes, but in real-life code, you'd cons at the head of the list and reverse it at the end - this is more efficient.
Note that (cons x xs) where x is an element and xs is a list produces a new list which has x as its first element.
Here is one way to use cons to add an element in the end of a list:
Example:
Add 4 to (1 2 3)
1. Reverse the list: (3 2 1)
2. Add 4 to the front: (4 3 2 1)
3. Reverse: (1 2 3 4)
> (reverse (cons 4 (reverse (list 1 2 3)))
(1 2 3 4)
A function that uses this principle:
(define (cons-to-back x xs)
(reverse (cons x (reverse xs))))
> (cons-to-back 4 (list 1 2 3))
(1 2 3 4)
An alternative is to use append which appends the elements of two lists:
> (append '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
All we need to do, is to the the element into a list before using append:
> (append '(1 2 3) (list 4))
'(1 2 3 4)
Alternative definition of cons-to-back:
(define (cons-to-back x xs)
(append xs (list x)))
I'm newbie in Scheme and find out that if I change a list with set-car!/set-cdr! (even locally) the parent list is modified too. This is an example what I mean:
(define my-list '(1 2 3 4 5)) ; Original list
(define (function list) ; Some example function
(let ((copy list))
(set-car! copy 'new)
(display copy)
)
(function my-list); will display (new 2 3 4 5)
my-list; but the original is changed "forever" and will be also '(new 2 3 4 5)
My question is:
Is there a way to make a copy of the original list and work only on it, so at the end the original not to be changed?
Your code (let ((copy list)) allows list to be accessed through the name copy. So when you set-car! copy, you are actually set-car!'ing the original list.
The phenomenon of mutation in lisp like languages is a little confusing at first.
Mutation is to be avoided usually, for the reasons you have discovered. Parts of lists and things a floating around. This is because internally each node in a list has two parts - the car is it's value which may point to another list, and it's cdr is the part that follows it - usually this is a lit of things similar to the value in car.
This solves your problem using SRFI1's list-copy function
(let ((copy (list-copy list)))
Here is one way to copy a list (make a new list). This code is not really complete. When we look at it internally it adds pieces of a list in each recursive call. Where do the pieces of each new list-section come from? The cdr is generated with the call (list-copy (cdr list)), while the car is simply taken - not copied! - from the other list.
(define (list-copy list)
(if (null? list) '() (cons (car list) (list-copy (cdr list)))))
The result for you when experimenting, was that the car's of your list copy where borrowed from my-list.
Here is a more proper version:
(define (full-copy list)
(if (null? list)
'()
(if (list? list)
(cons (full-copy (car list)) (full-copy (cdr list)))
list)))
Only the code for the car has changed. Now, the car is reconstructed to. The only parts that are borrowed are the numbers (so this copy is not okay when the numbers are instead something more special). I'm not sure how the srfi1 version works.
The let statement only ever binds one identifier to an object - the object is not copied, you simply have another way to access it. So any changes (use of mutation, as in functions that end in "!") will affect both.
I suggest that when you're learning Scheme or Racket, you avoid all the ! functions such as set-car!, set-cdr!, and set!.
Instead of mutating things, try to think in terms of creating a new thing from the old thing(s).
For instance if you have a list and want something new at the start:
;; Return a new list with `x` replacing the head of the list `xs`.
(define (replace-head xs x) ; list? any? -> list?
(cons x (cdr xs)))
;; Using it:
(replace-head '(1 2 3 4 5) 100)
; => '(100 2 3 4 5)
p.s. A version of replace-head that can handle xs being empty could be:
(define (replace-head xs x) ; list? any? -> list?
(cond [(empty? xs) (list x)]
[else (cons x (cdr xs))]))
(replace-head '(1 2 3 4 5) 100)
; => '(100 2 3 4 5)
(replace-head '() 100)
; => '(100)
You also might find it helpful to look at How to Design Programs, for examples of good ways to design and think in Scheme or Racket.
I am quite new to scheme and I want to take a list such as (1 2 3 4) and send it to a function that will return (4 1 2 3). A second run would return (3 4 1 2) and so on creating a right shifted list with each call to the function.
The first way I figured out to solve this problem is to recursively swap the first and last values of the list. So in scheme I would append the car of the list with the cdr of the list and send the cdr of the list back to my function recursively until only one last swap can be made.
However I am not great with creating recursive functions and I'm having trouble doing it in a new language for me such as scheme. This is what I have tried so far to give an idea of where I want to head.
(define (rShift lst)
(if (null? lst)
'()
(append (cdr lst (car lst))(rShift (cdr lst)))))
The best you can do is take a look at the documentation of your interpreter to see what list functions are available, and construct a solution using them. For example, in Racket using the list procedures will accomplish a straightforward solution:
(define (rShift lst)
(cons ; stick together the solution
(last lst) ; pick the last item
(take lst (sub1 (length lst))))) ; pick all items except the last one
Let's give it a try:
(rShift '(1 2 3 4))
=> '(4 1 2 3)
(rShift (rShift '(1 2 3 4)))
=> '(3 4 1 2)
There are countless ways to solve this problem, I'll leave it to you to find the one that best suits your needs, but remember - always try to solve a problem in terms of the building blocks already at your disposal, and don't reinvent the wheel. Just for fun, here's another way, using reverse:
(define (rShift lst)
(let ((rev (reverse lst)))
(cons (car rev)
(reverse (cdr rev)))))
If you are intent on doing a recursive solution, then you need to ask yourself "what part of the problem can a solve that leaves an identical but smaller part of the problem remaining" and "how do I end the recursion".
For your problem you end the recursion when you have the last element which you then use to add to the front of the list. The problem is that recursing with 'right-shift' can't be sufficient because you need to keep around the list in order to put the last element on its front.
So:
(define (right-shift list)
(if (null? list)
'()
(let shifting ((list list) (result '()))
(if (null? (cdr list))
(cons (car list) (reverse result))
(shifting (cdr list) (cons (car list) result))))))
;; Hey look, it compiles... gosh I love interactive languages.
;; ... and works.
> (right-shift '(1 2 3 4))
(4 1 2 3)
> (right-shift (right-shift '(1 2 3 4)))
(3 4 1 2)
So I am new to using Scheme/Guile and have an assignment where I must take 2 inputs; a simple variable and a list.
I then add the variable to each number in the list and print it out. I have got it to load but when I enter this:
(add 1 (1 2 3))
I get this error:
Backtrace:
39: 0* [add 1 ...
39: 1* [1 2 3]
standard inout:39:8: In expression (1 2 3):
standard input:39:8:wrong type to appy: 1
ABORT: (misc-error)
Here is the code that I currently have.
(define a (list a))
(define (add y a)
(define x 0)
(while (< x (length a))
(display (+ y (car a)))
(newline)
(set! a (cdr a))
(set! x (+ x 1))
)
)
My question is:
How do I get the list to work in the parameter? I have looked around online but havent found much to fix this problem.
Many thanks ahead of time for any help you can give me.
You're getting an error because (1 2 3) tells it to call the function 1 with two arguments, 2 and 3. To create a list containing 1, 2 and 3, use (list 1 2 3).
Do you really intend to write a procedure in such an imperative way? It looks not very Scheme-like. Evaluating your procedures in GNU Guile yields many error messages about unbound variables etc. set! is eval for beginners of Scheme ;) :) (SICP treats assignment extensively in chapter 3). You can do it much easier in functional style without the side-effects of set! and display.
If you are already aware about mapping and high-order functions, you could do something like this:
(define (add x)
(lambda (y) (+ x y)))
(map (add 1) (list 1 2 3))
===> (2 3 4)
If not, write a recursive procedure yourself:
(define (add-to-list x list)
(if (null? list)
'()
(cons (+ x (car list))
(add-to-list x (cdr list)))))
(add-to-list 1 '(1 2 3))
===> (2 3 4)
In order to learn thinking recursively, consult books like SICP, HTDP or "The litte Schemer".
Below is my code which takes a car element of a list(carVal) and an list(initialized to empty) as parameters. I want to append the element to the list but the same is not working.
(define populateValues
(lambda (carVal currVal)
(append currVal(list carVal ))
(display currVal)))
The display shows empty list all the time () . Can anyone help me understand why?
Well, there is append! as a primitive, which solves most of your problems, as noted already, Scheme tends to frown on mutation, it is possible, but typically avoided, so all procedures that mutate have a ! (called a bang) at their end.
Also, set! does not mutate data, it changes an environment, it makes a variable point to another thing, the original data is left unchanged.
Mutating data in Scheme is quite cumbersome, but, to give you my own implementation of append! to see how it is done:
(define (append! lst . lsts)
(if (not (null? lsts))
(if (null? (cdr lst))
(begin
(set-cdr! lst (car lsts))
(apply append! (car lsts) (cdr lsts)))
(apply append! (cdr lst) lsts))))
Note the use of set-cdr!, which is a true mutator, it only works on pairs, it mutates data in memory, unlike `set!'. If a pair is passed to a function and mutated with set-cdr! or set-car!, it is mutated every-where in the program.
This obeys the SRFI append! spec which says that it should be variadic and that it should return an undefined value, for instance.
(define l1 (list 1 2 3 4))
(define l2 (list 2 3 4))
(define l3 (list 3 1))
(append! l1 l2 l3)
l1
l2
l3
Which displays:
(1 2 3 4 2 3 4 3 1)
(2 3 4 3 1)
(3 1)
As visible, append! can take an infinite number of arguments and it mutates them all but the last.
Scheme might not be the ideal language for you though. The use of append! as said before is nonstandard, instead, append is preferred, which does not mutate and is called for its return value. Which I implement as such:
(define (append . lsts)
(cond
((null? lsts) '())
((null? (car lsts)) (apply append (cdr lsts)))
(else (cons (caar lsts) (apply append (cdar lsts) (cdr lsts))))))
> (append (list 1 2 3) (list 4 5 6) (list 'granny 'porn))
(1 2 3 4 5 6 granny porn)
Which shows a more familiar Scheme style in the absence of mutation, heavy use of recursion
and no use of sequencing.
Edit: If you just want to add some elements to a list and not per se join two though:
(define (extend l . xs)
(if (null? l)
xs
(cons (car l) (apply extend (cdr l) xs))))
(define (extend! l . xs)
(if (null? (cdr l))
(set-cdr! l xs)
(apply extend! (cdr l) xs)))
(extend '(0 1 2 3) 4 5 6)
(define list1 '(0 1 2 3))
(extend! list1 4 5 6)
list1
Which does what you expect
append creates a new list, it does not modify an existing one.
This is because in general, Scheme (and Racket in this case) is a language that prefers functional style.
You could get somewhat closer with a set! -- but even that will disappoint you since it will modify only the local binding.
Note that in Racket in particular, lists are immutable, so there's nothing that can change a list.
Furthermore, even if you could modify a list this way, it's a very inefficient way to accumulate long lists, since you have to repeatedly scan the whole list.
Finally, if you have issues at this level, then I strongly recommend going over HtDP
(append foo bar) returns the concatenation of foo and bar. It doesn't change either foo or bar.
You have to update the value of currVal with set!. Your example should have
(set! currVal (append currVal (list carVal))
(display currVal)
You really need to think about what exact functionality you are looking for
If you want to mutate a referenced list in place, then you have to do the equivalent of append! (as noted in the other answers). But that is dangerous, BECAUSE you may have other code that is counting on the list being immutable, and if you are going to do that, your procedure needs to have a ! in the end to flag that danger.
A cheap approximation to what you want to do, in a more functional style, is:
(define (populateValues carVal currVal)
(let ((ll (append currVal (list carVal))))
(display ll)
ll))
Note that it makes a new list, does the append, displays the result, and RETURNS the new list as a value. This is a useful debugging technique if you don't have access to the intermediate value: bind to a varible, display or log it, and then return it.