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.
Related
I am trying to write a function that takes a list of divisors, a list of numbers to test and applies drop-divisible for each element of the list of divisors. I am supposed to use filter, map or foldl and no recursion
I wrote the drop-divisible function:
(define (drop-divisible l n)
(cond
[(empty? l) empty]
[(empty? (rest l)) l]
(let ([i (first l)])
(if (zero? (modulo i n))
(drop-divisible (rest l) n)
(cons i (drop-divisible(rest l)n))))]))
That seems to work, but I'm confused on how I can call drop-divisible for each element in the list when it only wants one list and an integer as a parameter?
Hopefully, that makes sense, thanks
When you want "all the elements of this list except the ones that meet such-and-such criterion", the filter function provides an easy way to do that:
(define (drop-divisible l n)
(filter (λ (i) (not (zero? (modulo i n))))
l))
> (drop-divisible '(4 6 9 8 12 14) 3)
'(4 8 14)
filter constructs a new list, containing only the items of the original list that meet your criterion. The first argument to filter is a function that takes a single element of the list and returns #f if the element should be skipped when creating the new list, and returns any other value to include the element in the new list. The second argument to filter is the list to filter.
In other words, filter does exactly what your code does, but it's generalized to apply any filtering criterion.
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))
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) '())))
Assume I have the list (3 1 4 5 2) with the name "numbers". I am looking for a command that will reverse the list from index 0 up to an arbitrary index, i.e. (reverse numbers 2) which will give the new list as (4 1 3 5 2).
I've tried googling, but could not find a suitable function and I'm too much of a newbie to write the function myself at this stage.
Thank you.
Simple CL version based on the libary functions:
(defun reverse-first-n (list n)
(nreconc (subseq list 0 n) (nthcdr n list)))
This is memory-optimal, i.e., it does not allocate unnecessarily:
no need to copy the tail, thus nthcdr instead of subseq
revappend copies the 1st argument which is a fresh list anyway, so nreconc is more economical.
This version is speed-suboptimal, because it traverses list to the nth position 3 times - once in subseq, once in nthcdr, and then once in nreconc.
Here is the optimal verion:
(defun reverse-first-n (list n)
(if (or (= n 0) (= n 1))
list
(do* ((tail (list (pop list)))
(head tail (cons (pop list) head))
(count (1- n) (1- count)))
((zerop count)
(setf (cdr tail) list)
head))))
Note that there is very little chance that this is the performance bottleneck in your code. My main purpose in providing the second version is to show how much time and effort the extensive and well-designed CL library saves you.
Which dialect of Lisp are you using? Here's a Scheme solution (using SRFI 1):
(require srfi/1) ; assuming you're using Racket
(define (reverse-first-n lst n)
(call-with-values (lambda ()
(split-at lst n))
append-reverse!))
I made the function really "reverse the first n elements" like your title says, and unlike your question description. So for example:
> (reverse-first-n '(3 1 4 5 2) 2)
'(1 3 4 5 2)
> (reverse-first-n '(3 1 4 5 2) 3)
'(4 1 3 5 2)
As requested by the OP, here's a Common Lisp version. sds already posted a pretty decent version, so the version I'm writing is a more direct port of my Scheme solution (append-reverse! ⇒ nreconc; call-with-values ⇒ multiple-value-call; and I'm porting SRFI 1's split-at to CL):
(defun split-at (list n)
(if (zerop n)
(values '() list)
(multiple-value-bind (prefix suffix)
(split-at (cdr list) (1- n))
(values (cons (car list) prefix) suffix))))
(defun reverse-first-n (list n)
(multiple-value-call #'nreconc (split-at list n)))
(Why split-at? Its purpose is to provide both the take (subseq) and drop (nthcdr) with only one traversal of the input list.)
How would you define a function which takes one argument, which should be a list, and returns the elements in the
list which are themselves lists?
(check-expect (find-sublists ’(1 2 () (3) (a b c) a b c))
’(() (3) (a b c)))
Do you have experience designing functions that can filter through a list?
A simpler problem with the same flavor as the original is something like this: design a function that takes a list of numbers and keeps only the even numbers. Would you be able to do that function?
Looking at http://www.ccs.neu.edu/home/matthias/HtDP2e/htdp2e-part2.html and going through its guided exercises may also help.
Two useful tools which should start you on your way:
1) Traversing through a list:
; traverse: takes a list of numbers
; Goes through each element, one-by-one, and alters it
(define traverse
(lambda (the_list)
(if (empty? the_list)
empty
(cons (+ 1 (first the_list))
(traverse (rest the_list))))))
(traverse (cons 3 (cons 4 empty))) returns (cons 4 (cons 5 empty))
2) list?:
(list? (list 1 2 3)) returns #t
(list? 5) returns #f