I'm trying to write a function that takes a list and returns true if it contains a duplicate entry and false otherwise. I know I'm supposed to use member. Here is my attempt so far (which fails):
(defun dupl (lst)
(if (null lst) '())
(if ((member (car lst) (cdr lst)) (cons (car lst) (dupes (cdr lst))))
(t (dupl (cdr lst)))))
You have a few problems in your code.
The first if should use return-from to actually return the value. It's also better to use nil instead of '().
In the second if you are trying to use cond syntax.
I'm not even sure what you were trying to achieve with the cons, but that doesn't seem necessary.
With these fixed, your code would look like this:
(defun dupl (lst)
(if (null lst) (return-from dupl nil))
(if (member (car lst) (cdr lst))
t
(dupl (cdr lst))))
It might be cleaner to turn the two ifs into a single cond:
(defun dupl (lst)
(cond ((null lst) nil)
((member (car lst) (cdr lst)) t)
(t (dupl (cdr lst)))))
If a function returns a boolean, it is likely to be expressible as a boolean expression. The following quadratic version is a possible implementation:
(defun duplicatesp (list &key (test #'eql))
(and list
(or (member (first list) (rest list) :test test)
(duplicatesp (rest list) :test test))))
The lazy-programmer version that follows also does the trick:
(defun duplicatesp (list)
(not (equal (remove-duplicates list) list)))
You could also sort a copy of your list first for a better time complexity of O(n.log(n)).
Just my two cents on efficiency. If you use memberp to test for duplicates, then you're comparing each element to each other element and the complexity is O(N^2). Joshua in his answer suggested using a hash table to test for duplicates, which will give a linear running time O(N) at the expense of space. It might also be slower for smaller lists. Finally, if your list can be sorted, then you should get O(N.log(N)) as coredump- mentions. Here is an example that tests for duplicates in numeric lists using sort. (This is a destructive function.)
(defun duplicatesp (list)
(mapl (lambda (cdr) (if (eql (first cdr) (second cdr))
(return-from duplicatesp T)))
(sort list '<)) nil)
UPDATE
Out of curiosity, I measured the performance of the suggested answers for worst-case scenarios (almost no duplicates). So, 1 mln tries of lists of 10 elements:
using member (Jan's answer): 1.139 seconds;
using hash-table (Joshua's answer): 1.436 seconds;
using sort (see above, but with first copying the list): 1.484 seconds.
So, no difference with small lists. Interestingly, using a hash table has some penalty but it is very small. Let's try 1000 tries of lists of 1000 elements:
using member: 9.968 seconds;
using hash-table: 0.234 seconds;
using sort: 0.390 seconds.
As expected, using member has higher complexity. The difference between sorting and hashing is non-visible at this list size. Let's do 10 tries of lists of 1,000,000 elements:
using hash-table: 3.214 seconds;
using sort: 9.296 seconds.
So, sort is still quite good but is slowing down. Here is a simple code I used to profile the functions:
(defun random-list (length)
(loop for i from 0 below length collecting
(random (expt 10 10))))
(defun random-collection (length tries)
(loop for i from 0 below tries collecting
(random-list length)))
(defun test-duplicates (function &key length tries)
(let ((rc (random-collection length tries)))
(time (mapc function rc))
nil))
(test-duplicates #'dp_hash :length 1000000 :tries 10)
;etc.
Many functions in Common Lisp uses generalized booleans, according to which nil (the empty list) is false, and everything else is true:
Type BOOLEAN
… Conditional operations, such as if, permit the use of generalized
booleans, not just booleans; any non-nil value, not just t, counts as
true for a generalized boolean. However, as a matter of convention,
the symbol t is considered the canonical value to use even for a
generalized boolean when no better choice presents itself.
Note the remark that t is used "when no better choice presents itself." It's often helpful to make functions that return a generalized boolean return some other piece of useful information as the true value. For instance, member returns the tail of the list whose first element is the element being checked for membership. In this case, it might be useful to return an association list mapping duplicated elements to the number of times that they appear in the list.
Here's an approach that does that. It first iterates through the list, building a hash table of the unique (as per test and key arguments) elements of the list, mapping each one to the number of times it appears. Then, a pass through the hash table is used to build an association list of all the elements that appear more than once.
(defun contains-duplicates (list &key (test 'eql) (key 'identity))
"Returns an association list mapping duplicated elements to the
number of times that they appear in LIST. TEST is a comparison
operator used to determine whether two elements are the same, and must
be acceptable as a test argument to MAKE-HASH-TABLE. KEY is used to
extract a value from the elements of LIST, and the extracted values
are compared and returned in the result."
(let ((table (make-hash-table :test test))
(result '()))
(dolist (x list)
(incf (gethash (funcall key x) table 0)))
(maphash #'(lambda (key count)
(unless (eql 1 count)
(push (cons key count) result)))
table)
result))
(contains-duplicates '(1 1 2 3 4 4 4))
;;=> ((4 . 3) (1 . 2))
(contains-duplicates '(1 2 3 4)) ; no duplicates
;;=> NIL
(contains-duplicates '("A" "a" b a) :test 'equal :key 'string)
;;=> (("A" . 2))
(contains-duplicates '("A" "a" b a) :test 'equal :key 'string) ; "A" ~ a, but not "a"
;;=> (("A" . 2))
(contains-duplicates '("A" "a" b a) :test 'equalp :key 'string) ; "A" ~ "a" ~ a
;;=> (("A" . 3))
(contains-duplicates '(1 2 3 5) :key 'evenp) ; two even elements
;;=> ((NIL . 2))
Related
I am trying to figure out how to access the elements in a nested list in LISP. For example:
((3 (1 7) (((5)))) 4)
If I use dolist, i run into the brackets. Is there any method to just get the elements from the list?
This is actually a surprisingly subtle question! It's in some ways the equivalent of asking: how do I get the nested elements of an HTML DOM, by specifying a pattern. (more on that aspect later)
If you just want to get the non-list elements as a sequence, e.g.
((3 (1 7) (((5)))) 4) -->
(nth 3 (3 1 7 5 4)) -->
5
You can use the 'cheat' way: the flatten function in the CL alexandria library. (via quicklisp)
(ql:quicklisp :alexandria)
(nth 3 (alexandria:flatten '((3 (1 7) (((5)))) 4)))
Which gives us the sought after,
5
But, the alexandrian function is simple enough that we can take a look at the source code itself:
(defun flatten (list)
(cond
((null list)
nil)
((consp (car list))
(nconc (flatten (car list)) (flatten (cdr list))))
((null (cdr list))
(cons (car list) nil))
(t
(cons (car list) (flatten (cdr list))))))
As you can see, it's a recursive function -- at each level of recursion it asks the question: what is the object that I'm flattening? If it's the empty list, return nil. I'm done!
Otherwise it has to be a non empty list. If the first element of the list is also a list then flatten that and also flatten the cdr of the function argument list and concatenate the results.
If the first element is not a list and the second element is '(), that must mean we have a list with one element: return it.
The final case case, which exhausts our possibilities is that the first element in the list is an atom while the rest of the list is a list with at least one element. In that case concatenate the first element with the results of a flatten performed on the rest of the list.
The fact that the description in English is so ponderous shows the power of recursion, (and also my own lack of fluency when describing it).
But there's actually another way your question could interpreted: if I have a list that looks something like: ((n1 (n2 n3) (((n4)))) n5) How do I get at n2, even if n2 is itself a list? Our previous recursive algorithm won't work -- it depends on n2 not being a list to know when to stop. But, we can still use recursion and the very list we're searching as the basis for a pattern:
;; For each element in our pattern list look for a corresponding
;; element in the target, recursing on lists and skipping atoms that
;; don't match.
(defun extract-from-list (pattern target to-find)
(dotimes (i (length pattern))
(let ((ith-pattern (nth i pattern)))
(cond
((consp ith-pattern)
(let ((results
(extract-from-list (nth i pattern)
(nth i target)
to-find)))
(when results
(return results))))
(t
(if (eq to-find ith-pattern)
(return (nth i target))))))))
Note that,
(extract-from-list
'((n1 (n2 n3) (((n4)))) n5) ;; The pattern describing the list.
'((3 (1 7) (((5)))) 4) ;; The list itself.
'n4) ;; which of the elements we want.
still returns the old answer:
5
But,
(extract-from-list
'((n1 (n2 n3) (n4)) n5) ;; The pattern describing the list, note (n4) rather than (((n4)))
'((3 (1 7) (((5)))) 4) ;; The list itself.
'n4) ;; The element we want to pull from the list
Returns
((5))
Magic! One of the aspects of Lisp that makes it so extraordinarily powerful.
I'm trying to write a function using Scheme that :
take a list of integers with more than two elements as a parameter
sum the n-th-element and (n+1)-th-element
return this list
Result should be as follows :
> (SumNeighbors (list 1 2 3 4))
(3 5 7)
I think I get the way to add elements but my recursion is totally wrong...
(define (SumNeighbors lst)
(if (not (null? (cdr lst)))
(append (list (+ (car lst) (car (cdr lst)))) (SumNeighbors (cdr lst)))))
Any help would be appreciated.
The solution to this problem follows a well-known pattern. I'll give you some hints, it'll be more fun if you find the answer by your own means:
(define (SumNeighbors lst)
(if <???> ; if there's only one element left
<???> ; we're done, return the empty list
(cons ; otherwise call `cons`
(+ <???> <???>) ; add first and second elements
(SumNeighbors <???>)))) ; and advance recursion
Notice the following:
Your solution is lacking the base case - what happens when the list we're traversing only has one element left? it's time to finish the recursion! and because we're building a list as the output, what should be the value returned?
We normally use cons to build an output list, not append. That's the natural way to build a list
The part of this procedure that falls outside the solution template is the fact that we stop when there's a single elment left in the list, not when the list is empty (as is the usual case)
You'll see that many procedures that iterate over an input list and return a list as output follow the same solution template, it's very important that you learn how and why this works, it's the foundation for writing solutions to other similar problems.
#!r6rs
(import (except (rnrs base) map)
(only (srfi :1) map))
(define (sum-neighbors lst)
(map + lst (cdr lst)))
The higher order function map as defined in SRFI-1 supports uneven lenght arguments. It will stop at the shortest list.
If you call (sum-neighbors '(1 2 3 4)) it will become (map + (1 2 3 4) (2 3 4)) which is the same as (cons (+ 1 2) (cons (+ 2 3) (cons (+ 3 4) '())))
I'm trying to figure out how to obtain the last (non-empty) list from within another list, or return nil if there is no such list (recursively). This is an homework assignment, and as such I am looking for help on the method, not necessarily the code for it. Example:
(lastele '(1 (2 3) 4 5)) ;=> (2 3)
(lastele '(1 (2 3) (4 5)) ;=> (4 5)
(lastele '(1 2 3 4 5)) ;=> NIL
I was trying to run through the list, and if I encountered a sublist, I would check to see if the rest of the list contained any more non-empty sublists, if it did, continue with setting the list to that, and repeating until we had a null list.
(defun lastele2 (L)
(if (null L)
'()
(if (hasMoreLists (rest L))
(lastele2 (rest L))
(first L))))
It seems as if I can't get hasMoreLists to work, though. Returning t or f within is just erroring. Is this the best way to go about this?
First of all, note that you're implicitly assuming that none of the sublists are the empty list; if they could be the empty list, then nil is an ambiguous result, because you can't tell whether your function returned nil because there were no sublists, or because there were, and the last one was empty. E.g.,
(fn '(1 2 3 4 5)) ;=> nil because there are no sublists
(fn '(1 2 3 () 5)) ;=> nil because there are sublists, and the last one is nil
So, under the assumption that there are no non-null sublists in the toplevel list, we can continue.
A non-homework solution using standard functions
You don't need to write this. You can just use find-if with the predicate listp and specify that you want to search from the end by using the keyword argument :from-end t:
CL-USER> (find-if 'listp '(1 (2 3) 4 5) :from-end t)
(2 3)
CL-USER> (find-if 'listp '(1 (2 3) (4 5)) :from-end t)
(4 5)
CL-USER> (find-if 'listp '(1 2 3 4 5) :from-end t)
NIL
Writing your own
If you need to write something like this, your best bet is to use a recursive function that searches a list and keeps track of the most recent list element that you've seen as the result (the starting value would be nil) and when you finally reach the end of the list, you'd return that result. E.g.,
(defun last-list (list)
(labels ((ll (list result) ; ll takes a list and a "current result"
(if (endp list) ; if list is empty
result ; then return the result
(ll (cdr list) ; else continue on the rest of list
(if (listp (car list)) ; but with a "current result" that is
(car list) ; (car list) [if it's a list]
result))))) ; and the same result if it's not
(ll list nil))) ; start with list and nil
The local function ll here is tail recursive, and some implementations will optimize it into a loop, but would be more idiomatic to use a genuine looping construct. E.g., with do, you'd write:
(defun last-list (list)
(do ((result nil (if (listp (car list)) (car list) result))
(list list (cdr list)))
((endp list) result)))
If you don't want to use labels, you can define this as two functions:
(defun ll (list result)
(if (endp list)
result
(ll (cdr list)
(if (listp (car list))
(car list)
result))))
(defun last-list (list)
(ll list nil))
Alternatively, you could make last-list and ll be the same functions by having last-list take the result as an optional parameter:
(defun last-list (list &optional result)
(if (endp list)
result
(last-list (cdr list)
(if (listp (car list))
(car list)
result))))
In all of these cases, the algorithm that you're implementing is essentially iterative. It's
Input: list
result ← nil
while ( list is not empty )
if ( first element of list is a list )
result ← first element of list
end if
list ← rest of list
end while
return result
Something based on the code in the question
We can still find something that's a bit closer to your original approach (which will use more stack space), though. First, your original code with proper indentation (and some newlines, but there's more flexible in coding styles there):
(defun lastele2 (L)
(if (null L)
'()
(if (hasMoreLists (rest L))
(lastele2 (rest L))
(first L))))
The approach it looks like you're trying to use is to define the last sublist of a list L as:
nil, if L is empty;
if (rest L) has some sublists, whatever the last sublist of (rest L) is; and
if (rest L) doesn't have some sublists, then (first L).
That last line isn't quite right, though. It needs to be
if (rest L) doesn't have some sublists, then (first L) if (first L) is a list, and nil otherwise.
Now, you've already got a way to check whether (rest L) has any (non-null) sublists; you just check whether (lastele2 (rest L)) returns you nil or not. If it returns nil, then it didn't contain any (non-null) sublists. Otherwise it returned one of the lists. This means that you can write:
(defun last-list (list)
(if (endp list) ; if list is empty
nil ; then return nil
(let ((result (last-list (rest list)))) ; otherwise, see what (last-list (rest list)) returns
(if (not (null result)) ; if it's not null, then there were more sublists, and
result ; last-list returned the result that you wantso return it
(if (listp (first list)) ; otherwise, if (first list) is a list
(first list) ; return it
nil))))) ; otherwise return nil
This is implementing the an essentially recursive algorithm; the value of the subproblem is returned, and then lastList returns a value after examining it that result:
Function: lastList(list)
if ( list is empty )
return nil
else
result ← lastList(list)
if ( result is not nil )
return result
else if ( first element of list is a list )
return first element of list
else
return nil
end if
end if
No, it's not the best way to go about this. To find whether the rest of list has more lists, you need to search it - and if it has, you restart scanning over the rest of your list.
I.e. you do a lot of back and forth.
Instead, just search along, and update a side variable to point to any list you find along the way.
(defun lastele (lst &aux a) ; a is NIL initially
(dolist (e lst a) ; return a in the end
(if (consp e) (setq a e))))
I am writing a procedure which returns a list with all of the negative odd and positive
even integers removed (strings can stay), by using lambda in the primitive filter procedure. I also am avoiding using recursion, but that is what's stumping me.
What I have so far is:
(define (f2b lst)
(cond ((null? lst)'()) ; if the list is empty, return the empty list
((pair? (car lst)) ; if the current element isn't a list
(filter (lambda (x) (or (even? x) (positive? x))) (car lst))
(filter (lambda (x) (or (odd? x) (negative? x))) (car lst)))
(else (string? (car lst)) ;otherwise, if the current element is a string,
(car lst) ; then return that element
(f2b (cdr lst)))))
I'm also not sure how I can apply both of the filter procedures at the same time.
It's way simpler than that. All you have to do is filter the list. You just need the appropriate predicate.
When do you want to keep an element? You phrased it in terms of what you want to remove, so let's start with that. You want to remove if it's a negative odd or a positive even integer, and leave everything else in. It's easier to break it down into smaller functions.
(define (positive-even? x) (and (positive? x) (even? x)))
(define (negative-odd? x) (and (negative? x) (odd? x)))
(define (remove-num? x) (or (positive-even? x) (negative-odd? x)))
This defines whether to keep a number. But the list element might not be a number. So we
keep it if it's not a number, or if it doesn't match remove-num?:
(define (keep-element? x) (or (not (number? x)) (not (remove-num? x))
Then your function just has to call filter:
(define (f2b lst) (filter keep-element? lst))
Seems to work:
(f2b '(-4 -3 -2 -1 0 1 2 3 4 "a string" "another"))
=> (-4 -2 0 1 3 "a string" "another")
Here's how it would look as one big honkin' function:
(define (f2b lst)
(filter
(lambda (x)
(or (not (number? x))
(not (or (and (positive? x) (even? x))
(and (negative? x) (odd? x))))))
lst)
Personally, the nested or not or and gets a bit hard to read for my taste...
Ok, apparently you have nested lists. All you have to do here is map the result of the filter with a function which:
when given a list, returns (f2b lst)
otherwise, returns the element unchanged.
I will leave it as an exercise for you since, if you thought my function could possibly work on a nested list, clearly you have a lot of learning to do...
I have written this function to 'check' if the list contains only 2-elements pairs like these: ((1 2)(3 1)(6 2)) --- (sorted based on first elements & no repetition of 1st elements). But I am getting errors, can anyone please give an idea:
(define Bag?
(lambda setlist
(cond ((null? setlist) '())
((and (pair? (caar setlist)) (= (length (caar setlist)) 2)))
((> (caar setlist) 0) (< (caar setlist) (car (cdr (car setlist) ))))
(else(Bag? (cdr setlist))))
))
The list traversal doesn't look right. The lambda is incorrectly declaring its parameter, and the second condition is wrong, you're not advancing the recursion there.
It'd better to start from scratch. I'll give you a high-level solution in Racket stating what needs to be checked, it's up to you to rewrite it in terms of simpler procedures:
(define (bag? setlist)
(and (apply < (map car setlist))
(andmap (lambda (e) (and (pair? e) (= 2 (length e))))
setlist)))
The above verifies that the first-elements in each pair appear sorted in ascending order, and that each pair in the list contains exactly two elements.