The following function from pg 150 of the Seasoned Schemer establishes whether two lists have the same identity (i.e. occupy the same memory) by mutating each list's cdr and then checking whether the change has affected both:
(define same?
(lambda (c1 c2)
(let ((t1 (cdr c1))
(t2 (cdr c2)))
(set-cdr! c1 1)
(set-cdr! c2 2)
(let ((v (= (cdr c1) (cdr c2))))
(set-cdr! c1 t1)
(set-cdr! c2 t2)
v))))
Now if I define a_list as follows:
(define a_list (list 'a 'b 'c 'd))
and evaluate
(same? a_list (cdr a_list))
the function returns #f, and the debugger (Dr. Racket) confirms that these two lists -- which ought to share most of their members since the second argument is a proper subset of the first -- do in fact have different copies of the same members. How is this possible?!
For a slight twist on this idea:
(set-cdr! (cddr a_list) a_list)
Now a_list is cyclical. If I test this function with same? it only registers #t when the two arguments are in phase, i.e. (same? a_list a_list) and (same? a_list (cdddr a_list)).
[EDIT The answer is at the bottom of the accepted post's comment chain]
The same? function does not check whether two lists share elements.
It checks whether two pairs (i.e. two cons cells) are the same.
In (define a_list (list 'a 'b 'c 'd)) you have 4 pairs.
In (same? a_list (cdr a_list)) you check whether the first
and second pair is the same pair, and since they aren't,
same? returns #f.
With respect to:
.. and the debugger (Dr. Racket) confirms that these two lists -- which
ought to share most of their members since the second argument is a
proper subset of the first -- do in fact have different copies of the
same members. How is this possible?!
Can you be more precise about how you check this in DrRacket?
The two lists a-list and (cdr a-list) do share members.
EDIT:
Suppose c1 and c2 are names for two different cons cells:
c1: (foo . bar) c2: (baz . qux)
Now we evaluate (set-cdr! c1 1) and get:
c1: (foo . 1) c2: (baz . qux)
Now we evaluate (set-cdr! c2 2) and get:
c1: (foo . 1) c2: (baz . 2)
Then we compare the cdrs with (= (cdr c1) (cdr c2)).
Since the cdrs are different, we get #f.
Conclusion: When the cons cells are different, same? returns #f.
Now suppose c1 and c2 are names for the same cons cell:
c1 = c2: (foo . bar)
Now we evaluate (set-cdr! c1 1) and get:
c1 = c2: (foo . 1)
Now we evaluate (set-cdr! c2 2) and get:
c1 = c2: (foo . 2)
Then we compare the cdrs with (= (cdr c1) (cdr c2)).
Since the cdrs are the same, we get #t.
Conclusion: When the cons cells are the same, same? returns #f.
EDIT 2
To check whether the cons cell c is one of the
cons cells of l use this:
(define (loop c l)
(cond
[(null? l) #f]
[(same? c l) #t]
[else (loop c (cdr l))]))
Related
For my programming languages class I'm supposed to write a function in Scheme to reverse a list without using the pre-made reverse function. So far what I got was
(define (reverseList lst)
(COND
((NULL? lst) '())
(ELSE (CONS (reverseList(CDR lst)) (CAR lst)))
))
The problem I'm having is that if I input a list, lets say (a b c) it gives me (((() . c) . b) . a).
How am I supposed to get a clean list without multiple sets of parenthesis and the .'s?
The problem with your implementation is that cons isn't receiving a list as its second parameter, so the answer you're building isn't a proper list, remember: a proper list is constructed by consing an element with a list, and the last list is empty.
One possible workaround for this is to use a helper function that builds the answer in an accumulator parameter, consing the elements in reverse - incidentally, this solution is tail recursive:
(define (reverse lst)
(reverse-helper lst '()))
(define (reverse-helper lst acc)
(if (null? lst)
acc
(reverse-helper (cdr lst) (cons (car lst) acc))))
(reverse '(1 2 3 4 5))
=> '(5 4 3 2 1)
You are half way there. The order of the elements in your result is correct, only the structure needs fixing.
What you want is to perform this transformation:
(((() . c) . b) . a) ; input
--------------------
(((() . c) . b) . a) () ; trans-
((() . c) . b) (a) ; for-
(() . c) (b a) ; mation
() (c b a) ; steps
--------------------
(c b a) ; result
This is easy to code. The car and cdr of the interim value are immediately available to us. At each step, the next interim-result is constructed by (cons (cdr interim-value) interim-result), and interim-result starts up as an empty list, because this is what we construct here - a list:
(define (transform-rev input)
(let step ( (interim-value input) ; initial set-up of
(interim-result '() ) ) ; the two loop variables
(if (null? interim-value)
interim-result ; return it in the end, or else
(step (car interim-value) ; go on with the next interim value
(cons ; and the next interim result
(... what goes here? ...)
interim-result )))))
interim-result serves as an accumulator. This is what's known as "accumulator technique". step represents a loop's step coded with "named-let" syntax.
So overall reverse is
(define (my-reverse lst)
(transform-rev
(reverseList lst)))
Can you tweak transform-rev so that it is able to accept the original list as an input, and thus skip the reverseList call? You only need to change the data-access parts, i.e. how you get the next interim value, and what you add into the interim result.
(define (my-reverse L)
(fold cons '() L)) ;;left fold
Step through the list and keep appending the car of the list to the recursive call.
(define (reverseList lst)
(COND
((NULL? lst) '())
(ELSE (APPEND (reverseList(CDR lst)) (LIST (CAR lst))))
))
Instead of using cons, try append
(define (reverseList lst)
(if (null? lst)
'()
(append (reverseList (cdr lst)) (list (car lst)) )
)
)
a sample run would be:
1]=> (reverseList '(a b c 1 2 + -))
>>> (- + 2 1 c b a)
car will give you just one symbol but cdr a list
Always make sure that you provide append with two lists.
If you don't give two lists to the cons it will give you dotted pair (a . b) rather than a list.
See Pairs and Lists for more information.
I am trying to write a program that will check the structural equivalence of some list input, whether it includes just atoms or nested sub lists.
I am having trouble with using AND, I don't even know if its possible and I cant seem to understand documentation I am looking at.
My code:
(define (structEqual a b)
(cond
(((null? car a) AND (null? car b)) (structEqual (cdr a) (cdr b)))
(((null? car a) OR (null? car b)) #f)
(((pair? car a) AND (pair? car b))
(if (= (length car a) (length car b))
(structEqual (cdr a) (cdr b))
#f))
(((pair? car a) OR (pair? car b)) #f)
(else (structEqual (cdr a) (cdr b)))))
The idea is (i think): (when I say both, i mean the current cdr of a or b)
Check if both a and b are null, then they are structurally equal
Check if only either a or b is null, then they are not structually equal
Check if both of them are pairs
If they are both pairs, then see if the length of the pair is equal, if not they are not structurally equal.
If they are not both pairs, then if one of them is a pair and the other isnt then they are not structurally equivalent.
If neither of them are pairs, then they both must be atoms, so they are structurally equivalent.
So as you can see I am trying to recursively do this by checking the equivalence of the car of a or b, and then either returning #f if they fail or moving on to the cdr of each if they are equivalent at each step.
Any help?
There is no infix operators in Scheme (or any LISP) only prefix. Every time the operator comes first. (or x (and y z q) (and y w e)) where each letter can be a complex expression. Everything that is not #f is a true value. Thus (if 4 'a 'b) evaluates to a because 4 is a true value. car needs its parentheses.
When evaluating another predicate in cond you should make use of the fact that everything up to that has been false. eg.
(define (structure-equal? a b)
(cond
((null? a) (null? b)) ; if a is null the result is if b is null
((not (pair? a)) (not (pair? b))) ; if a is not pair the result is if b is not also
((pair? b) (and (structure-equal? (car a) (car b)) ; if b is pair (both a and b is pair then) both
(structure-equal? (cdr a) (cdr b)))) ; car and cdr needs to be structurally equal
(else #f))) ; one pair the other not makes it #f
(structure-equal '(a (b (c d e) f) g . h) '(h (g (f e d) c) b . a)) ; ==> #t
This question already has answers here:
Count occurrence of element in a list in Scheme?
(4 answers)
Closed 8 years ago.
I want to make a function that occurs how many times an element occurs in a list. For example in the list: '(a b c b b c c a) I want it to return a nested list with:
'((a 2) (b 3) (c 3))
I know the function will look sort of like this:
(define collect-similar
(lambda (elm ls)
(cond
[(null? ls) '()]
[(equal? elm (car ls))]
I know that I need to continue checking through the list until it arrives back at the base case of the null list and I can check the rest of the list by using cadr. But I'm not too sure on how to get the value and how to make it return the nested list.
The next function I'm trying to write finds the most common element in the list. For example running the function on the list '(a a a a a b c) will simply return a. I know I can make use of the collect-similar function and find which number is the highest.
This has been asked before, just adapt one of #ChrisJester-Young's bagify implementations. For example:
(define (collect-similar lst) ; a slightly modified `bagify`
(hash->list
(foldl (lambda (key ht)
(hash-update ht key add1 0))
'#hash()
lst)))
(collect-similar '(a b c b b c c a))
=> '((a . 2) (b . 3) (c . 3))
With collect-similar in place, it's simple to find the most common element:
(define (most-common lst)
(let loop ((alst (collect-similar lst)) ; use previous procedure
(maxv '(#f . -inf.0)))
(cond ((null? alst) (car maxv))
((> (cdar alst) (cdr maxv))
(loop (cdr alst) (car alst)))
(else
(loop (cdr alst) maxv)))))
(most-common '(a a a a a b c))
=> 'a
This is the function that removes the last element of the list.
(define (remove-last ll)
(if (null? (cdr ll))
'()
(cons (car ll) (remove-last (cdr ll)))))
So from my understanding if we cons a list (eg. a b c with an empty list, i.e. '(), we should get
a b c. However, testing in interaction windows (DrScheme), the result was:
If (cons '() '(a b c))
(() a b c)
If (cons '(a b c) '())
((a b c))
I'm like what the heck :(!
Then I came back to my problem, remove all elements which have adjacent duplicate. For example,
(a b a a c c) would be (a b).
(define (remove-dup lst)
(cond ((null? lst) '())
((null? (cdr lst)) (car lst))
((equal? (car lst) (car (cdr lst))) (remove-dup (cdr (cdr lst))))
(else (cons (car lst) (car (cdr lst))))
)
)
It was not correct, however I realize the answer have a . between a b. How could this happen?
`(a . b)`
There was only one call to cons in my code above, I couldn't see which part could generate this .. Any idea?
Thanks,
cons build pairs, not lists. Lisp interpreters uses a 'dot' to visually separate the elements in the pair. So (cons 1 2) will print (1 . 2). car and cdr respectively return the first and second elements of a pair. Lists are built on top of pairs. If the cdr of a pair points to another pair, that sequence is treated as a list. The cdr of the last pair will point to a special object called null (represented by '()) and this tells the interpreter that it has reached the end of the list. For example, the list '(a b c) is constructed by evaluating the following expression:
> (cons 'a (cons 'b (cons 'c '())))
(a b c)
The list procedure provides a shortcut for creating lists:
> (list 'a 'b 'c)
(a b c)
The expression (cons '(a b c) '()) creates a pair whose first element is a list.
Your remove-dup procedure is creating a pair at the else clause. Instead, it should create a list by recursively calling remove-dup and putting the result as the second element of the pair. I have cleaned up the procedure a bit:
(define (remove-dup lst)
(if (>= (length lst) 2)
(if (eq? (car lst) (cadr lst))
(cons (car lst) (remove-dup (cddr lst)))
(cons (car lst) (remove-dup (cdr lst))))
lst))
Tests:
> (remove-dup '(a b c))
(a b c)
> (remove-dup '(a a b c))
(a b c)
> (remove-dup '(a a b b c c))
(a b c)
Also see section 2.2 (Hierarchical Data and the Closure Property) in SICP.
For completeness, here is a version of remove-dup that removes all identical adjacent elements:
(define (remove-dup lst)
(if (>= (length lst) 2)
(let loop ((f (car lst)) (r (cdr lst)))
(cond ((and (not (null? r))(eq? f (car r)))
(loop f (cdr r)))
(else
(cons (car lst) (remove-dup r)))))
lst))
Here in pseudocode:
class Pair {
Object left,
Object right}.
function cons(Object left, Object right) {return new Pair(left, right)};
So,
1. cons('A,'B) => Pair('A,'B)
2. cons('A,NIL) => Pair('A,NIL)
3. cons(NIL,'A) => Pair(NIL,'A)
4. cons('A,cons('B,NIL)) => Pair('A, Pair('B,NIL))
5. cons(cons('A 'B),NIL)) => Pair(Pair('A,'B),NIL)
Let's see lefts and rights in all cases:
1. 'A and 'B are atoms, and whole Pair is not a list, so (const 'a 'b) gives (a . b) in scheme
2. NIL is an empty list and 'A is an atom, (cons 'a '()) gives list (a)
3. NIL and 'A as above, but as left is list(!), (cons '() 'a) gives pair (() . a)
4. Easy case, we have proper list here (a b).
5. Proper list, head is pair (a . b), tail is empty.
Hope, you got the idea.
Regarding your function. You working on LIST but construct PAIRS.
Lists are pairs (of pairs), but not all pairs are lists! To be list pair have to have NIL as tail.
(a b) pair & list
(a . b) pair not list
Despite cons, your function has errors, it just don't work on '(a b a a c c d). As this is not related to your question, I will not post fix for this here.
I have the following items
(define itemslist
(list 'a1 'b2 'c3 (list 'z1 'z2) 'd5 'e6))
My method to find items is below
(define find-item
(lambda (item itemslist)
(cond ((null? itemslist) #f)
((list? (car itemslist))
(cond ((null? itemslist) #f)
(else (find-item item (car itemslist)))))
((equal? stn (car itemslist)) (display "found"))
(else (find-stn stn (cdr itemslist)))
)
)
)
With my method above I can find a1, b2, c3, z1, z2. But when I want to find d5 onwards, it returns nothing. It seems to have skip the stack. Btw, I am just starting to learn Scheme so simple explanation would be better.
One more qns, how about if I have this
(list 'a1 'b2 'c3 (list 'z1 (list 'y1 'y2) 'z2) 'd5 'e6)
does this works as well? Thanks!
Yes, you skip lists.
Example:
'(1 '(2 3) 4)
If (list? '(2 3)) is true, the else part (outer cond) wont be evaluated so 4 is skipped.
So, either you place the else part inside list? block or you redesign your code.
If the itemlist is a list/pair you can pass it right away in a recursive call so
you can avoid writing code like (car itemslist) inside predicates thus make the code simple.
Here is a redesigned (simplified) version:
(define (find-item item l)
(cond ((equal? item l) #t)
((pair? l) (or (find-item item (car l)) (find-item item (cdr l))))
(else #f)))
Tip: you can also can write lists with '(...) notation instead of (list ...), ie
(find-item 'z2 '('a1 'b2 'c3 '('z1 '('y1 'y2) 'z2) 'd5 'e6))
#t
(find-item 'e6 '('a1 'b2 'c3 '('z1 '('y1 'y2) 'z2) 'd5 'e6))
#t
To write more for the sake of writing more... This is usually called a "tree-walk" because a nested list like that is really a binary tree. Lists are really made up of binary pairs, so when you have a pair (l . r), l is the left branch of the tree and r is the right branch of the tree.
For example, '(1 (2 3) 4) is short for '(1 . ((2 . (3 . ())) . (4 . ()))) which can be drawn as
.
/ \
1 .
/ \
/ .
/ / \
. 4 ()
/ \
2 .
/ \
3 ()
and at any pair (l . r), car gets you the left tree and cdr gets you the right. So, when you write (pair? ls), you're really asking if you're at a branch in the tree, at which point you should recur on both the left branch (car) and the right branch (cdr). Hope that helps you understand lists.
Even though you got your [specific] answer, here's something that might help you with similar questions.
(define describe
(lambda (e)
(cond #;((list? e)
`(list ,#(map describe e)))
((pair? e)
`(cons ,(describe (car e))
,(describe (cdr e))))
((vector? e)
`(vector ,#(map describe (vector->list e))))
((or (null? e) (symbol? e)) `',e)
(else e))))
This procedure prints the code that generates a given sexpr. For example:
> (describe '(a 2 b 3))
(cons 'a (cons 2 (cons 'b (cons 3 '()))))
It will also place the quotes where needed, so it places them before symbols or () but not before numbers. When you are comfortable with nested pairs, you may want to remove the #; on the third line, to generate more compact output:
> (describe '(a 2 b 3))
(list 'a 2 'b 3)
This code can teach you many things about quote. For example:
> (describe ''''a)
(list 'quote (list 'quote (list 'quote 'a)))