Instance variables in Lisp? - lisp

I'm writing a function for a CLOS class that reverses the list element of an object of said class.
I have a method that will return the reverse list, but how do I make it set the object's list to that list? Can I have a instance variable in the function that stores the list then set the element to that? Or is there an easier way?
Here's the method as it is now:
(defun my-reverse (lst)
(cond ((null lst) ‘())
(t (append (my-reverse (cdr lst)) (car lst)))))
The object it is passed is (l my-list) and the accessor would then be (my-list-ls l).
Edit: Realized that cons doesn't work on 2 lists.
Edit2: What I assume the right code would be:
(defun my-reverse (l my-list)
(cond ((null (my-list-ls l) ‘())
(t (setf (my-list-ls l) (append (my-reverse (cdr (my-list-ls l)))
(car (my-list-ls l)))))))

If you want to modify an object's slot, you need to pass that object itself to your function, not just the value of the slot you want to change.
EDIT: regarding the edit2 of the question
I assume my-list is the name of the class, and you don't actually want to pass it to the function, right? In that case, you should substitute the defun with a defmethod. Also, it should be better to change the instance only once, after you reversed the whole list, instead of at each step. You could use an inner function for that:
(defmethod my-reverse ((l my-list))
(labels ((inner (list acc)
(if (endp list)
acc
(inner (rest list) (cons (first list) acc)))))
(setf (my-list-ls l) (inner (my-list-ls l) ()))))
EDIT 2: detailed explanation
defmethod is the alternative to defun for defining (polymorphic) methods. Though, if you don't need polymorphism, you could just use (defun my-reverse (l) for the first line.
labels is for inner function definitions. Here, it defines an inner function named inner with the two parameters list and acc. inner is the function that does the actual reversing, and it's a tail-recursive function because reversing goes naturally with tail-recursion. (It can build its result with cons and is therefore of linear complexity, whereas your solution needs append and thereby is of quadratic complexity, because cons itself is constant, but append is linear.)
first and rest are just alternate names for car and cdr, endp is mostly just an alternate name for null, with the difference that endp will signal an error if its argument isn't actually a list.
Finally, the last line calls inner with the original and an empty list as arguments, and assigns the result to the slot (aka instance variable).

Related

lisp last element functional form

Hey guys I need help with lisp function. I am supposed to create:
(myLast L)
Evaluates to the last element of list L.
eg. (myLast ‘(p a e g)) → g
I cant use all of the predefined forms for lisp only the ones we have been given in class:
(atom X)
(quote X)
‘X
(eq X Y)
(cons X L)
(car L)
(cdr L)
(list A B C)
(if X Y Z)
(cond (C1 S1) (C2 S2) …… (Cn Sn))
I thought I had it right when I put in:
(defun myLast (L)
(if ((eq L '()) (cdr L))
(car L)
(myLast (cdr L))))
However I am getting an error:
Error: The variable MYHW4.LISP is unbound.
Error signalled by EVAL
Backtrace: EVAL
Broken at SYSTEM::GCL-TOP-LEVEL.
I am completely new to LISP and trying to complete this assignment. I was hoping you guys could help me out and let me know why I am getting this error, and is my logic for the last functional form correct? Thanks!
There are multiple problems with your code.
You have excess parentheses. ((eq L '()) is not allowed as the only expression allowed at operator position is an anonymous function.
Your if only have a consequence expression but not a alternative. It isn't the last expression so it's dead code.
The you do car, also not in real position so far code also.
Tail expression is the recursion and is done unconditionally. It's called infinite recursion.
I think perhaps you meant something like this:
(defun myLast (list)
(if (null (cdr list))
(car list)
(myLast (cdr list))))
The error message is unrelated to your code. You probably typed (load myhw4.lisp) without quotes in which case your Lisp rightly think that you wanted to take the value bound to the variable myhw4.lisp, which does not exist. You need to quote strings "like so".
Also, ((eq L '()) ...) is problematic, since the first form is (eq ...) which is not a function or a lambda. That will signal an error..
The above makes your code wrong, but you are not far from it.

Lisp - Elements of a lisp occur in other list

i'm having a problem with this lisp function. I want to create a function that receives two lists, and verifies if the elements of the first list (all of them) occur in the second list, it returns True if this happens.
Currently i have the following code:
(defun ocorre-listas (l1 l2)
(dolist (elem1 l1)
(dolist (elem2 l2)
(if (equal elem1 elem2)
t))))
It's not working, as expected. Should i try to do it just with a simple recursion? I'm really not getting how i can iterate both lists in search of equal elements.
I decided to try without the dolists. This is what i have now, it's still not working.
(defun ocorre-listas (l1 l2)
(cond ((null l1) nil)
((null l2) nil)
((if (/= (first l1)(first l2)) (ocorre-listas l1 (rest l2))))
(t (if (= (first l1) (first l2)) (ocorre-listas (rest l1)(rest l2))))))
I get a warning saying that "t" is an undefined function. Also, every example i try returns null. What am i doing wrong ?
In the second piece of code, if the first list is empty then all of its elements are in the second one.
You don't need the ifs since you are inside a cond
After test if the lists are empty, you'll only need to test if the first element of the first list is in the second one and call the function again with the first list without this element
Instead of trying to do everything in one function, consider splitting it into two (or more) functions, e.g.
One that takes a number and the second list, and tests whether the number appears in the list
Another that iterates over the numbers in the first list, and for each one tests (using the first function) whether it appears in the second list.
As well as DOLIST, consider using MAPCAR and FIND-IF (assuming they are allowed in this assignment.)
So you need to check if every element of l1 is a member of l2. These are both functions in the Common Lisp standard library, so if you're allowed to use them, you can build a simple solution with them.
See the common lisp subsetp
predicate and its implementation:
CL-USER> (subsetp '(1 2 3) '(1 2 3 4)
T
To be able to work on both lists at the same time, the trick is probably to sort the lists before starting the recursion. Then it should be a simple matter of comparing the first element, and applying the same function to the rest of the list recursively, with some CAR/CDR magic added of course...
While there are many ways to do this, I would recommend using a hash table to avoid O(n^2) complexity. Using a hash table, you can achieve O(n) complexity.
here is a union function
(defun my-union (a b)
(let ((h (make-hash-table :test #'equal)))
(mapcar (lambda (x) (setf (gethash x h) x)) a)
(mapcan (lambda (x) (when (gethash x h) (list x))) b)))
here is a function testing for IDENTICAL elements in boths lists
(defun same-elements (a b)
(apply #'= (mapcar #'length (list (my-union a b) a b))))
here is a function making sure a is a subset of b (what you asked)
(defun subset (a b)
(same-elements (my-union a b) a))

Finding duplicate atoms in possibly nested lists in LISP

I am trying to figure out how to find duplicate atom in possibly nested lists. I have been trying to figure this out all day. If you could please give me the logic, that would be great because I really want to learn.
basically
(findDup '(a b b)) would return t
(findDup '(a c ((d (f a)) s))) would also return t
The easiest and most efficient way would be the following (pseudocode):
Create a data structure (such as Common Lisp's hash table) to remembering which atoms were seen
Create a recursive sub-function that does the actual traversing - walking the nested lists and adding all new atoms to the data structure, and if one is already there, returning true
If the list is sorted or can be sorted, this is a simple solution:
(defun find-duplicates (lst)
(let ((dup-list nil))
(sort lst)
(mapcon (lambda (l) (when (eq (car l) (cadr l)) (push (car l) dup-list))) lst)
dup-list ))
This should take care of the first case:
(defun find-duplicates (lst)
(let ((h (make-hash-table))
(dupes))
(mapcar #'(lambda (x)
(if (gethash x h)
(push x dupes)
(setf (gethash x h) t)))
lst)
dupes))
If the list is empty/without an atomic car however deeply you go (e.g. (car (car (car ...))) recursively), then the answer is false.
You want to find the first atom of the list, and see if that atom occurs anywhere else in the list. You can do that with a function like member-of?—something similar is discussed in The Little Schemer, but basically you just test all the atoms in the list, and recur on lists, against that atom.
Then if that atom is in the list, you can return true.
Else, you'll try again (recur on) with the cdr of the list.
I'd start with a wrapper function that creates a hash table and passes the hash table and the list to a second function (alternatively, use a &optional argument, if you're using Common Lisp).
Then, the following pseudo-code should be enough:
If we're looking at an empty list, there are no duplicates
If the head is a list, we can return the logical OR of "inspect the head" and "inspect the tail"
If the head is an atom, it's a duplicate if it's already in the hash table. If not, add it to the hash table and inspect the tail for duplicates.

Homework: Lisp items that appear more than once in a list

Given a list, I'm trying to return a new one that has only the items that appear more than once in the first list I receive as a parameter.
I have done the following:
(defun myf (lista)
(if (endp lista)
nil
(if (member (first lista) (rest lista))
(append (list (first lista)) (myf (rest lista)))
(myf (rest lista)))))
If I run the following: (myf '(A A B A B C)), it returns (A A B).
How can I make it return items only once (i.e., not have double "A")?
Once you've found that a letter is in the list more than once, you don't need to check it again, so you don't need it in the rest of the list. So you could modify the remaining list...
Note: this answer intentionally somewhat vague, since it's for homework and all. :)
The problem seems to be that you are checking to see if the first element of the list is in the tail of the list and appending it to the result list. The problem comes up when you come to the second instance of A, which when checked says yes it is in the tail because there is a 3rd instance of A in the list. If the input list had 4 A's then it would return 3 of them.
So to solve this need to check if A is already a member of the result list. That is, if first lista is not a member of list then append first lista to list.
Vincent, Gishu and Jerry hinted that you need to check if the item is already in the result list before appending to it, while Derek hinted that you could modify the original list when you see that an item is repeated.
Read the documentation of the functions adjoin and remove here:
http://www.lispworks.com/documentation/HyperSpec/Body/f_adjoin.htm
http://www.lispworks.com/documentation/HyperSpec/Body/f_rm_rm.htm
The HyperSpec is a very useful reference, add it your bookmarks.
Those two functions do not modify their arguments, but instead return the result as a new list. There are other functions that DO modify their arguments and thus might be more efficient, but at this point perhaps you shouldn't worry about them.
OK, I hope that by the time I have written this you have figured it out. If not, keep trying, that's the only way you'll really learn.
Now I'd like to talk to you about another approach, and that is about carrying the result through the recursive calls.
Our function repeated will do nothing but call a helper function repeatedr which will do the actual work, passing to it an initial empty result '():
(defun repeated (lst)
(repeatedr lst '()))
Now let's define the helper function, it receives two parameters: the list to search for duplicates, and the result list where we will accumulate the duplicate items.
(defun repeatedr (lst result)
(if (null lst)
result
(if (member (first lst) (rest lst))
(repeatedr (rest lst) (adjoin (first lst) result))
(repeatedr (rest lst) result))))
When the condition (member (first lst) (rest lst)) holds true we will adjoin the first item to the result, and the result of that adjoining will be passed to the recursive call as the second parameter; otherwise we just pass the result as is to the recursive call.
When the list is finally empty (if (null lst) we will return the result.
> (repeated '(a a b a b c))
(B A)
Notice that the result is (B A) and maybe you were expecting it to be (A B). Try to follow the execution of the recursive calls and the values of the parameters at each call with pen and paper, that will be a good exercise, and you'll have to play with adjoin to understand its behaviour. If you want to get the result reversed you can modify the function like this:
(defun repeatedr (lst result)
(if (null lst)
(reverse result)
(if (member (first lst) (rest lst))
(repeatedr (rest lst) (adjoin (first lst) result))
(repeatedr (rest lst) result))))
This reverses the result when the recursion finishes.
Now, what about the suggestion of removing the duplicate elements from the list before going forward? We could have written our function like this:
(defun repeatedr (lst result)
(if (null lst)
result
(if (member (first lst) (rest lst))
(repeatedr (remove (first lst) lst) (cons (first lst) result))
(repeatedr (rest lst) result))))
Try to play with remove at the REPL:
> (remove 'a '(a b c d a e f b a d))
(B C D E F B D)
Notice that we are no longer adjoining to the result, instead we just create a new "cons cell" with (cons (first lst) result). cons and adjoin do the same thing, except that adjoin will add the value only if it is not already present in the list.
Hope this gives you something to play with.
The problem seems to be that you're not checking if the element exists in the output list before appending to it.
Right now, for each element in the list, you're adding that element to the result if its not contained in the remainder of the list.
To get the result you apparently want, you'd add each element in the list to the result if it's not yet contained in the result.
If order is not a concern then you can also do:
(defun my-fun-no-order (lst)
(loop for (item . rest) on lst
when (= (count item rest) 0)
collect item))
Here we iterate over a list spitting it into the current item being iterated over and the rest of the items yet to be examined. The when clause determines the situation under which we collect the item.

How can I tell if a list has a third item?

I have a function that takes a list that either has two or three elements.
;; expecting either ((a b c) d) or ((a b c) d e)
(define (has-third-item ls)
(if (null? (caddr ls))
false
true)
)
But this code fails with
mcar: expects argument of type <mutable-pair>; given ()
on the (null? (caddr ls)) expression.
I also tried
(eq? '() (caddr ls))
but it didn't work either. How do I tell if there's a third item or not?
You don't want caddr, you want (if (null? (cddr ls)) ... Or just use length to find the length of the list, and compare it to the value you're interested in.
The '() that terminates a list will always be in the cdr position of a pair, so looking for it in the car position (which cad+r will do) isn't going to be productive.
The problem is that if you have a list with two or fewer items, you can't take the caddr of it. Try this:
(define (has-third-item lst)
(<= 3 (length lst)))
There could be some cases where taking the length of the list can be inefficient (such as the list containing millions of items); in this case, we can test to see if the list has length zero, one, or two by hand:
(define (has-third-item lst)
(not (or (null? lst)
(null? (cdr lst))
(null? (cddr lst)))))
edit: Regarding the two other answers, while taking the cddr may work in this case as the input domain consists of list with either two or three elements, has-third-item would still fail for lists with zero or one. In the interest of generality, I suggest going with a solution that works for any domain.
Provided you know your list has either two or three elements (as you say it has), you can do
(define (has-third-item? l)
(not (null? (cddr l))))
You're checking whether the second cons cell (cddr l) has a cdr or not. You don't have to check whether l itself is null or l has only one element, unless you want a more generic function.
try...
(and l (cdr l)(cddr l))
Why not use (third ls)
Will return the third element or NIL if none is present.