So I need to define filter using accumulation, and I need to complete this definition:
(define filter
(lambda (predicate sequence)
(accumulate ???? null sequence)))
I fill in:
(lambda (arg result)
(cons
(cond ((null? arg) null)
((predicate arg) arg)
(else (filter predicate (cdr sequence))
(result))
But when I test it on (list 1 2 3 4) I get something weird: '((2 ((4) 4) 4) 2 (2 ((4) 4) 4) 4).
Can anyone help me out? What did I do wrong?
Thanks a lot!
Given the SICP definition of accumulate:
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
Then op is called with two arguments: the interesting element of sequence and the result of filtering the rest of the sequence, or initial if there is no more list. And you are expected to use accumulate as:
(define filter
(λ (predicate sequence)
(accumulate ???? null sequence)))
So, if ???? wants to filter based on predicate it should:
if predicate is true on the interesting element of the list, return a list whose car is that element, and whose cdr is the result of filtering the rest of the list;
if predicate is false on the interesting element of the list, return the result of filtering the rest of the list.
That should be enough for you to write ????.
Related
I am trying to write my own 'list?' predicate in scheme. I know that definition of a list: 1. en empty list 2. pair, whose cdr is a pair
I know my code is wrong because it is returning true value on every pair, but every pair is not a list. I do not know how to implement the condition that cdr of the list must be also a pair.
(define (my-list? x)
(if (equal? x ()) #t
(pair? x)))
The list? procedure checks if a list is proper (nested cons-cells ending in a null value), hence we have to consider three cases:
(define (my-list? x)
(cond ((null? x) #t) ; empty list
((pair? x) (my-list? (cdr x))) ; pair, advance recursion
(else #f))) ; atom
A null value is considered an empty list. If we're in a pair, then we advance on its cdr by calling the procedure recursively. If it's not null nor a pair, it's an atom. This will cover edge cases, such as this:
(my-list? 42)
=> #f
(my-list? (cons 1 2))
=> #f
And will return true for proper lists:
(my-list? '())
=> #t
(my-list? (cons 1 (cons 2 '())))
=> #t
I just started learning Racket so I am still trying to figure out the intricacies of the language. I am trying to implement my own search function in a list. If the function finds it, it returns the index, otherwise it returns -1.
(define (find-index list item)
(if (equal? (length list) 1)
(if (equal? (first list) item) 0 1)
(if (equal? (first list) item)
0
(+ 1 (my-search (rest list) item)))))
So the find-index function is a recursive function that walks through the list looking for an item that is equivalent to "item." I have written it so that, if there are 4 elements in a list, the function can return any number between 0-4.
(define (my-search list item)
(define length (my-length list))
(define index (find-index list item))
(if (= index length) -1 index))
My idea is that if the find-index function returns a number equal to the list's length, it means the function did not find the item, therefore my-search function is supposed to return -1.
However, when I put in
(my-search (list "apple" "barbecue" "child" "demon" "enter") "fire")
the result I get is 3, instead of -1. If I print index before the if statement the index is 3 instead of 5. If
(if (= index length) -1 index))
is not part of the my-search function then everything is fine.
What I think is going on is that index is the id for the function itself, not the result of the function. However, I don't see why that effects the returning result of my-search. Is anyone willing to shed some light on this question?
Also, any style critique is welcome. I would like to know if I am not following conventions.
The weird behavior is caused by the fact that find-index is calling my-search which is calling find-index (a mutual recursion!). At some point, that extra if is causing the recursion to end prematurely. Solution: replace the call to my-search with find-index in the find-index procedure.
Now that that was settled, we can write a single procedure for finding an element's index in a list or signaling that it wasn't found, like this:
(define (find-index lst item)
(cond ((empty? lst) #f)
((equal? (first lst) item) 0)
(else
(let ((result (find-index (rest lst) item)))
(if result (add1 result) #f)))))
Let's see how the above improves your procedure:
The preferred way to structure a procedure with multiple conditions is to use a cond
You should not use list as a parameter name, it clashes with a built-in procedure with the same name
For the same reason, you should not call length a local definition
It's not a good idea to use length for checking if we stepped outside of a list, a well-built recursion will take care of that, without having to iterate over the list all over again
It's common to use #f to indicate that a search procedure didn't find what it was looking for
In a well-structured recursion over lists, you should check if a list is empty, normally this is the first base case we write - your procedure will fail if an empty list is passed
We use let to declare local variables, in this case it makes sense, to avoid calling the recursion twice
Use (add1 x), it's more idiomatic than (+ 1 x)
But wait, we can do better! The above solution can be rewritten in a tail-recursive style; by ensuring that the recursive call is the last thing we do, our procedure will use constant space, and it'll be as efficient as a loop in a conventional programming language. The trick is to pass an extra parameter with the value to return (in this case, the index). I'll use a named let for brevity:
(define (find-index lst item)
(let loop ((lst lst) (idx 0))
(cond ((empty? lst) #f)
((equal? (first lst) item) idx)
(else (loop (rest lst) (add1 idx))))))
You can verify that both procedures work as advertised:
(find-index (list "apple" "barbecue" "child" "demon" "enter") "fire")
=> #f
(find-index (list "apple" "barbecue" "child" "demon" "enter") "child")
=> 2
That's how I would attack the problem.
(define (find-index L item) ; L your list. item the item for which you want the index
(define (aux L res) ; Auxiliary function. L your list. item the item for which you want the index
(cond ((null? L) -1) ; No thing was found, return -1.
((eq? (car L) item) res) ; If the item is equal to the current item then return the position.
(else (aux (cdr L) (add1 res))))) ; Move on to the next item in the list and increment the position.
(aux L 0)) ; Call of the auxiliary function that will be doing the job
Test run...
(define L '(a b c d))
Element not in the list
(find-index L 'e)
Output : -1
Element "d"
(find-index L 'd)
Output : 3
Here is a version of find-index that attempts to use the same style as your original example. Instead of list I use xs (which is short for "list of xes").
Note that it would be better to use the false value #f to indicate "not found".
(define (find-index xs item)
(if (empty? xs)
-1 ; not found
(if (equal? (first xs) item)
0 ; found at beginning
(let ([index-in-rest (find-index (rest xs) item)]) ; index in rest of list
(if (= index-in-rest -1)
-1 ; not found in rest of list
(+ 1 index-in-rest)))))) ; add 1 because we skipped
the first element
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 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))))
(define fun3
(lambda (item list)
(cond ((equal? item (car list)))
((fun3 item (cdr list)))
(else #f))))
I want to know what is wrong if I enter an element which is not in the list. There it show an error.--mcar: expects argument of type <mutable-pair>; given ()
What happens when you get to the end of your list? A list like (1 2 3) is actually a chain of cons cells:
(1 2 3) == (1 . (2 . (3 . ())))
You get the thing on the left side of the . using car, and the thing on the right using cdr. Consider what happens when you search for 4 in (1 2 3) with your code:
(define fun3
(lambda (item list)
(cond ((equal? item (car list)))
((fun3 item (cdr list)))
(else #f))))
Eventually you recurse to the case where item is (still) 4, and list is (3 . ()). Now, (fun3 item (cdr list)) will be called, and then item will (still) be 4, but list will be (). You can't call (car ()) because () isn't a cons cell. You need to explicitly check the case that list is the empty list:
(define fun3
(lambda (item list)
(cond ((null? list) <...>)
((equal? item (car list)))
((fun3 item (cdr list)))
(else #f))))
Now, there are two things to note:
This can be simplified significantly. Using some boolean logic, you could even get rid of the cond entirely (see Scheme, search if a word is a part of list for some ideas about how). The general point here is that you're doing something analogous to the C code
if ( condition ) {
return false;
}
else {
return true;
}
which can be simplified greatly to return !condition;. Do you see how your code is similar to that? Particularly, your second case is (fun3 item (cdr list)). If it's true, then you return true. If it's false, then you go to the next case and… return false. This means you can simply return the value of (fun3 item (cdr list)).
The more important issue is that you said you wanted to check whether item is an element of a list or any of its sublists, but your code right now (pending the fix about checking for the empty list) only checks whether item is a member of list, not any of its sublists. When item isn't equal to (car list), it could be because (car list) is another list, and you'll need to recurse into to and check whether item is in it. You might be helped by looking at search through nested list in scheme to find a number, but it won't tell you exactly how to do that.
In case you use mutable lists/pairs in racket, you have to keep in mind that it's completely separate data type from regular lists, where you use the () syntax.
When you 'request' the library for working with mutable data by calling:
(request scheme/mpair)
You now have to switch to using corresponding procedures from that lib - mcar, mcdr, mpair, mlist, mcons when constructing the pair, predicates, etc. Otherwise there's no way to receive and error from 'mcons'.
http://docs.racket-lang.org/reference/mpairs.html