I have a circular list, eg: #0=(1 2 3 4 . #0#).
What I want to do is to insert a new element (x) into this list so that the outcome is #0=(x 1 2 3 4 . #0#). I have been trying using this code (x is the circular list):
(define (insert! elm)
(let ((temp x))
(set-car! x elm)
(set-cdr! x temp)))
However, I think that set-cdr! is not working like I want it to. What am I missing here? Maybe I am way off?
The easiest way to prepend an element to a list is to modify the car of the list, and set the cdr of the list to a new cons whose car is the original first element of the list and whose cdr is the original tail of the list:
(define (prepend! x list) ; list = (a . (b ...))
(set-cdr! list (cons (car list) (cdr list))) ; list = (a . (a . (b ...)))
(set-car! list x)) ; list = (x . (a . (b ...)))
(let ((l (list 1 2 3)))
(prepend! 'x l)
(display l))
;=> (x 1 2 3)
Now, that will still work with circular lists, because the cons cell (i.e., pair) that is the beginning of the list remains the same, so the "final" cdr will still point back to object that is the beginning. To test this, though, we need some functions to create and sample from circular lists, since they're not included in the language (as far as I know).
(define (make-circular list)
(let loop ((tail list))
(cond
((null? (cdr tail))
(set-cdr! tail list)
list)
(else
(loop (cdr tail))))))
(define (take n list)
(if (= n 0)
'()
(cons (car list)
(take (- n 1)
(cdr list)))))
(display (take 10 (make-circular (list 1 2 3))))
;=> (1 2 3 1 2 3 1 2 3 1)
Now we can check what happens if we prepend to a circular list:
(let ((l (make-circular (list 1 2 3))))
(prepend! 'x l)
(display (take 15 l)))
;=> (x 1 2 3 x 1 2 3 x 1 2 3 x 1 2)
Since you're trying to prepend an element to a circular list, you need to do two things:
Insert a new cons cell at the front of the list containing the additional element. This is easy because you can just perform a simple (cons elm x).
You also need to modify the recursive portion of the circular list to point at the newly created cons cell, otherwise the circular portion will only include the old parts of the list.
To perform the latter, you need a way to figure out where the "end" of the circular list is. This doesn't actually exist, since the list is, of course, circular, but it can be determined by performing an eq? check on each element of the list until it finds an element equal to the head of the list.
Creating a helper function to do this, a simple implementation of insert! would look like this:
(define (find-cdr v lst)
(if (eq? v (cdr lst)) lst
(find-cdr v (cdr lst))))
(define (insert! elm)
(set! x (cons elm x))
(set-cdr! (find-cdr (cdr x) (cdr x)) x))
Related
I need to devise a procedure which returns the number of distinct pairs in any structure. Is my program correct? The solutions I have found online are all quite different.
(define (count-pairs x)
(define (count-cdr x)
(if (not (pair? x))
0
(+ (count-pairs (car x))
(count-cdr (cdr x)))))
(if (not (pair? x))
0
(+ (count-pairs (car x))
(count-cdr (cdr x))
1)))
Notice that the question asks for the number of distinct pairs in any structure, so we have to be careful and avoid counting more than once the same pair, even if it's in a different place within the structure! Also notice the hint:
Traverse the structure, maintaining an auxiliary data structure that is used to keep track of which pairs have already been counted
You have to keep track of the pairs you've found, and check if they are repeated, your solution completely ignores the hint. This worked for me:
(define (count-pairs x)
(let ((acc '()))
(define (count lst)
(cond ((or (not (pair? lst)) (memq lst acc)) 0)
(else (set! acc (cons lst acc))
(+ 1 (count (car lst)) (count (cdr lst))))))
(count x)))
And here are some tests that prove my point:
(define l1 (cons 1 (cons 2 (cons 3 null))))
(define x1 (cons null null))
(define y1 (cons x1 x1))
(define l2 (cons y1 null))
(define x2 (cons 1 (cons 2 null)))
(define l3 (cons x2 x2))
(define x3 (cons null null))
(define y3 (cons x3 x3))
(define l4 (cons y3 y3))
(count-pairs l1)
=> 3
(count-pairs l2)
=> 3
(count-pairs l3)
=> 3
(count-pairs l4)
=> 3
Here is what I think is a rather natural approach to this in Scheme. Although people (notably me) obsess about making processes iterative rather than recursive rather too much (why is stack space so much more valuable than the potentially huge agenda that this code creates?), it is quite nice to write iterative programs for this sort of thing.
This function keeps two lists:
a list of pairs that it has already seen, so it can know whether it's seen a pair
an agenda of things it needs to process, onto which it pushes the cdr of new pair it sees, before processing the car.
The process is then to check if the thing it's looking at is both a pair and has not been seen. If that's true then it adds one to the count and processes the car of the pair, pushing the cdr onto the agenda and the pair as whole onto the seen list. If that's not true then it either pulls something off the agenda to process, or, if there's no agenda it's finished and the count is the answer.
Note that, in real life, cp-loop would be a named let which makes the iteration clearer I think.
(define (count-pairs thing)
(define (cp-loop it count agenda seen)
(cond
[(and (pair? it) (not (memq it seen)))
;; It's a pair, and we have not seen it, so:
;; - add one to the count
;; - loop on its car
;; - pushing its cdr onto the agenda
;; - note we've seen it
(cp-loop (car it)
(+ count 1)
(cons (cdr it) agenda)
(cons it seen))]
[(not (null? agenda))
;; Either it is not a pair, or we've seen it
;; But there is more to do, so:
;; - pick the first agenda item & try that
(cp-loop (first agenda)
count
(rest agenda)
seen)]
[else
;; it's not a pair or we have seen it
;; and there is no agenda: we are done
count]))
(cp-loop thing 0 '() '()))
And now:
> (define o1 '(1 . (2 . (3 . ()))))
> o1
'(1 2 3)
> (count-pairs o1)
3
> (define o2 '(1 . (2 . (3 . (4 . 5)))))
> o2
'(1 2 3 4 . 5)
> (count-pairs o2)
4
> (define o3 (cons o1 o1))
> (count-pairs o3)
4
> o3
'((1 2 3) 1 2 3)
> (count-pairs '((1 2 3) 1 2 3))
7
In the following code:
(define l (cons 1 2))
l
(list? l)
(pair? l)
(list-ref l 0) ; works;
(list-ref l 1) ; DOES NOT WORK;
Output:
'(1 . 2)
#f
#t
1
Error: list-ref: index reaches a non-pair
index: 1
in: '(1 . 2)
Why (list-ref l 0) works if l is not a list. Otherwise, why (list-ref l 1) does not work. This behavior seems to be inconsistent and confusion creating.
The functions car and cdr also work, returning 1 and 2, respectively. Why should cdr work if this is not a list. One would expect car to return 1.2 and not just 1.
From the documentation:
The lst argument need not actually be a list; lst must merely start with a chain of at least (add1 pos) pairs.
This means the lst argument is allowed to be an improper list as long as the index is smaller than the index of the first pair whose cdr is a non-pair.
A list is simply a lot of pairs linked together terminating in an empty list. For example, (list 1 2 3) is equivalent to (cons 1 (cons 2 (cons 3 '()))).
There's no reason to loop through the entire list to check if l is a list, so as long as it looks like a list, scheme doesn't care if it terminates in an empty list. list-ref is basically defined as so:
(define (list-ref lyst index)
(cond ((= index 0) (car index))
(else (list-ref (cdr lyst) (- index 1)))))
So of course it will encounter an error when it tries to run (car 2)
As for the other question, car and cdr simply return the first element in a pair and the second element in a pair respectively.
Is it possible to add or remove elements in the middle of a linked list in Scheme? I can't seem to think of a way doing this with car/cdr/cons, but I reccon there must be a way to this.
If I have a list '(1 3 5 6), and I need to put in 4 between 5 and 6 for example, is this doable?
Adding an element at a given position was done in a previous answer. Removing an element at a given position is similar:
(define (delete-at k lst)
(cond ((null? lst)
'())
((zero? k)
(cdr lst))
(else
(cons (car lst)
(delete-at (sub1 k) (cdr lst))))))
Here are two versions:
; list-insert : value index list -> list
; return list where the ith element is x
(define (list-insert x i xs)
(if (= i 0)
(cons x xs)
(cons (first xs) (list-insert x (- i 1) (rest xs)))))
(define (list-insert/version2 x i xs)
(define-values (before after) (split-at xs i))
(append before (list x) after))
(list-insert/version2 'x 2 '(a b c d))
(list-insert 'x 2 '(a b c d))
Both versions will allocate a new list. For large lists it will become inefficient.
I was wondering, if I was given a list such as:
(list 3 6 9 2 1 0 5 9)
and I wanted to produce only the first 5. i.e.: I want to produce:
(list 3 6 9 2 1)
How could I go about doing this. By the way, recursion is not allowed, and the language is intermediate student. Thanks :)
Actually a lis like (1 2 3 4) is a chain of pairs (1 . (2 . (3 . (4 . ())))). You cannot reuse the pairs since you need the 5th pair to point to () (null) instead of the rest of the pair chain. The only way to do this is to make a new pair for each element you'd like by using the same car elements in each.
(define (take lst cnt)
(if (zero? cnt) ; if you ask for zero element
'() ; then return empty list
(cons (car lst) ; else make a pair with first element
(take (cdr lst) ; and result from take with the rest of the list
(- cnt 1))))) ; with one less element than you originally asked for
If memory serves, in addition to car and cdr, Scheme supplies caar, caaar, caaaar and the equivalent repeated ds and the various combinations and permutations. So one solution would be:
(define x (list 3 6 9 2 1 0 5 9))
(list (car x) (cadr x) (caddr x) (cadddr x) (car (cddddr x)))
(and I'm sure that's why you were asked for the first five; there's no cdddddr — the teaching point was likely the permitted repetition of ds and as and the limit to which you can go with those)
I do not know why you would want to do this, but one way to avoid recursion is to unroll the loop:
(define (take1 xs) (cons (car xs) '()))
(define (take2 xs)
(cons (car xs) (take1 (cdr xs))))
(define (take3 xs)
(cons (car xs) (take2 (cdr xs))))
(define (take4 xs)
(cons (car xs) (take3 (cdr xs))))
(define (take5 xs)
(cons (car xs) (take4 (cdr xs))))
what's the typical way to add an item to the end of the list?
I have a list (1 2 3) and want to add 4 to it (where 4 is the result of an evaluation (+ 2 2))
(setf nlist '(1 2 3))
(append nlist (+ 2 2))
This says that append expects a list, not a number. How would I accomplish this?
You could use append, but beware that it can lead to bad performance if used in a loop or on very long lists.
(append '(1 2 3) (list (+ 2 2)))
If performance is important, the usual idiom is building lists by prepending (using cons), then reverse (or nreverse).
If the "cons at the front, finish by reversing" idiom isn't suitable for you (if you. for example, need to pass the list on to other functions DURING its construction), there's also the "keep track of the end" trick. However, it's probably cleaner to just build the list by consing to the front of it, then finish by using reverse or nreverse before finally using it.
In essence, this allows you to have the list in the right order while building it, at the expense of needing to keep track of it.
(defun track-tail (count)
(let* ((list (cons 0 nil))
(tail list))
(loop for n from 1 below count
do (progn
(setf (cdr tail) (cons n nil))
(setf tail (cdr tail))
(format t "With n == ~d, the list is ~a~%" n list)))
list))
This gives the following output:
CL-USER> (track-tail 5)
With n == 1, the list is (0 1)
With n == 2, the list is (0 1 2)
With n == 3, the list is (0 1 2 3)
With n == 4, the list is (0 1 2 3 4)
(0 1 2 3 4)
You can also use nconc to create the list, which is like append, only it modifies the structure of the input lists.
(nconc nlist (list (+ 2 2)))
You haven't specified the kind of Lisp, so if you use Emacs Lisp and dash list manipulation library, it has a function -snoc that returns a new list with the element added to the end. The name is reversed "cons".
(-snoc '(1 2) 3) ; (1 2 3)
This function might be useful in some situations, it transparently appends a single element to a list, i.e. it modifies the list but returns the appended element (enclosed in a list):
(defun attach1 (lst x)
(setf (cdr (last lst)) (cons x nil)))
;; (attach1 nlist (+ 2 2)) ; append without wrapping element to be added in a list
(append l (list e)) ; e is the element that you want to add at the tail of a list
Cons-ing at the end of a list can be achieved with this function:
(defun cons-last (lst x)
(let ((y (copy-list lst))) (setf (cdr (last y)) (cons x nil)) y))
;; (cons-last nlist (+ 2 2))
If you are trying to add two lists for example (1 2 3) + (1 2 3) here is the code (recursive)
(defun add-to-all (x y)
(T (appendl (+ (first x) (first y)) (add-to-all (tail x) (tail y)) ))
)
If you are trying to add an item to the end of the second list, for example 3 + (1 2 3)
(defun add-to-all (x y)
(cond ((null? y) nil)
(T (appendl (+ (first x) (first y)) (add-to-all (tail x) (tail y)) ))
)
)
If you want to add an item onto the end of a given list without changing that list, then as previously suggested you can use a function like
(defun annex (lst item)
"Returns a new list with item added onto the end of the given list."
(nconc (copy-list lst) (list item)))
This returns a new extended list, while preserving the input list. However, if you want to modify the input list to include the added item, then you can use a macro like
(define-modify-macro pushend (item)
(lambda (place item)
(nconc place (list item)))
"Push item onto end of a list: (pushend place item).")
Pushend operates like push, but "pushes" the item onto the end of the given list. Also note the argument order is the reverse of push.