i am learning racket for my course and while going through solutions to an assignment, I found that a function doesn't return true, when comparing empty and '()
Here is the output I'm getting:
(lst-contains? '(1 2 3 ()) '()) ; output: #t
(lst-contains? '(1 2 3 ()) empty) ; output: #t
(lst-contains? '(1 2 3 empty) empty) ; output: #f (unexpected!)
(lst-contains? '(1 2 3 empty) '()) ; output: #f (unexpected!)
Here's my definition of lst-contains?:
(define (lst-contains? lst element)
(cond
[(empty? lst) #f]
[(equal? (first lst) (element)) #t]
[else (lst-contains? (rest lst) element)]))
Thanks : )
This is due to the fact that quote (or ') "distributes" across a list. So, when you write '(1 2 3 empty), you're actually creating something that's equivalent to (list 1 2 3 'empty) not (list 1 2 3 empty).
You can check for yourself that this is the case by looking at the result of:
(list-ref '(1 2 3 empty) 3) ; output: 'empty
(list-ref (list 1 2 3 empty) 3) ; output: '()
In racket they are equal:
(define (lst-contains lst element)
(cond
((empty? lst) #f)
((equal? (first lst) element) #t)
(else (lst-contains (rest lst) element))))
(lst-contains '(1 2 3 ()) empty) ; -> #t
Tested in DrRacket.
Note that '(1 2 3 empty) is different from '(1 2 3 ()) since in the first case empty is taken as a symbol.
Related
I am trying to come up with a procedure that filters out any value other than a number. For example:
'(1 2 (a) 3) => '(1 2 () 3) or '(1 2 (a 10 11 (b 2 (c))) 3) => '(1 2 (10 11 (2 ())) 3)
This is what I have so far but it doesn't really work because it replaces the non numbers with an empty list instead of nothing:
(define (filter-numbers lst)
(if (null? lst)
'()
(if (list? lst)
(cons
(filter-numbers (car lst))
(filter-numbers (cdr lst)))
(if (number? lst)
lst
'())))
)
The procedure outputs the following:
> (filter-numbers '(1 2 (a) 3))
'(1 2 (()) 3)
instead of: '(1 2 () 3)
You need to check if an element in a sublist is a number before deciding to cons it to the output or not. I think it's easier if we restructure the code a bit; also some tips: don't use list?, prefer pair? as it's faster (unlike list? it doesn't have to traverse the whole list). And prefer cond instead of nesting ifs, your code will be easier to read. This is what I mean:
(define (filter-numbers lst)
(cond ((null? lst) lst)
((not (pair? (car lst)))
; check the element while still in a list, if we wait
; until we reach an atom it's too late to filter it out
(if (number? (car lst))
; either add the element to the output or skip it
(cons (car lst) (filter-numbers (cdr lst)))
(filter-numbers (cdr lst))))
(else (cons
(filter-numbers (car lst))
(filter-numbers (cdr lst))))))
It works as expected:
(filter-numbers '(1 2 (a) 3))
=> (1 2 () 3)
(filter-numbers '(1 2 (a 10 11 (b 2 (c))) 3))
=> '(1 2 (10 11 (2 ())) 3)
I am looking for a function that does what "filter" is used for, but without using filter. On top, it should also filter nested lists.
E. g. list ( 1 2 (list 3 'a 'b) 3 4) should return list (1 2 (list 3) 3 4).
I already have the function to filter. My thought is, that I have to distinguish in the beginning of the function, if first lst is a list itself.
What I got so far is:
(define (my-filter pred lst)
(cond ((null? lst) null) ;;(after this I would try to test if it is a list itself, but I am not sure how)
((pred (first lst))
(cons (first lst) (my-filter pred (rest lst))))
(else (my-filter pred (rest lst)))))
As it has been said in the comments, a possible solution is:
(define (my-filter pred lst)
(cond [(null? lst) null]
[(list? (first lst)) (cons (my-filter pred (first lst))
(my-filter pred (rest lst)))]
[else (if (pred (first lst))
(cons (first lst) (my-filter pred (rest lst)))
(my-filter pred (rest lst)))]))
Example
> (my-filter number? '(1 a 2 (3 (b 4) c) 5 6 d 7))
'(1 2 (3 (4)) 5 6 7)
> (my-filter (lambda (x) (not (number? x))) '(1 a 2 (3 (b 4) c) 5 6 d 7))
'(a ((b) c) d)
Trying to write a function that returns every third element in a list
including the first element in racket. All I get now is my code blowing up with a first: contract violation
expected: (and/c list? (not/c empty?))
given: 4
(define l (list 1 2 3 4 5 6 7 8 9))
(define (skipper lst)
(if (null? lst)
'()
(cons (first lst)
(skipper (car (cdr (cdr (cdr lst))))))))
(skipper l)
The problem was just the car around cdddr.
(define l (list 1 2 3 4 5 6 7 8 9))
(define (skipper lst)
(if (null? lst)
'()
(cons (first lst)
(skipper (if (< (length lst) 3)
'()
(cdddr lst))))))
(skipper l) ;; '(1 4 7)
Generalized Solution
(define (my-cdr lst) ;; `cdr` behaving like in common-lisp: (cdr '()) -> '()
(cond ((null? lst) '())
(else (cdr lst))))
(define (multi-cdr lst k) ;; apply `my-cdr` k-times on `lst`
(cond ((zero? k) lst)
(else (multi-cdr (my-cdr lst) (- k 1)))))
(define (skipper lst k)
(if (null? lst)
'()
(cons (first lst)
(skipper (multi-cdr lst k) k))))
Test it:
(skipper l 3) ;; '(1 4 7)
(skipper l 4) ;; '(1 5 9)
(skipper l 2) ;; '(1 3 5 7 9)
(skipper l 1) ;; '(1 2 3 4 5 6 7 8 9)
The issue is that you cannot call (cdr (cdr (cdr lst))) when lst has less than 3 elements.
You tagged this with racket, so I'm going to show you a solution using match
(define (skipper l)
(match l
;; some element and at least 3 more
((list a rest ..3)
(cons a (skipper (cddr rest))))
;; at least one element
((cons a _)
(list a))
;; otherwise
(else
empty)))
(skipper '())
;; '()
(skipper '(0))
;; '(0)
(skipper '(0 1 2 3 4 5 6 7))
;; '(0 3 6)
(skipper '(0 1 2 3 4 5 6 7 8 9))
;; '(0 3 6 9)
This solution doesn't use length which unnecessarily computes the length of the list
Does anyone know how to return a number of specific elements in a list?
Example: given
(list 'a 'a 'a 'a 'a 'b 'b 'b)
Returns the numbers of 'a: 5
numbers of 'b: 3
You count them. You make a procedure that takes a list and what you want to search for and you iterate that list while keeping a count and when you reach the end you return that value.
A skeleton for a simple recursive solution:
(define (count-element element lst)
(define (helper lst count)
(cond ((empty? lst) count)
((equal? element <first element>) <recurse whith cdr and increasing count>)
(else <recurse with cdr>)))
(helper lst 0))
Or you can use foldl
(define (count-element element lst)
(foldl (lambda (e count)
(if <e is the same as element>
<return 1 more than count>
<return count>))
0
lst))
There are probably 10 more ways I could do it, but the first is the most educational and the second the most common way I would do it.
Some tests:
(define test '(a a a a a b b b))
(count-element 'b '()) ; ==> 0
(count-element 'e test) ; ==> 0
(count-element 'a test) ; ==> 5
(count-element 'b test) ; ==> 3
I somewhat managed to find the answer, so here's the function definition:
(define (number-of s L)
(cond
[(empty? L) 0]
[else (cond [(eq? s (first L)) (+ 1 (number-of s (rest L)))]
[else (number-of s (rest L))])]))
I have a sorted list of integers, (1 2 4 5 6 6 7 8 10 10 10). I want to group them all, so that I get ((1) (2) (4) (5) (6 6) (7) (8) (10 10 10)).
So far I have this, which works:
(let ((current-group (list)) (groups (list)))
(dolist (n *sorted*)
(when (and (not (null current-group)) (not (eql (first current-group) n)))
(push current-group groups)
(setf current-group (list)))
(push n current-group))
(push current-group groups)
(nreverse groups))
But I'm sure there must be a much more LISPy way to do this. Any ideas?
Not that bad. I would write it this way:
(defun group (list)
(flet ((take-same (item)
(loop while (and list (eql (first list) item))
collect (pop list))))
(loop while list
collect (take-same (first list)))))
CL-USER 1 > (group '(1 2 4 5 6 6 7 8 10 10 10))
((1) (2) (4) (5) (6 6) (7) (8) (10 10 10))
There's already an accepted answer, but I think it's worth looking at another way of decomposing this problem, although the approach here is essentially the same). First, let's define cut that takes a list and a predicate, and returns the prefix and suffix of the list, where the suffix begins with the first element of the list that satisfies the predicate, and the prefix is everything before that that didn't:
(defun cut (list predicate)
"Returns two values: the prefix of the list
containing elements that do no satisfy predicate,
and the suffix beginning with an element that
satisfies predicate."
(do ((tail list (rest tail))
(prefix '() (list* (first tail) prefix)))
((or (funcall predicate (first tail))
(endp tail))
(values (nreverse prefix) tail))))
(cut '(1 1 1 2 2 3 3 4 5) 'evenp)
;=> (1 1 1) (2 2 3 3 4 5)
(let ((l '(1 1 2 3 4 4 3)))
(cut l (lambda (x) (not (eql x (first l))))))
;=> (1 1), (2 3 4 4 3)
Then, using cut, we can move down the an input list taking prefixes and suffixes with a predicate that's checking whether an element is not eql to the first element of the list. That is, beginning with (1 1 1 2 3 3) you'd cut with the predicate checking for "not eql to 1", to get (1 1 1) and (2 3 3). You'd add the first to the list of groups, and the second becomes the new tail.
(defun group (list)
(do ((group '()) ; group's initial value doesn't get used
(results '() (list* group results))) ; empty, but add a group at each iteration
((endp list) (nreverse results)) ; return (reversed) results when list is gone
(multiple-value-setq (group list) ; update group and list with the prefix
(cut list ; and suffix from cutting list on the
(lambda (x) ; predicate "not eql to (first list)".
(not (eql x (first list))))))))
(group '(1 1 2 3 3 3))
;=> ((1 1) (2) (3 3 3))
On implementing cut
I tried to make that cut relatively efficient, insofar as it only makes one pass through the list. Since member returns the entire tail of the list that begins with the found element, you can actually use member with :test-not to get the tail that you want:
(let ((list '(1 1 1 2 2 3)))
(member (first list) list :test-not 'eql))
;=> (2 2 3)
Then, you can use ldiff to return the prefix that comes before that tail:
(let* ((list '(1 1 1 2 2 3))
(tail (member (first list) list :test-not 'eql)))
(ldiff list tail))
;=> (1 1 1)
It's a simple matter, then, to combine the approaches and to return the tail and the prefix as multiples values. This gives a version of cut that takes only the list as an argument, and might be easier to understand (but it's a bit less efficient).
(defun cut (list)
(let ((tail (member (first list) list :test-not 'eql)))
(values (ldiff list tail) tail)))
(cut '(1 1 2 2 2 3 3 3))
;=> (1 1), (2 2 2 3 3)
I like to use reduce:
(defun group (lst)
(nreverse
(reduce (lambda (r e) (if (and (not (null r)) (eql e (caar r)))
(cons (cons e (car r)) (cdr r))
(cons (list e) r)))
lst
:initial-value nil)))
or using push:
(defun group (lst)
(nreverse
(reduce (lambda (r e)
(cond
((and (not (null r)) (eql e (caar r))) (push e (car r)) r)
(t (push (list e) r))))
lst
:initial-value nil)))