Say, that I have a pre-defined function 'sum' elsewhere.
I want to sum two lists after I do (setq a '(4 3 4)) and (setq b '(6 10 9))
And I do (recurse a b).
However, I keep getting 'nil' as the return value. What am I doing wrong with this recursion?
Trace isn't being helpful at the moment.
(defun recurse (x y)
(cond
( (null x) nil) )
(t (sum (car x) (car y) ) (recurse (cdr x) (cdr y)) )
)
)
You need to cons up the results; otherwise they are thrown out.
(defun recurse (x y)
(cond
((null x) nil)
(t (cons (sum (car x) (car y))
(recurse (cdr x) (cdr y))))))
Related
The task is: for given list of elements and X element, remove an element after X if it is not equal to X. Example: (a 8 2 a a 5 a) X=a, expecting (a 2 a a a).
I have code that removes an element before X, so it gives me (a 8 a a a) instead. How do I fix it?
(defun purgatory (n w)
(cond ((null w) nil)
((and (eq (cadr w) n) (not (eq (car w) (cadr w)))) (purgatory n (cdr w)))
((cons (car w) (purgatory n (cdr w))))))
You can use the destructuring of for on clauses in loop:
(defun purgatory (list x)
(cons (first list)
(loop :for (a b) :on list
:unless (and (eql a x)
(not (eql b x)))
:collect b)))
I think you are on the right lines with a recursive algorithm. I think that the algorithm works better as a tail-optimised recursion. You take an in-list and an X, and build up an out-list. The output is reversed, and so reverse needs to be applied at the end, thus:
(defparameter my-list '(a 8 2 a a 5 a))
(defun remove-after (in-list X &optional (out-list '()) (last '()))
(if (null in-list)
(reverse out-list)
(if (and (eql last X) (not (eql (car in-list) X)))
(remove-after (cdr in-list) X out-list (car in-list))
(remove-after (cdr in-list) X (cons (car in-list) out-list) (car in-list))
)))
; (A 2 A A A)
As for the non-tail algorithm, I think this does it:
(defun purgatory (n w)
(cond ((null w) nil)
((and (eq (car w) n) (not (eq n (cadr w)))) (cons (car w) (purgatory n (cddr w))))
(t (cons (car w) (purgatory n (cdr w))))
))
; (A 2 A A A)
So, if the first element is n and the next is not n, then add n at the front of the algorithm, but skip cddr the next element. Otherwise, add the first element to the front of the algorithm, no skip cdr.
NB: since you've defined the problem in terms of X, I think this should be one of your parameters, not n
I have that Lisp function:
(defun F(l)
(cond
((atom l) -1)
((>(F(car l))0)(+(car l)(F(car l))(F(cdr l))))
(t(F(cdr l)))
)
)
and I want to prevent double invocation of recursive function (F (car l)) in the second line of cond using a lambda function.
I tried that:
(defun F(l)
((LAMBDA (ff)
(cond
((atom l) -1)
((> ff 0)(+(car l) ff (F(cdr l))))
(t(F(cdr l)))
)
) (F (car l)))
)
but I get error :
CAR: 1 is not a list
at call (F '(1 2 3 4)).
Also I'm not sure if that correctly avoids double recursive call.
The reason you are getting this error is that you are calling (car l) always, even when l is, in fact, not a list.
To avoid this, only do that when needed:
(defun F(l)
(if (atom l) -1
(let* ((l1 (car l))
(f1 (F l1))
(f2 (F (cdr l))))
(if (plusp f1)
(+ l1 f1 f2)
f2))))
I'm working my way through Paul Graham's "ANSI Common Lisp" (1996).
Chapter 3, exercises, qu. 2 asks for a function as stated in title of this post. I'm only using what has been taught in the book up to this point (obviously there's case construct that could clean up the if's but I'm not minding that at present).
As a first attempt I ended up writing interleave, which retains duplicates:
(defun interleave (x y)
(if (and (null x)
(null y))
nil
(if (null x)
(cons (car y)
(interleave (cdr y) x))
; where y is null, but also for any other case:
(cons (car x)
(interleave y (cdr x))))))
Following that, I had the idea to store a carry of elements which have been seen, and defer to a helper function, as below.
However, the below is obviously rather ugly and hard to understand.
I'm seeking some suggestions on directions I might take to achieve elegance.
Tips on approach & style might be just as useful at this point as providing the canonical solution. Should my number one impulse given code below be to extract another function? (or maybe I've gone in the wrong direction trying to store the carry in the first place?) Thank you fellow hackers!
(defun new-union (x y)
(new-union-helper x y '())) ; <- idea, add a carry to store what's been seen.
(defun new-union-helper (x y seen)
(if (and (null x)
(null y))
nil
(if (null x)
(if (not (member (car y) seen)) ; if first el of y hasn't yet been seen...
; cons it to the ultimate result & recur, while adding it to seen:
(cons (car y) (new-union-helper (cdr y) x (cons (car y) seen)))
; if it has been seen, just continue, (skip the duplicate):
(new-union-helper (cdr y) x seen))
(if (not (member (car x) seen))
(cons (car x) (new-union-helper y (cdr x) (cons (car x) seen)))
(new-union-helper (cdr x) y seen)))))
Update: I've attempted to replace the nested ifs with cond, having looked up cond in the index of the book. Sorry in advance, this is so ugly... but if anyone can tell me what I'm doing wrong here that would be greatly appreciated. This code works same as above, but it prints a nil as the last member of the resulting list (on some inputs), not sure why yet.
; attempt to use cond instead:
(defun new-union-helper (x y seen)
(cond ((and (null x) (null y))
nil)
((and (null x) (not (member (car y) seen)))
(cons (car y) (new-union-helper (cdr y) x (cons (car y) seen))))
((null x)
(new-union-helper (cdr y) x seen))
((not (member (car x) seen))
(cons (car x) (new-union-helper y (cdr x) (cons (car x) seen))))
(t
(new-union-helper (cdr x) y seen))))
Update 2: I've tried to adopt better indenting. The below does what I want it to do from informal tests. Any further tips on what I'm still doing wrong? (I realise I should maybe abandon this and pursue another path, but since this is a learning exercise I wanted to fix as many potential bad habits as possible, early, before continuing on a new path).
How does this rate on the ugliness stakes? :) Is it now readable to an experienced lisper?
; better (standard?) formatting
(defun new-union-helper (x y seen)
(cond ((and (null x)
(null y))
nil)
((and (null x)
(member (car y) seen)) ; replacing find with member stops duplicate nils
(new-union-helper (cdr y) x seen))
((null x)
(cons (car y)
(new-union-helper (cdr y) x
(cons (car y) seen))))
((member (car x) seen)
(new-union-helper (cdr x) y seen))
(t
(cons (car x)
(new-union-helper y (cdr x)
(cons (car x) seen))))))
(defun new-union (list1 list2 &aux (list3 (reverse list1)))
(loop for e in list2 do (pushnew e list3))
(reverse list3))
(defun new-union (list1 list2 &aux (list3 (reverse list1)))
(dolist (e list2 (reverse list3))
(pushnew e list3)))
Union takes two lists as arguments and will return a new list with the duplicates removed as you know. You want to retain the order of the original lists it appears. The specific question from the book if I recall is that if you have the lists:
(new-union '(a b c) '(b a d))
It should return:
(A B C D)
in order to maintain the proper order. So i'd imagine you need a function that takes two lists obviously, and something such as an accumulator so that you do not destructure the original lists. Union is a "non-destructuring" function. Since we are working with lists, you can use the dolist macro so that we can loop through both lists. That would lead us to the conclusion that the function below may work, as it will maintain the original structure of both lists, maintain the order of both lists, and remove duplicates:
(defun new-union(lst1 lst2)
(let((accum nil))
(dolist(x lst1)
(push x accum))
(dolist(y lst2)
(if(not(find y accum))
(push y accum)))
(nreverse accum))
We can push each element from the first list to our accumulator, and then we can iterate through the second list and ONLY push it to the list if it is not an element that has already been pushed to the accumulator. This way, we avoid duplicates, maintain the structure of both of the original lists, and maintain the proper order if we return the our accumulator with the reverse function. Let's test it in the REPL:
CL-USER> (new-union '(a b c) '(b a d))
(A B C D)
Here is a recursive implementation. It can be made faster with a few hacks. For example, a hash-table may be used to save elements that have been seen. In that case, find will be replaced with a hash-table lookup which is constant time.
(defun new-union (lst1 lst2)
"return xs U ys preserving order in originals"
(labels ((rec (xs ys acc)
(let ((x (car xs))
(xx (cdr xs))
(y (car ys))
(yy (cdr ys)))
(cond ((and (null xs) (null ys))
acc)
((null xs)
(or (and (find y acc) (rec xx yy acc))
(rec xx yy (cons y acc))))
((null ys)
(or (and (find x acc) (rec xx yy acc))
(rec xx yy (cons x acc))))
((and (find x acc) (find y acc))
(rec xx yy acc))
((and (find x acc) (not (find y acc)))
(rec xx yy (cons y acc)))
((and (not (find x acc)) (find y acc))
(rec xx yy (cons x acc)))
(t (rec xx yy (cons y (cons x acc))))))))
(nreverse (rec lst1 lst2 nil))))
I'm finishing up a Scheme assignment and I'm having some trouble with the recursive cases for two functions.
The first function is a running-sums function which takes in a list and returns a list of the running sums i.e (summer '(1 2 3)) ---> (1 3 6) Now I believe I'm very close but can't quite figure out how to fix my case. Currently I have
(define (summer L)
(cond ((null? L) '())
((null? (cdr L)) '())
(else (cons (car L) (+ (car L) (cadr L))))))
I know I need to recursively call summer, but I'm confused on how to put the recursive call in there.
Secondly, I'm writing a function which counts the occurrences of an element in a list. This function works fine through using a helper function but it creates duplicate pairs.
(define (counts L)
(cond ((null? L) '())
(else (cons (cons (car L) (countEle L (car L))) (counts (cdr L))))))
(define (countEle L x)
(if (null? L) 0
(if (eq? x (car L)) (+ 1 (countEle (cdr L) x)) (countEle (cdr L) x))))
The expected output is:
(counts '(a b c c b b)) --> '((a 1) (b 3) ( c 2))
But it's currently returning '((a . 1) (b . 3) (c . 2) (c . 1) (b . 2) (b . 1)). So it's close; I'm just not sure how to handle checking if I've already counted the element.
Any help is appreciated, thank you!
To have a running sum, you need in some way to keep track of the last sum. So some procedure should have two arguments: the rest of the list to sum (which may be the whole list) and the sum so far.
(define (running-sum L)
(define (rs l s)
...)
(rs L 0))
For the second procedure you want to do something like
(define (count-elems L)
(define (remove-elem e L) ...)
(define (count-single e L) ...)
(if (null? L)
'()
(let ((this-element (car L)))
(cons (list this-element (count-single this-element L))
(count-elems (remove-elem this-element (cdr L)))))))
Be sure to remove the elements you've counted before continuing! I think you can fill in the rest.
To your first problem:
The mistake in your procedure is, that there is no recursive call of "summer". Have a look at the last line.
(else (cons (car L) (+ (car L) (cadr L))))))
Here is the complete solution:
(define (summer LL)
(define (loop sum LL)
(if (null? LL)
'()
(cons (+ sum (car LL)) (loop (+ sum (car ll)) (cdr LL)))))
(loop 0 LL))
I'm having trouble developing a recursive function that will see if two list's are equal to each other, including looking at the sub list's. So far I have:
(defun are-equal2 (X Y)
(cond
((null X) nil)
((and (listp (first X)) (listp (first Y)))
(are-equal2 (first X) (first Y))
)
((eq (first X) (first Y))
T
)
)
)
It seems to work sometimes. for example (are-equal2 '((A) B) '((A) B)) returns T and (are-equal2 '((A) B) '(A B)) returns nil. but (are-equal2 '(F (A G) B) '(F (T G) B)) returns T..... I think it might have to do with my last conditional. I'm not sure how to re-work it though.
Never mind lol. Did some tinkering waiting for a reply and got it. Did a bunch of nested if statements. Code:
(defun are-equal2 (X Y)
(if (and (listp (first X)) (listp (first Y)))
(are-equal2 (first X) (first Y))
(if (and (eq (first X) (first Y)))
(if (and (endp (rest X)) (endp (rest Y)))
T
(are-equal2 (rest X) (rest Y))
)
nil
)
)
)
I don't think you can get away with a tail-recursive version here.
I am afraid you will have to think of your arguments as trees, not sequences.
E.g.,
(defun are-equal (x y &key (test #'eql))
(or (funcall test x y)
(and (consp x)
(consp y)
(are-equal (car x) (car y))
(are-equal (cdr x) (cdr y)))))
This compares leaves using eql by default (cf. Rules about Test Functions), as opposed to eq in your example:
(are-equal '((1) a) '((1) a))
==> T
(are-equal '((1) a) '((1) b))
==> NIL
(are-equal '((1) a) '((2) a))
==> NIL
(are-equal '(("1") a) '(("1") a))
==> NIL