Stack Overflow on lisp changing 2 elements on a list - common-lisp

I have this function to swap two elements on a list:
(defun change-path (List)
(setf len (list-length List))
(setf x1 (random len))
(setf x2 (random len))
(setf e1 (get-n-element List x1))
(setf e2 (get-n-element List x2))
(changeElem (changeElem List x2 e1) x1 e2)
)
the function get-n-element is working fine, and the change-path works fine sometimes, but when I pass for exemple a list of list it always return program stack overflow, what can I do to fix this?
(defun changeElem (List pos novo)
(cond ((null List) nil)
((zerop (1- pos)) (cons novo (changeElem (rest List) (1- pos) novo)))
(t (cons (first List) (changeElem (rest Lista) (1- pos) novo)))
)
)
(defun get-n-element (Lista n)
(cond ((zerop (1- n)) (first Lista))
(t (get-n-element (rest Lista) (1- n)))
)
)

Related

Lisp: labels or separate global functions?

This is what my wason-deck produces:
((15 . D) (35 . H) (3 . B) (19 . K) (L . 15) (A . 16) (T . 23) (R . 53)
(N . 13) (M . 7) (I . 52) (35 . Q) (S . 19) (Y . 29) (45 . G) (44 . W)
(11 . V) (J . 25) (21 . F) (39 . Z) (25 . X) (50 . E) (5 . P) (33 . C)
(O . 34))
this being a list of pairs representing a Wason deck. (See this, Example 6). In the deck there should be all the letters of the alphabet matched with even or odd numbers depending on whether a vowel or consonant respectively. I randomly shuffle and flip the cards as you can see. Then I (optionally) randomly pollute the deck by occasionally breaking the vowel:even, consonant:odd rule. Here's the code I've come up with:
(defun wason-deck (&optional (p 0))
"This `consolst` and `vowlist` building is unnecessary, but a good exercise"
(let* ((alphab '(a b c d e f g h i j k l m n o p q r s t u v w x y z))
(consonents '(b c d f g h j k l m n p q r s t v w x y z))
(consolst (remove 'NIL (mapcar (lambda (x) (find x consonents)) alphab)))
(vowlst (remove 'NIL (mapcar (lambda (x) (find x '(a e i o))) alphab)))
(wdeck '()))
(labels ((make-consodeck ()
(mapcar (lambda (x) (let ((num (random 54)))
(cons x (if (evenp num)
(1+ num)
num)))) consolst))
(make-voweldeck ()
(mapcar (lambda (x) (let ((num (random 54)))
(cons x (if (oddp num)
(1+ num)
num)))) vowlst))
(swap (slst el1 el2)
(let ((tmp (elt slst el1)))
(setf (elt slst el1) (elt slst el2))
(setf (elt slst el2) tmp)))
(shuffle (slst)
(loop for i in (reverse (range (length slst) :min 1))
do (let ((j (random (+ i 1))))
(swap slst i j)))
slst)
(flip (flst)
(mapcar (lambda (x) (let ((num (random 2)))
(if (zerop num)
(cons (cdr x) (car x))
x))) flst)))
(setf wdeck (flip (shuffle (append (make-consodeck) (make-voweldeck)))))
(if (zerop p) wdeck
(mapcar (lambda (x) (let ((num (random 6)))
(cond ((and (zerop num) (numberp (car x))) (cons (1+ (car x)) (cdr x)))
((and (zerop num) (numberp (cdr x))) (cons (car x) (1+ (cdr x))))
(t x)))) wdeck)))))
It works, but what I fear is not really knowing what I'm doing, i.e., I've misused labels as well as done a setf in the code. If some of the more senior people could tell me whether this is totally off in the wrong direction or not.
Addendum:
This is what I've got after the suggestions from below:
(defun wason-deck3 (&optional (p 0))
(let* ((consonents '(b c d f g h j k l m n p q r s t v w x y z))
(vowels '(a e i o u))
(conso-deck (mapcar (lambda (x)
(cons x (1+ (* 2 (random 27)))))
consonents))
(vowel-deck (mapcar (lambda (x)
(cons x (* 2 (random 27))))
vowels))
(wdeck '()))
(labels
((shuffle (slst)
(loop :for i :from (1- (length slst)) :downto 1
:do (rotatef (nth i slst)
(nth (random (1+ i)) slst)))
slst)
(flip (flst)
(mapcar (lambda (x) (let ((num (random 2)))
(if (zerop num)
(cons (cdr x) (car x))
x))) flst)))
(setf wdeck (flip (shuffle (append conso-deck vowel-deck)))))
(if (zerop p) wdeck
(mapcar (lambda (x) (let ((num (random 6)))
(cond ((and (zerop num) (numberp (car x))) (cons (1+ (car x)) (cdr x)))
((and (zerop num) (numberp (cdr x))) (cons (car x) (1+ (cdr x))))
(t x)))) wdeck))))
Please add any new suggestions.
Using labels is totally OK, and your code is not entirely unreasonable.
A few pointers:
I'd represent characters as characters: '(#\a #\b #\c …)
I'd take my list exercises elsewhere, or at least use set-difference.
When you create a function for just one call, you might as well just save the result:
(let ((consonant-deck (mapcar (lambda (c)
(cons c (1+ (* 2 (random 27)))))
consonants))
(vowel-deck (mapcar (lambda (c)
(cons c (* 2 (random 27))))
vowels)))
…)
For swapping, there is rotatef: (rotatef (nth i list) (nth j list)). Such things are rather expensive on lists, so I'd prefer to use a vector for this. Then it comes in handy that a string is just a vector of characters…
Loop can do counting for you, you don't need to create lists:
(loop :for i :from (1- (length list)) :downto 1
:do (rotatef (nth i list)
(nth (random (1+ i)) list)))
(Using keywords as loop keywords is optional, but indentation should be like this.)
If you put the labels around the let, you can immediately bind wdeck, so that you do not need to setf it afterwards.
You do not need this function for the exercise that you linked to.

Multi-level recursion in lisp

I have a problem with multilevel recursion. I have two numbers n1 and n2. When counter is between n1 and n2 i want thoose numbers to be in the result, keeping the list internal structure.
(defun izdvoj (lista n1 n2 counter)
(cond ((null lista) counter)
((and (atom (car lista))
(< counter n1)
(izdvoj (cdr lista) n1 n2 (+ counter 1))))
((and (atom (car lista))
(> counter n2)
(izdvoj (cdr lista) n1 n2 (+ counter 1))))
((atom (car lista))
(cons (car lista) (izdvoj (cdr lista) n1 n2 (+ counter 1))))
(t
(cons (izdvoj (car lista) n1 n2 counter) (izdvoj (cdr lista) n1 n2 counter)))))
(izdvoj '(1 2 (3 (4) 5 (6 (7)))(8 (9 (10 ((11))) 12)) (13 ((14) (15)))) 7 13 0)
The result should be ((((7))) (8 (9 (10 ((11))) 12)) (13))
And I get (((4) ((7))) (((((11))) 12)) (((14) (15))))
Any suggestions please?
The easiest way to do this is by returning the index so far as well as the result in the helper. That way you know how to continue when you have done car and are ready to do the cdr:
(defun get-range (list from-index to-index)
(labels ((aux (list cur acc)
(cond ((null list)
(values (nreverse acc) cur))
((not (atom (car list)))
(multiple-value-bind (res cur)
(aux (car list) cur '())
(aux (cdr list) cur (cons res acc))))
((<= from-index cur to-index)
(aux (cdr list) (1+ cur) (cons (car list) acc)))
(t (aux (cdr list) (1+ cur) acc)))))
(nth-value 0 (aux list 1 '()))))
(get-range '(a b (c d (e (f))) (g h)) 3 5)
; ==> ((c d (e ())) ())

recursively count sublists beginning with a number

I am trying to write code in Lisp counting sublists beginning with number, recursively. I ve trying to use numberp but my code, when arrived to an atom, doesn't count the rest of the list.
With my code here,
(defun nombres (liste)
(cond
((atom liste) 0)((atom (car liste)) 0)
((and (numberp (caar liste)) (+ (nombres (cdr liste)) 1)))
(t (nombres (cdr liste))) ) )
I can get a count of sublists but when arrived to an atom , it doesn't count the rest.
[67]> (nombres '((a b d) (5 g) (7 m)))
2
[68]> (nombres '((a b d) (5 g) g (7 m)))
1
When I test the sublist with (listp (car list), it gives me nil.
[69]> (defun nombres (liste)
(cond
((atom liste) 0)((atom (car liste)) 0)
((listp (car liste))(and (numberp (caar liste)) (+ (nombres (cdr liste)) 1))) (t (nombres (cdr liste))) ) )
NOMBRES
[70]> (nombres '((a b d) (5 g) g (7 m) m))
NIL
I want to get something like :
(nombres '((a b d) a (5 g) (b) (7 m) j (8 h l g)))
3
Thanks for your help
You need to think about the cases you need to handle.
The end of the list => return the result
A sublist that has a number in front => add one to the result
Anything else => continue to the next element
These will quite easily translate to a COND:
(cond ((endp list) ...) ; 1
((and (listp (car list)) ; 2
(numberp (caar list)))
...)
(t ...) ; 3
Using an accumulator as an optional parameter, the counting is easy to fill in:
(defun count-sublists (list &optional (acc 0))
(cond ((endp list) acc)
((and (listp (car list))
(numberp (caar list)))
(count-sublists (cdr list) (1+ acc)))
(t (count-sublists (cdr list) acc))))
(count-sublists '((a b d) a (5 g) (b) (7 m) j (8 h l g)))
;=> 3
The standard Common Lisp function count-if is easier to use:
CL-USER > (count-if (lambda (item)
(and (consp item)
(numberp (first item))))
'((a b d) a (5 g) (b) (7 m) j (8 h l g)))
3

Lisp recursion returns NIL

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))))))

homework on scheme

how to design a function content which
inputs a single list of atoms lat and which returns
the content of lat.Thus the content of '(a b c a b c d d) is '(a b c d).
The procedure content below should get you what you need.
(define (work x y)
(if (null? (cdr x))
(if (in? (car x) y)
y
(cons (car x) y))
(if (in? (car x) y)
(work (cdr x) y)
(work (cdr x) (cons (car x) y)))))
(define (in? x y)
(if (null? y)
#f
(if (equal? x (car y))
#t
(in? x (cdr y)))))
(define (content x) (work x (list)))
The procedure content accepts a list as a parameter. It sends the list to another procedure called work. This procedure processes the list and adds the items in the list to a new list (if they are not already in the new list). The work procedure makes use of yet another procedure called in, which checks to see if an item is a member of a list.
My solution essentially divides your problem into two sub-problems and makes use of procedures which operate at a lower level of abstraction than your original problem.
Hope that helps.
It is PLT Scheme solution:
(define (is_exists list element)
(cond
[(empty? list) false]
[else
(cond
[(= (first list) element) true]
[else (is_exists (rest list) element)])]))
(define (unique list target)
(cond
[(empty? list) target]
[else
(cond
[(is_exists target (first list)) (unique (rest list) target)]
[else (unique (rest list) (cons (first list) target))])]))
(define (create_unique list)
(unique list empty))
Check it:
> (define my_list (cons '1 (cons '2 (cons '3 (cons '2 (cons '1 empty))))))
> my_list
(list 1 2 3 2 1)
> (create_unique my_list)
(list 3 2 1)
How about little schemer style,
(define (rember-all a lat)
(cond
((null? lat) '())
((eq? a (car lat)) (rember-all a (cdr lat)))
(else (cons (car lat) (rember-all a (cdr lat))))))
(define (content lat)
(cond
((null? lat) '())
(else (cons (car lat)
(content (rember-all (car lat) (cdr lat)))))))
Start from a procedure that simply creates a copy of the passed-in list (very easy to do):
(define (unique-elements seq)
(define (loop ans rest)
(cond ((null? rest) ans)
(else
(loop (cons (car rest) ans)
(cdr rest)))))
(loop '() seq))
To ensure that the output list's elements are unique, we should skip the CONS if the head of REST is already a member of ANS. So we add another condition to do just that:
;;; Create list containing elements of SEQ, discarding duplicates.
(define (unique-elements seq)
(define (loop ans rest)
(cond ((null? rest) ans)
((member (car rest) ans) ; *new*
(loop ans (cdr rest))) ; *new*
(else
(loop (cons (car rest) ans)
(cdr rest)))))
(loop '() seq))
The following function takes in a list and returns a new list with only the unique inputs of it's argument using recursion:
(defun uniq (list)
(labels ((next (lst new)
(if (null lst)
new
(if (member (car lst) new)
(next (cdr lst) new)
(next (cdr lst) (cons (car lst) new))))))
(next list ())))
As was mentioned in the comments, common lisp already has this function:
(defun uniq (list)
(remove-duplicates list))
(define (remove-duplicates aloc)
(cond
((empty? aloc) '())
(else (cons (first aloc)
(remove-duplicates
(filter (lambda (x)
(cond
((eq? x (first aloc)) #f)
(else #t)))
(rest aloc)))))))

Resources