Scheme - How to find the median using user defined sort and average functions? - scheme

I'm new to Scheme, and I've hit a wall. I have my sort and average functions, and I'm trying to change a median function I found on this site. However, no matter what I try, I keep getting errors where I have more than one expression in the median function, or when I try to use sort in the median function it's "undefined".
(define (sort1 L)
(if (or (null? L) (<= (length L) 1)) L
(let loop ((l null) (r null)
(pivot (car L)) (rest (cdr L)))
(if (null? rest)
(append (append (sort1 l) (list pivot)) (sort1 r))
(if (<= (car rest) pivot)
(loop (append l (list (car rest))) r pivot (cdr rest))
(loop l (append r (list (car rest))) pivot (cdr rest)))))))
(define (avg lst)
(let loop ((count 0) (sum 0) (args lst))
(if (not (null? args))
(loop (add1 count) (+ sum (car args)) (cdr args))
(/ sum count))))
(define (median L)
(if (null? L) (error "The list is empty")
(let loop ((L1 L) (L2 L))
(cond ((null? (cdr L2)) (car L1))
((null? (cddr L2)) (list (car L1) (cadr L1)))
(else (loop (cdr L1) (cddr L2)))))))
I'm trying to edit the median function to first sort the list, and if there are an even number of elements, I need to take the average of the list, and use the element closest to the average.
Any help would be appreciated, thank you in advance.

Like I said in a comment, what you want isn't a let, it's function composition.
Your current median function is this:
(define (median L)
(if (null? L)
(error "The list is empty")
(let loop ((L1 L) (L2 L))
(cond ((null? (cdr L2)) (car L1))
((null? (cddr L2)) (list (car L1) (cadr L1)))
(else (loop (cdr L1) (cddr L2)))))))
But as Oscar Lopez pointed out, this doesn't properly compute the median. However, it does some of the work, so keep it. Rename it to median-helper or something.
(define (median-helper L)
(if (null? L)
(error "The list is empty")
(let loop ((L1 L) (L2 L))
(cond ((null? (cdr L2)) (car L1))
((null? (cddr L2)) (list (car L1) (cadr L1)))
(else (loop (cdr L1) (cddr L2)))))))
Then you can use function composition to define the "real" median function:
(define (median lst)
(median-helper (sort1 lst)))
This returns the middle element for odd-length lists, and the middle-two elements for even length lists. If this is want you wanted, great. If not, then you can fix median-helper by returning the average in the second case of the cond. So instead of (list (car L1) (cadr L1)) there, you would have (avg (list (car L1) (cadr L1))).
;; median-helper : (Listof Number) -> Number
(define (median-helper L)
(if (null? L)
(error "The list is empty")
(let loop ((L1 L) (L2 L))
(cond ((null? (cdr L2)) (car L1))
((null? (cddr L2)) (avg (list (car L1) (cadr L1))))
(else (loop (cdr L1) (cddr L2)))))))
;; median : (Listof Number) -> Number
(define (median lst)
(median-helper (sort1 lst)))

I think you're misunderstanding the definition of a median. A very simple (if not particularly efficient) implementation follows:
(define (my-sort L)
(sort L <))
(define (average x y)
(exact->inexact (/ (+ x y) 2)))
(define (median L)
(if (null? L)
(error "The list is empty")
(let* ((n (length L))
(sorted (my-sort L))
(half (quotient n 2)))
(if (odd? n)
(list-ref sorted half)
(average (list-ref sorted half)
(list-ref sorted (sub1 half)))))))
It works as defined:
(median '())
=> The list is empty
(median '(3 2 1 5 4))
=> 3
(median '(6 4 3 1 2 5))
=> 3.5

Related

Returning two parts of a list as a pair

In my program, I am supposed to write a function where it splits a list into even and odd. The problem is that the output/syntax is incorrect. I am getting ((1 3) (2 4)) when testing out the example (split '(1 2 3 4)). The output needs to look like ((1 3) 2 4)
Here is my code:
(define (split l)
(define (odd l)
(if (null? l) '()
(if (null? (cdr l)) (list (car l))
(cons (car l) (odd (cddr l))))))
(define (even l)
(if (null? l) '()
(if (null? (cdr l)) '()
(cons (cadr l) (even (cddr l))))))
(cons (odd l) (cons (even l) '())))
(even l) is already a list. You don't need to wrap it in an extra cons.
The code below should work.
(define (split l)
(define (odd l)
(if (null? l) '()
(if (null? (cdr l)) (list (car l))
(cons (car l) (odd (cddr l))))))
(define (even l)
(if (null? l) '()
(if (null? (cdr l)) '()
(cons (cadr l) (even (cddr l))))))
(cons (odd l) (even l)))

Rotating a list to the right in scheme

if this is rotating a list to the left:
(define (rotate-left l)
(if (null? l)
'()
(append (cdr l) (cons(car l) '()))))
How would I rotate a list to the right?
If you're ok with writing a helper function to find the last element, it's pretty easy to do a recursive implementation:
(define rotate-right
(lambda (lis full)
(if (null? (cdr lis))
(cons (car lis) (get-all-but-last full))
(rotate-right (cdr lis) full))))
(define get-all-but-last
(lambda (lis)
(if (null? (cdr lis))
'()
(cons (car lis) (get-all-but-last (cdr lis))))))
Here is a short non-recursive solution:
(define (rotate-right l)
(let ((rev (reverse l)))
(cons (car rev) (reverse (cdr rev)))))
And here is a iterative solution:
(define (rotate-right l)
(let iter ((remain l)
(output '()))
(if (null? (cdr remain))
(cons (car remain) (reverse output))
(iter (cdr remain) (cons (car remain) output)))))
(rotate-right '(1 2 3 4 5)) ;==> (5 1 2 3 4)

How to write a function that computes the maximum and minimum of a list of integers using Scheme

I am new to scheme. I am trying to find max and min of a list using scheme. Using "loop" I was able get the answer. Now I am trying different way to implement the same thing. I made some changes and for some reason I can't find what I am doing wrong.
;non working version
(define (min-max list1)
(let (ls list1) (max (car list1)) (min(car list1))
(cond
((null? ls)
(list "max: " max "min: " min))
((> (car ls) max)
(let ((car ls) max))
(min-max (cdr ls)))
((< (car ls) min)
(let ((car ls) min))
(min-max (cdr ls)))
(else
(min-max (cdr ls))))))
(define list1(list 1 2 3 4 ))
(display list1)
(newline)
(min-max list1)
;working version
(define (min list1)
(let loop((ls list1) (max (car list1)) (min(car list1)))
(cond
((null? ls)
(list "max: " max "min: " min))
((> (car ls) max)
(loop (cdr ls)(car ls) min))
((< (car ls) min)
(loop (cdr ls) max (car ls) ))
(else
(loop (cdr ls) max min)))))
(define list1(list 1 2 3 4 ))
(display list1)
(newline)
(min list1)
This isn't Code Review but I'll start with the "working" version of your procedure first. As is stands that procedure doesn't work for empty lists, so you should add a test for that. Then, no need to re-compare the first element of the list to min and max. Then, you seem to think that loop is a keyword, so I've changed the name to helper. Finally, I've modified it to be less repetitive:
(define (min-max lst)
(if (null? lst)
'()
(let helper ((lst (cdr lst)) (min (car lst)) (max (car lst)))
(if (null? lst)
(list min max)
(let ((c (car lst)))
(helper (cdr lst)
(if (< c min) c min)
(if (> c max) c max)))))))
which is the same as
(define (min-max lst)
(define (helper lst min max)
(if (null? lst)
(list min max)
(let ((c (car lst)))
(helper (cdr lst)
(if (< c min) c min)
(if (> c max) c max)))))
(if (null? lst)
'()
(helper (cdr lst) (car lst) (car lst))))
Testing:
> (min-max '(1 2 3 4))
'(1 4)
> (min-max '(1 8 2 3 4))
'(1 8)
> (min-max '())
'()
Your first procedure doesn't work because at each recursive call you re-initialize min and max. Also, since there is no test for the empty list, inevitably you end up taking the car of the empty list, which is not allowed in Scheme. Finally, it looks like you want to change the value of min and max outside the recursive call, which means you have to use set!. Here's a working version of that:
(define (min-max lst)
(if (null? lst)
'()
(let ((min (car lst)) (max (car lst)))
(define (helper lst)
(if (null? lst)
(list min max)
(let ((c (car lst)))
(when (< c min) (set! min c))
(when (> c max) (set! max c))
(helper (cdr lst)))))
(helper lst))))
This yields the same results, but you can see how much less elegant the code looks with set! as compared to the recursive call to helper.

Scheme quick-sort with filter

I need to write the function (quick-sort pred lst)
lst is the list of numbers to be sorted
pred is the predicate by which the list is ordered, the signature of this predicate is: (lambda (x y) …)
- (quick-sort < lst) will sort ascending (small to large)
- (quick-sort > lst) will sort descending (large to small)
- (quick-sort (lambda (x y) (< (car x) (car y))) lst) will sort a list
with inner lists according to the first element of the inner list, ascending.
I started with regular quick-sort:
(define (quick-sort lst)
(cond
((null? lst) '())
((= (length lst) 1) lst)
(else (append (quick-sort (filter (lambda (n) (< n (car lst))) lst))
(list (car lst))
(quick-sort (filter (lambda (n) (> n (car lst))) lst))))))
And now I'm trying to do this with pred:
(define (quick-sort pred lst)
(define (quick-sort-help lst)
(cond ((null? lst) ())
((= (length lst) 1) lst)
(else
(append (quick-sort-help (filter (lambda (n) (pred n (car lst))) lst))
(list (car lst))
(quick-sort-help (filter (lambda (n) (not(pred n (car lst)))) lst)))))) (quick-sort-help lst))
And I get an infinite recursion or something.
Can you help me solve this problem please?
Thanks!
First of you don't need the helper function quick-sort-help.
It recurs infinitely because you apply your helper function to lst instead cdr lst. In your regular quicksort you have (filter (lambda (n) (< n (car lst))) and (filter (lambda (n) (> n (car lst))). But then in the one with the predicate you have the problem that (not (pred ...) would cover the cases for <= and not < if the predicate is > and vice versa. So it gets stuck because the first element in the list is always equal with itself.
Here a correct quicksort:
(define (qsort f lst)
(if (null? lst)
null
(let ([pivot (car lst)])
(append (qsort f (filter (λ (n) (f n pivot)) (cdr lst)))
(list pivot)
(qsort f (filter (λ (n) (not (f n pivot))) (cdr lst)))))))

How to sort disorder list of numbers in scheme

What it the proper way to sort a list with values in Scheme? For example I have the values which are not ordered:
x1, x5, x32 .... xn
or
3, 4, 1, 3, 4, .. 9
First I want to for them by increase number and display them in this order:
x1, xn, x2, xn-1
or
1, 6, 2, 5, 3, 4
Any help will be valuable.
This is the same question you posted before, but with a small twist. As I told you in the comments of my answer, you just have to sort the list before rearranging it. Here's a Racket solution:
(define (interleave l1 l2)
(cond ((empty? l1) l2)
((empty? l2) l1)
(else (cons (first l1)
(interleave l2 (rest l1))))))
(define (zippy lst)
(let-values (((head tail) (split-at
(sort lst <) ; this is the new part
(quotient (length lst) 2))))
(interleave head (reverse tail))))
It works as expected:
(zippy '(4 2 6 3 5 1))
=> '(1 6 2 5 3 4)
This R6RS solution does what Chris Jester-Young proposes and it really is how to do it the bad way. BTW Chris' and Óscar's solutions on the same question without sorting is superior to this zippy procedure.
#!r6rs
(import (rnrs base)
(rnrs sorting)) ; list-sort
(define (zippy lis)
(let loop ((count-down (- (length lis) 1))
(count-up 0))
(cond ((> count-up count-down) '())
((= count-up count-down) (cons (list-ref lis count-down) '()))
(else (cons (list-ref lis count-down)
(cons (list-ref lis count-up)
(loop (- count-down 1)
(+ count-up 1))))))))
(define (sort-rearrange lis)
(zippy (list-sort < lis)))
Here is a simple, tail-recursive approach that uses a 'slow/fast' technique to stop the recursion when half the list is traversed:
(define (interleave l)
(let ((l (list-sort < l)))
(let merging ((slow l) (fast l) (revl (reverse l)) (rslt '()))
(cond ((null? fast)
(reverse rslt))
((null? (cdr fast))
(reverse (cons (car slow) rslt)))
(else
(merging (cdr slow) (cddr fast) (cdr revl)
(cons (car revl) (cons (car slow) rslt))))))))
So, you don't mind slow and just want a selection-based approach, eh? Here we go....
First, we define a select1 function that gets the minimum (or maximum) element, followed by all the other elements. For linked lists, this is probably the simplest approach, easier than trying to implement (say) quickselect.
(define (select1 lst cmp?)
(let loop ((seen '())
(rest lst)
(ext #f)
(extseen '()))
(cond ((null? rest)
(cons (car ext) (append-reverse (cdr extseen) (cdr ext))))
((or (not ext) (cmp? (car rest) (car ext)))
(let ((newseen (cons (car rest) seen)))
(loop newseen (cdr rest) rest newseen)))
(else
(loop (cons (car rest) seen) (cdr rest) ext extseen)))))
Now actually do the interweaving:
(define (zippy lst)
(let recur ((lst lst)
(left? #t))
(if (null? lst)
'()
(let ((selected (select1 lst (if left? < >))))
(cons (car selected) (recur (cdr selected) (not left?)))))))
This approach is O(n²), whereas the sort-and-interleave approach recommended by everybody else here is O(n log n).

Resources