This question already has an answer here:
DrRacket - Display all the values in a list that are above average
(1 answer)
Closed 3 years ago.
I'm not too sure what I'm doing wrong. I need to write a program that filters the numbers below the average of a list of numbers and only keeps the numbers that are greater than the average of all the numbers.
(define (total lon)
(cond
[(empty? lon) 0]
[else (+ first lon) (total (rest lon)))]))
(define (mean alon)
(/ (total alon) (length alon)))
(define (average-filter log)
(cond
[(empty? log) empty]
[(> (first log) (mean log))
(cons (first log) (average-filter (rest log)))]
[else (average-filter (rest log))]))
I expect it to remove all the ones below average, but I end up with an empty list.
Ex
(average-filter (list 1 2 3)) -> empty
when i should be getting:
(cons 3 empty)
Since log shrinks on each recursive call (mean log) changes too!
Compute the mean once:
(define (average-filter log)
(average-filter-h log (mean log)))
And then filter the numbers that are greater:
(define (average-filter-h log mean)
(cond
[(empty? log) empty]
[(> (first log) mean)
(cons (first log) (average-filter-h (rest log) mean))]
[else (average-filter-h (rest log) mean)]))
side note:
You can also use abstractions to put this more succinctly:
(define (mean alon)
(/ (apply + alon) (length alon)))
(define (average-filter log)
(let ([m (mean log)])
(filter (λ (x) (> x m)) log)))
Related
Im trying to write a small program that will take a list as an input and the first function is supposed to remove values that are greater than 4 and the other one is supposed to remove values that are lower than 4 in the check-expects. I figure this is a simple solution but I cant seem to figure it out as Im still new to lists and data structures. Any help would be appreciated, here is the code:
(define (removehigher lon n)
(cond [(> n lon)(remove (lon))]
[(< n lon) true]))
(define (removelower lon n)
(cond [(> n lon) true]
[(< n lon) (remove(lon))]))
(check-expect(removehigher(list 0 1 2 3 4 5 6)4)(list 0 1 2 3))
(check-expect(removelower(list 0 1 2 5 6 7) 3)(list 5 6 7))
There is a function called filter which would do this.
(define (my-filter pred lst (acc '()))
(cond [(null? lst) (reverse acc)]
[(pred (car lst)) (my-filter pred (cdr lst) (cons (car lst) acc))]
[else (my-filter pred (cdr lst) acc)]))
It is actually an in-built function filter. I use my- as prefix to not to overwrite this in-built function.
Once you have the function filter (or my-filter), you can write your desired functions easily - it is then about to define the predicate function (pred) correctly for each of the cases:
(define (remove-higher lst n)
(filter (lambda (x) (<= x n)) lst))
(define (remove-lower lst n)
(filter (lambda (x) (<= n x)) lst))
Alternatively, one could also use append-map:
(define (remove-higher lst n)
(append-map (lambda (x) (if (<= x n) (list x) '())) lst))
(define (remove-lower lst n)
(append-map (lambda (x) (if (<= n x) (list x) '())) lst))
Or define filter using append-map:
(define (my-filter pred lst)
(append-map (lambda (x) (if (pred x) (list x) '())) lst))
append-map in turn can be defined:
(define (my-append-map func lst)
(apply append (map func lst)))
The program i'm attempting to create is one that can perform a range of math functions from average to std-dev and so on but the one that im getting tripped up on is median. The list is already sorted so how can i get the length of this list to determine whether even or odd and then get the median from that. Current code below. Quite new to Scheme so the syntax is quite confusing to me still.
CODE
(define make-tswb
(lambda ()
(let ((records '()))
(lambda (command . args)
(cond
((equal? command 'empty?)
(null? records))
((equal? command 'add!)
(set! records (cons (car args) records)))
((equal? command 'get)
(letrec ((sort-records (lambda (r) (sort r (lambda (x y) (<= (car x) (car y)))))))
(if (null? args)
(sort-records records)
(sort-records (filter(car args) records)))))
((equal? command 'analytic)
(cond
((= (length args) 1)
((car args) records))
((= (length args) 2)
((car args) (filter (cadr args) records))))))))))
(define listofVal
(lambda (lst)
(if (null? lst)
'()
(sort (map cadddr lst) <))))
(define median
(lambda (medianList)
(let ((values (listofVal medianList)))
(let ((len (length medianList)))
// ??????????
(define tswb (make-tswb))
(tswb 'add! '(2 123 "temp1" 76.1))
(tswb 'add! '(1 123 "temp1" 72.0))
(tswb 'add! '(1 123 "temp1" 75.0))
(tswb 'analytic median)
According to this page, you need to handle two cases:
Length of the list is even number.
It is not an even number.
For the first case, you need to take two values in the middle, count a sum of them and then divide by two. For the second case, you need to find the middle of the sorted list and this value will be the median.
So, I think, this should work for you:
(define (count-median-for-even len listOfVal)
(/ (+ (list-ref listOfVal (round (- (/ len 2) 1)))
(list-ref listOfVal (round (/ len 2)))) 2))
(define (count-median-for-odd len listOfVal)
(list-ref listOfVal (round (/ len 2))))
(define median
(lambda (medianList)
(let ((values (listofVal medianList)))
(let ((len (length medianList)))
(if (even? len) (count-median-for-even len values) (count-median-for-odd len values))))))
Having tested this on the three test-cases from the link above, I can say this must do the job for you.
I've just started learning Racket.
I have written this procedure:
#lang racket
(define split
(lambda (list)
(define plus-list '())
(define minus-list '())
(cond ((null? list) '())
(else
(do ([i (length list) (- i 1)])
((zero? i))
(define l (list-ref list i))
(define item (last-element-on-list l))
(cond ((= (cdr l '+)) (set! plus-list (cons list plus-list)))
((= (cdr l '-)) (set! minus-list (cons list minus-list))))
)
(cons plus-list minus-list)
)
)
)
)
And instead of using (list-ref lst i) inside de do I have defined a variable l:
(define (list-ref lst i) l)
But it seems that I cann't do that, because I get the error:
define: not allowed in an expression context in: (define l (list-ref
lst i))
But there are a lot of define inside the do.
If I remove all the define inside the do, I have to write a lot of code and it is not easier to read and understand:
(define split
(lambda (list)
(define plus-list '())
(define minus-list '())
(cond ((null? list) '())
(else
(do ([i (length list) (- i 1)])
((zero? i))
(cond ((= (cdr (last-element-on-list (list-ref list i)) '+)) (set! plus-list (cons (list-ref list i) plus-list)))
((= (cdr (last-element-on-list (list-ref list i)) '-)) (set! minus-list (cons (list-ref list i) minus-list))))
)
(cons plus-list minus-list)
)
)
)
)
How can I define a variable inside a do?
Reading your other question I see why you write the bolded expressions -
…
(cond ((= (cdr (last-element-on-list (list-ref list i)) '+))
(set! plus-list
(cons (list-ref list i) plus-list)))
((= (cdr (last-element-on-list (list-ref list i)) '-))
(set! minus-list
(cons (list-ref list i) minus-list))))
…
Your input list shown there is –
(define lst
'((n 25 f +)
(s 25 m +)
(ll 20 no -)))
Your split is inspecting the contents of each element of l. split has overstepped its boundaries and now it only works for lists containing elements of this particular structure. Along with set!, lack of else in a cond is typically an indication you're doing something wrong. You also call (cdr (last-element-of-list ...)). If last-element-of-list returns an atom, cdr would throw an error here.
Consider designing split in a more generic way –
(define (split proc l)
(define (loop l true false)
(cond ((null? l)
(cons true false))
((proc (car l))
(loop (cdr l)
(cons (car l) true)
false))
(else
(loop (cdr l)
true
(cons (car l) false)))))
(loop l '() '()))
(split (lambda (x) (> x 5))
'(1 5 3 9 7 0 8 3 2 6 4))
;; '((6 8 7 9) 4 2 3 0 3 5 1)
If our list contains different elements, we can still use the same split procedure –
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +)))
;; '(((4 +) (3 +) (2 +) (1 +)) (3 -) (2 -) (1 -))
I think it's never too early to start learning continuation passing style. Below, return represents our continuation and defaults to cons, the same procedure we used to return the final result in our original implementation. Intuitively, a continuation represents "the next step" of the computation –
(define (split proc l (return cons)) ;; `return` is our continuation
(if (null? l)
;; base case: list is empty, return empty result
(return '() '())
;; inductive case: at least one `x`
(let* ((x (car l))
(bool (proc x)))
(split proc ;; tail recur with our proc
(cdr l) ;; ... a smaller list
(lambda (t f) ;; ... and "the next step"
(if bool ;; if `(proc x)` returned true
(return (cons x t) ;; ... cons the `x` onto the `t` result
f) ;; ... and leave the `f` the same
(return t ;; otherwise leave `t` the same
(cons x f)))))))) ;; ... and cons the `x` onto the `f` result
If we run our split procedure, you'll notice we get the same exact output as above. At first glance it looks like we made a mess of a nice program, however there's one distinct advantage of this implementation. Because the continuation is user-configurable, instead of cons, we could decide an entirely different fate for our two lists, t and f –
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
(lambda (plus minus)
(printf "plus: ~a, minus: ~a\n" plus minus)))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
Note how plus and minus were given the respective results. We didn't have to pick apart an intermediate cons result. More intuitively, we want printf to be "the next step", but we only need to specify the first argument –
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
(curry printf "plus: ~a, minus: ~a\n"))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
Now we've scratched the surface of functional style :D
do loops are not idiomatic Racket. They are inherited from Scheme, and for whatever reason, they don’t permit internal definitions. I have never once used a do loop in Racket, since the for comprehensions are more functional, and they’re just generally easier to work with. Plus, since they originate in Racket, not in Scheme, they support internal definitions as you’d expect.
You could write your split function using for/fold instead of do, which has the added advantage of not needing to use set! (and avoiding the quadratic access time of using list-ref instead of iterating through the list). I’m not completely sure what your split function is supposed to do, as even with the internal definition removed, it does not compile, but here’s my best guess at what you might be attempting to do:
(define (split lst)
(for/fold ([plus-lst '()]
[minus-lst '()])
([l (in-list lst)])
(define item (last l))
(cond
[(equal? item '+)
(values (cons l plus-lst) minus-lst)]
[(equal? item '-)
(values plus-lst (cons l minus-lst))]
[else
(values plus-lst minus-lst)])))
Aside from the obvious restructuring to use for/fold instead of do, this code also makes the following changes over your code:
It uses the built-in last function from racket/list to get the last element of a list.
It uses equal? instead of = to compare symbols, since = is specifically for comparing numbers.
It indents things properly and puts close parentheses in idiomatic locations.
I fixed your code using let, read documentation about let it's heavily used in Scheme/Racket. I haven't used Scheme lately so I couldn't explain it as well as it is in documentation.
Shortly it's local symbol definition/redefinition, and you can use symbol with value only in let body.
Short example on let
(define x 5)
(let ((x 10))
(display x)) # => 10
(display x) # => 5
(let ((y 1))
(display y)) # => 1
(display y) # = => (error) y: undefined
Your code fixed using let
(define split
(lambda (list)
(let ((plus-list '())
(minus-list '()))
(cond ((null? list) '())
(else
(do ([i (length list) (- i 1)])
((zero? i))
(let ((l (list-ref list i))
(item (last-element-on-list l)))
(cond ((= (cdr l '+)) (set! plus-list (cons list plus-list)))
((= (cdr l '-)) (set! minus-list (cons list minus-list))))))
(cons plus-list minus-list))))))
Here is a task: a list is given, some of the elements are also lists. It's nescessary to replace the nested lists with the sum of the numbers in them, if all of them are even, using recursion. For example:
(1 2 NIL (2 4 6) 5 7) -> (1 2 NIL 12 5 7)
if the parent list matches the condition after the transformation:
(2 2 (4 4) (2 2)) -> (2 2 8 4) -> 16
Now i have the following code:
;; check for all list elements are even
(defun is-even-list (lst)
(cond ((null lst) t)
((and (numberp (car lst)) (evenp (car lst))) (is-even-list (cdr lst)))
(t nil)
)
)
;; list summing
(defun sum-list (lst)
(cond ((null lst) 0)
(t (+ (car lst) (sum-list (cdr lst))))
)
)
;; main func
(defun task (lst)
(cond ((null lst) nil)
((atom (car lst)) (cons (car lst) (task (cdr lst))))
((is-even-list (car lst)) (cons (list (sum-list (car lst))) (task (cdr lst))))
(t (cons (task (car lst)) (task (cdr lst))))
)
)
But now it processes only the “lowest” level of the list if it exists:
(2 4) -> (2 4)
(2 (2 4 6) 6) -> (2 12 6)
(2 (4 (6 8) 10) 12) -> (2 (4 14 10) 12)
(2 (4 6) (8 10) 12) -> (2 10 18 12)
How can i change this code to get "full" processing?
It's definitely not the best solution but it works:
(defun is-even-list (lst)
(cond ((null lst) t)
((and (numberp (car lst)) (evenp (car lst))) (is-even-list (cdr lst)))
(t nil)
)
)
(defun sum-list (lst)
(cond ((null lst) 0)
(t (+ (car lst) (sum-list (cdr lst))))
)
)
(defun test (lst)
(dotimes (i (list-length lst))
(cond
((not (atom (nth i lst))) (setf (nth i lst) (test (nth i lst))))
)
)
(cond
((is-even-list lst) (setf lst (sum-list lst)))
((not (is-even-list lst)) (setf lst lst))
)
)
Here's a solution which I think meets the requirements of the question: recursively sum a list each element of which is either an even number or a list meeting the same requirement. It also does this making only a single pass over the structure it is trying to sum. For large lists, it relies on tail-call elimination in the implementation which probably is always true now but is not required to be. sum-list-loop could be turned into something explicitly iterative if not.
(defun sum-list-if-even (l)
;; Sum a list if all its elements are either even numbers or lists
;; for which this function returns an even number. If that's not
;; true return the list. This assumes that the list is proper and
;; elements are numbers or lists which meet the same requirement but
;; it does not check this in cases where it gives up for other
;; reasons first: (sum-list-if-even '(2 "")) signals a type error
;; (but (sum-list-if-even '(1 "")) fails to do so)
(labels ((sum-list-loop (tail sum)
(etypecase tail
(null sum) ;all the elements of '() are even numbers
(cons
(let ((first (first tail)))
(etypecase first
(integer
;; Easy case: an integer is either an even number
;; or we give up immediately
(if (evenp first)
(sum-list-loop (rest tail) (+ sum first))
;; give up immediately
l))
(list
;; rerurse on the car ...
(let ((try (sum-list-if-even first)))
;; ... and check to see what we got to know if
;; we should recurse on the cdr
(if (not (eq try first))
(sum-list-loop (rest tail) (+ sum try))
l)))))))))
(sum-list-loop l 0)))
Allow me to show some improvements on your own answer.
First, use conventional formatting: no dangling parentheses, bodies indented two spaces, other argument forms aligned. Use appropriate line breaks.
(defun is-even-list (lst)
(cond ((null lst) t)
((and (numberp (car lst))
(evenp (car lst)))
(is-even-list (cdr lst)))
(t nil)))
(defun sum-list (lst)
(cond ((null lst) 0)
(t (+ (car lst)
(sum-list (cdr lst))))))
(defun test (lst)
(dotimes (i (list-length lst))
(cond ((not (atom (nth i lst)))
(setf (nth i lst) (test (nth i lst))))))
(cond ((is-even-list lst) (setf lst (sum-list lst)))
((not (is-even-list lst)) (setf lst lst))))
The first function checks two things: that every element is a number, and that every element is even. In this context, the first condition mainly means: no sublists.
(defun flat-all-even-p (list)
(and (every #'numberp list)
(every #'even list)))
The second function sums a list and assumes that all elements are numbers (sublists would signal an error here).
(defun sum (list)
(reduce #'+ list))
The third function does not test, it sums. Note that it only accidentally returns the answer, since setf returns the value it sets. Another problem is that you do index lookup on lists in a loop, which is very inefficient. Finally, you modify the list you were given, which will surprise your caller.
(defun sum-if-all-even (tree)
(if (listp tree)
(let ((recursed-tree (mapcar #'sum-if-all-even tree)))
(if (flat-all-even-p recursed-tree)
(sum recursed-tree)
recursed-tree))
tree)
I am trying to list sucessive averages of elements in a list in reverse, for example in a list of (1 2 3) i try to get (3+2+1/3 2+1/2 1) here is an example of a list i tried my code on.
(list 2 3 4 5 6)
For some reason the values returned are " (4 7/2 4 7/2 4)". This is the code
(define (sucessive-avg lst)
(if (=(length lst) 1)
lst
(cons(avg(reverse lst)) (sucessive-avg(cdr(reverse lst))))))
The problem does not come from my avg so i dont know what is wrong with it
How many times will you call reverse? Try to do a step-by-step execution of your code to understand what is going on.
You could write a function which collect averages for the successives CDRs of a list, and call it with a reversed list:
(define (successive-averages lst)
(if (null? lst)
lst
(cons (avg lst)
(successive-averages (rest lst))))
(successive-averages (reverse (list 2 3 4 5 6)))
#coredump's answer is a pretty good start but it doesn't use proper tail recursion and it looks like it's averaging the wrong parts of the list
for example in a list of (1 2 3) i try to get (3+2+1/3 2+1/2 1)
For a list of '(2 3 4 5 6) my answer will give you
'((avg '(2 3 4 5 6))
(avg '(2 3 4 5))
(avg '(2 3 4))
(avg '(2 3))
(avg '(2)))
This seems to match your description more accurately.
Here's the code
(define (avg lst)
(/ (foldl + 0 lst)
(length lst)))
(define (successive-avg lst)
(define (iter res lst)
(if (empty? lst)
(reverse res)
(iter (cons (avg lst) res) (rest lst))))
(iter '() (reverse lst)))
(print (successive-avg '(2 3 4 5 6)))
Output
'(4 3 1/2 3 2 1/2 2)
You don't actually need to recall reverse or average at all. The reason you don't need average is that you can keep a running sum (in my code via a lambda chain) and an explicit count. And sense you want the result of the deepest recursion first, consing running average onto an acumulator as you recure gives you the right final order.
(define (successiveAvg Lst)
(cond ((null? Lst)
(error "succesiveAvg requires a non-empty list"))
((fold (lambda (acc x)
(if acc (number? x) #f))
#t
Lst)
(error "succesiveAvg requires a list of numbers" Lst))
(else
(let ((inc (lambda (x)
(+ 1 x)))
(f (lambda (x)
(lambda (y) (+ x y)))))
(let loop ((acc '())
(c (lambda (x) x))
(L lst)
(i 1))
(if (null? L)
acc
(loop (cons (/ (c (car L)) i)
acc)
(f (c (car L)))
(cdr L)
(inc i))))))))
(successiveavg '(2 3 4 5 6))
;Value 3: (4 7/2 3 5/2 2)