`set-car!` of `cdr` changes `car` as well, why? - data-structures

(define l '(a))
(define p (cons l l))
(set-car! (cdr p) 'b)
After the last (set-car! (cdr p) 'b), p will be ((b) b) rather than ((a) b). Why?

A cons cell essentially contains two pointers to two other objects. Your code in question would translate roughly to this pseudo-C:
struct cons {
void *car;
void *cdr;
};
cons *l = &cons{"a", NULL};
cons *p = &cons{l, l}; // no copy takes place, we're handling pointers here
p->cdr->car = "b"; // this changes the "l" object, and nothing else

Related

How do collector functions work in Scheme?

I am having trouble understanding the use of collector functions in Scheme. I am using the book "The Little Schemer" (by Daniel P. Friedman and Matthias Felleisen). A comprehensive example with some explanation would help me massively. An example of a function using a collector function is the following snippet:
(define identity
(lambda (l col)
(cond
((null? l) (col '()))
(else (identity
(cdr l)
(lambda (newl)
(col (cons (car l) newl))))))))
... with an example call being (identity '(a b c) self) and the self-function being (define self (lambda (x) x)). The identity function returns the given list l, so the output of the given call would be (a b c). The exact language used is the R5RS Legacy-language.
Given how those "collector" functions are defined in the identity definition, calling
(identity xs col)
for any list xs and some "collector" function col, is equivalent to calling
(col xs)
so the same list will be "returned" i.e. passed to its argument "collector" / continuation function col. That explains its name, identity, then.
For comparison, a reverse could be coded as
(define reverse ; to be called as e.g. (reverse l display)
(lambda (l col)
(cond
((null? l) (col '())) ; a reversed empty list is empty
(else (reverse (cdr l) ; a reversed (cdr l) is newl --
(lambda (newl) ; what shall I do with it when it's ready?
(col ; append (car l) at its end and let col
(append newl ; deal with it!
(list (car l))))))))))
This style of programming is known as continuation-passing style: each function is passed a "continuation" that is assumed that it will be passed the result of the rest of computation, so that the original continuation / collector function will be passed the final result eventually. Each collector's argument represents the future "result" it will receive, and the collector function itself then specifies how it is to be handled then.
Don't get confused by the terminology: these functions are not "continuations" captured by the call/cc function, they are normal Scheme functions, representing "what's to be done next".
The definition can be read as
identity :
to transform a list xs
with a collector function col,
is
| to call (col xs) , if xs is empty, or
| to transform (cdr xs)
with a new collector function col2
such that
(col2 r) = (col (cons (car xs) r)) , otherwise.
(or we can write this in a pseudocode, as)
(identity list col) =
| empty? list -> (col list)
| match? list (x . xs) -> (identity xs col2)
where
(col2 r) = (col (cons x r))
col2 handles its argument r by passing (cons x r) to the previous handler col. This means r is transformed into (cons x r), but instead of being returned as a value, it is fed into col for further processing. Thus we "return" the new value (cons x r) by passing it to the previous "collector".
A sample call, as an illustration:
(identity (list 1 2 3) display)
= (identity (list 2 3) k1)
; k1 = (lambda (r1) (display (cons 1 r1))) = display ° {cons 1}
= (identity (list 3) k2)
; k2 = (lambda (r2) (k1 (cons 2 r2))) = k1 ° {cons 2}
= (identity (list ) k3)
; k3 = (lambda (r3) (k2 (cons 3 r3))) = k2 ° {cons 3}
= (k3 '()) ; (((display ° {cons 1}) ° {cons 2}) ° {cons 3}) []
= (k2 (cons 3 '())) ; ((display ° {cons 1}) ° {cons 2}) [3]
= (k1 (cons 2 (list 3))) ; (display ° {cons 1}) [2,3]
= (display (cons 1 (list 2 3))) ; display [1,2,3]
= (display (list 1 2 3))
update: in a pattern-matching pseudocode I've been fond of using as of late, we could write
identity [] col = col []
identity [a, ...d] col = identity d ( newl => col [a, ...newl] )
and
reverse [] col = col []
reverse [a, ...d] col = reverse d ( newl => col [...newl, a] )
which hopefully is so much visually apparent that it almost needs no explanation!
I'm adding the second answer in the hopes it can clarify the remaining doubts in case you have any (as the lack of the "accepted" mark would indicate).
In the voice of Gerald J. Sussman, as heard/seen in the SICP lectures of which the videos are available here and there on the internet tubes, we can read it as we are writing it,
(define identity
"identity" is defined to be
(lambda
that function which, when given
(l col)
two arguments, l and col, will
(cond
((null? l)
-- in case (null? l) is true --
OK, this means l is a list, NB
(col '()))
return the value of the expression (col '())
OK, this now means col is a function, expecting of one argument, as one possibility an empty list,
(else (identity (cdr l)
or else it will make a tail recursive call with the updated values, one being (cdr l),
(lambda (newl)
(col (cons (car l) newl)))))))
and the other a newly constructed function, such that when it will be called with its argument newl (a list, just as was expected of col -- because it appears in the same role, it must follow the same conventions), will in turn call the function col with the non-empty list resulting from prefixing (car l) to the list newl.
Thus this function, identity, follows the equations
( identity (cons (car l) (cdr l)) col )
==
( identity (cdr l) (lambda (newl) (col (cons (car l) newl))) )
and
( identity '() col )
==
( col '() )
describing an iterative process, the one which turns the function call
(identity [a, b, c, ..., n] col )
into the call
(col
(cons a (cons b (cons c ... (cons n '()) ... ))))
recreating the same exact list anew, before feeding it as an argument to the function col it has been supplied with.

Solving a puzzle in Racket

I tried to solve this puzzle https://puzzling.stackexchange.com/questions/40094/who-committed-the-crime using Racket.
A crime has been carried out by one person, there are 5 suspects. Each
suspect is asked under polygraph who they think committed the crime.
Their answers are as follows:
Terry : It wasn't Carl, It was Steve
Steve : It wasn't Matt, It wasn't Carl
Matt : It was Carl, It wasn't Terry
Ben : It was Matt, It was Steve
Carl : It was Ben, It wasn't Terry
The polygraph showed that
each suspect told one lie and one truth. Who committed the crime?
Following is my code:
(define (oneof a b)
(or (and a (not b)) (and b (not a))))
(for ((i 5))
(define templist (list #f #f #f #f #f)) ; make a temporary list of all false;
(set! templist (list-set templist i #t)) ; one by one keep one as true in this loop;
(define t (list-ref templist 0)) ; allocate each person according to above list (one kept true one by one)
(define s (list-ref templist 1))
(define m (list-ref templist 2))
(define b (list-ref templist 3))
(define c (list-ref templist 4))
(when ; test if all statements fit with above assignment:
(and
(oneof (not c) s) ; Terry's statement
(oneof (not m) (not c)) ; Steve's statement
(oneof c (not t)) ; Matt's statement
(oneof m s) ; Ben's statement
(oneof b (not t))) ; Carl's statement
(println (list "t" "s" "m" "b" "c")) ; print allocation if all statement fit in;
(println templist)))
Output indicates Matt committed the crime:
'("t" "s" "m" "b" "c")
'(#f #f #t #f #f)
It works but the code is imperative and not very functional (especially define t, define s, ... part). How can it can be improved? Thanks for your comments/answers.
This answer is based on #Oscar Lopez's answer, but modified to use more idiomatic features like filter with a helper function instead of for, when, and println.
This style of code returning a value is much more useful because the value it produces could be used in later code, where an imperative action like println would be much less reusable.
#Oscar Lopez's make-matrix function produces an initial list of possibilities, but we have to filter out the ones that are impossible, and leave only the ones that are possible. So write that down:
;; If there are no valid solutions, this will be an empty list,
;; if there's one, this will be a list of one element,
;; and if there are more, this will include all of them.
(filter valid-solution? (make-matrix 5))
;; Wish list:
;; valid-solution? : (Listof Boolean) -> Boolean
Now we just need to add a definition for valid-solution?
;; valid-solution? : (Listof Boolean) -> Boolean
;; Given a list containing booleans for whether Terry, Steve, Matt, Ben, and Carl did it,
;; this determines whether that combination is possible under the constraints in the problem.
(define (valid-solution? row)
; unpack each row into the corresponding variables
(match-define (list t s m b c) row)
; don't reinvent the wheel, `oneof` is called `xor`
(and (xor (not c) s)
(xor (not m) (not c))
(xor c (not t))
(xor m s)
(xor b (not t))))
And that's it. The full program including make-matrix is this:
#lang racket
;; make-matrix : Natural -> (Listof (Listof Boolean))
;; generate a matrix with possibilities
(define (make-matrix n)
(for/list ([i (in-range n)])
(build-list n (λ (j) (= i j)))))
;; valid-solution? : (Listof Boolean) -> Boolean
;; Given a list containing booleans for whether Terry, Steve, Matt, Ben, and Carl did it,
;; this determines whether that combination is possible under the constraints in the problem.
(define (valid-solution? row)
; unpack each row into the corresponding variables
(match-define (list t s m b c) row)
; don't reinvent the wheel, `oneof` is called `xor`
(and (xor (not c) s)
(xor (not m) (not c))
(xor c (not t))
(xor m s)
(xor b (not t))))
;; If there are no valid solutions, this will be an empty list,
;; if there's one, this will be a list of one element,
;; and if there are more, this will include all of them.
(filter valid-solution? (make-matrix 5))
This is an equivalent program written in idiomatic Racket - and avoiding all those pesky set! and list-ref which are frowned upon when writing functional-style code:
; generate a matrix with possibilities
(define (make-matrix n)
(for/list [(i (in-range n))]
(build-list n (λ (j) (= i j)))))
; iterate over each row
(for [(row (make-matrix 5))]
; unpack each row into the corresponding variables
(match-let ([(list t s m b c) row])
; don't reinvent the wheel, `oneof` is called `xor`
(when (and (xor (not c) s)
(xor (not m) (not c))
(xor c (not t))
(xor m s)
(xor b (not t)))
(println '(t s m b c))
(println row))))

Using racket structs for summing elements at even and odd positions

In class we wrote an interpreter for a made up language (lanG) using the following racket structs.
(struct const (n))
(struct bool (b))
(struct join (e1 e2))
(struct if-then-else (b e1 e2))
(struct negate (e))
(struct add (e1 e2))
(struct multiply (e1 e2))
(struct head (e)) ;returns the head of the list
(struct tail (e)) ;returns the tail of the list
(struct biggerThan (e1 e2))
Macros for this language are defined as racket functions. A simple example would be:
(define (threeTimes x)
(add x (add x x)))
And using it would look like:
(lanG (threeTimes (const 3)))
which would produce an answer:
(const 9)
Now to my problem. There was a task on the exam where we had to write a macro sumAtEvenAndOdd, which would sum a list of lanG constants,
made with the join struct and return a pair of values consisting of the sum of elements at even positions and the sum of elements
at the odd positions.
An example of such a list would be:
(join (const 3) (join (const 2) (const 5))) ;lanG list with no null at the end
And its result would be:
(join (const 2) (const 8))
I tried solving this by converting the list into a racket list, ziping the positions with the elements, filtering the odd or even elements out of the list,
and producing the pair using the sums of those lists. This works but I am overcomplicating. Professor said the solution is about 5 lines long.
I thank you in advance for all your help.
I assume there are also predicates to identify a const and a join - let's call them const? and join?.
Supposing we had a function for adding up every other item of a list, sumAtEvenAndOdd could look like this:
(define (sumAtEvenAndOdd xs)
(join (sumEveryOther (tail xs)) (sumEveryOther xs)))
and then sumEveryOther could be implemented like this:
(define (sumEveryOther x)
(if-then-else (const? x)
x
(if-then-else (join? (tail x))
(add (head x) (sumEveryOther (tail (tail x))))
(head x))))
This is of course not optimal, since it traverses the list twice, but it's short ("exam-size") and implemented entirely within lanG.
A slightly longer solution that only traverses the list once, using accumulators:
(define (sumEvenOdd x evens odds odd?)
(if-then-else (const? x)
(if-then-else odd?
(join evens (add odds x))
(join (add evens x) odds))
(if-then-else odd?
(sumEvenOdd (tail x) evens (add (head x) odds) (negate odd?))
(sumEvenOdd (tail x) (add (head x) evens) odds (negate odd?)))))
(define (sumAtEvenAndOdd xs)
(sumEvenOdd xs 0 0 (bool #t)))
So join is like a pair where join-e2 could be a join?. To loop through it you do the same as with pair? with a dotted list since a proper list in you example ended with a const.
(let loop ((lst '(1 2 3 4 5 6 . 7)) (o 0) (e 0) (odd? #t))
(let* ((ele (if (pair? lst) (car lst) lst))
(no (if odd? (+ ele o) o))
(ne (if odd? e (+ ele e))))
(if (pair? lst)
(loop (cdr lst) no ne (not odd?))
(cons no ne))))
Here is a simple recursive solution.
(define (sum-even/odd xs)
(if (null? xs)
(values 0 0)
(call-with-values
(λ () (sum-even/odd (cdr xs)))
(λ (e o) (values (+ (car xs) o) e)))))
> (sum-even/odd '(1 2 3 4 5 6 7))
16
12

Scheme code doesn't work

I have some code that is supposed to take a list and return the reverse of that list. However, it just returns the list. Here is the code
(define (reverse listx)
(define (thing list1 list2)
(if (null? list1)
list2
(thing (cdr list1) (append list2 (list (car list1))))
)
)
(thing listx nil)
)
The problem I think hinges on the fact that
(append nil 1) => 1
rather than
(append nil 1) => (1)
No, that's not it: you do call (append list2 (list ...)), with the 2nd arg enclosed in a list.
The problem is that you use append while you ought to use simple cons there: (thing (cdr list1) (cons (car list1) list2)).
With cons, each new element from list1 will go on top (i.e. "to the left of") the previously handled ones, hence building the result in reverse:
(a b c d e)
--------------------------
(a b c d e) ()
(b c d e) (a)
(c d e) (b a)
(d e) (c b a)
(e) (d c b a)
() (e d c b a)
--------------------------
(e d c b a)
But with append you're just recreating the same list.

Operating on Nested Lists

I have a nested list structure of arbitrary length, with a depth of three. The first level has arbitrary length, as does the second level, but the third level is guaranteed to have a uniform length across the whole thing. An example of said structure would be '(((A B) (C D)) ((E F) (G H)) ((I J))).
I'm trying to write a function that would apply another function across the different levels of the structure (sorry, I don't really know how to phrase that). An example of the function mapping across the example structure would be in this order:
f A C = AC, f B D = BD, f E G = EG, f F H = FH, f I = I, f J = J,
yielding
'((AC BD) (EG FH) (I J))
but imagining that the third level of the list contains many more elements (say, around 32,000 in the final version).
Essentially, what I'm trying to do would be expressed in Haskell as something like f . transpose. I know I need something like (map car (map flatten (car ...))) to get the first part of the first section, but after that, I'm really lost with the logic here. I'm sorry if this is a really convoluted, poorly explained question. I'm just really lost.
How would I go about applying the function across the structure in this manner?
(define l '(((A B)
(C D))
((E F)
(G H))
((I J)))
)
(define zip (lambda lists (apply map list lists)))
(define (f values) (list 'f values))
(map (lambda (v) (map (lambda values (apply f values)) (apply zip v))) l)
prints
(((f (a c)) (f (b d))) ((f (e g)) (f (f h))) ((f (i)) (f (j))))
It would be much easier to define your f as a function that takes in a list of values. If not, then the last form is easy to add apply to, but it doesn't make it better. (Using a rest argument means that the language will have to create these lists anyway.)
#lang racket
(define data '(((A B) (C D)) ((E F) (G H)) ((I J))))
(define (f xs) (string->symbol (string-append* (map symbol->string xs))))
(map (λ (pairs)
(list (f (map first pairs))
(f (map second pairs))))
data)
(map (λ (pairs) (map f (apply map list pairs)))
data)
(for/list ([pairs (in-list data)])
(for/list ([xs (in-list (apply map list pairs))])
(f xs)))

Resources