How do I scale values all the values in a list where the largest value is 1 and the smallest value is 0

190 Views Asked by At

I am trying to scale all the values in a list where the largest value is 1 and the smallest value is 0. Here is an example of what I am trying to accomplish

(check-expect (squash (list 100 90 70 20)) (list 1 0.875 0.625 0)).

As you can see the largest value is 100 so it's scaled value is 1, the smallest value which is 20 is scaled down to 0.

To scale all these values down i'm performing the calculation Z=(Li - (smallest-val)) / ((largest-val) - (smallest-val))

where Li is all the values in the list (L1, L2, L3 ...)

Here is my code so far

(define (squash L)
  (local
    [
     ;;(largest-val M) returns the largest value M in L
     ;;(largest-val: (listof Num) -> Num
     (define (largest-val M) (foldr max (first M) (rest M)))

     ;;(smallest-val x) returns the smallest value x in L
     ;;(smallest-val: (listof Num) -> Num
     (define (smallest-val x) (foldr min (first x) (rest x)))
     ]
    (cond
      [(empty? L)'()]
      [else (cons (/ (- (first L) smallest-val) (- largest-val smallest-val))
                   (squash (rest L)))])))

Here is my error I am getting

:: -: expects a number as 2nd argument, given (lambda (a1) ...)

I'm unsure how to fix this code so my program works

I want to keep my solution using the imperative programming paradigm so I would prefer to keep my answer in the same format as it is now.

2

There are 2 best solutions below

6
soegaard On BEST ANSWER

Here is a variation of your function:

(define (squash L)
  (local
    [
     ;;(largest-val M) returns the largest value M in L
     ;;(largest-val: (listof Num) -> Num
     (define (largest-val M) (foldr max (first M) (rest M)))

     ;;(smallest-val x) returns the smallest value x in L
     ;;(smallest-val: (listof Num) -> Num
     (define (smallest-val x) (foldr min (first x) (rest x)))
     (define (scale x)
       (/ (- x                 (smallest-val L))
          (- (largest-val  L)  (smallest-val L))))]
    (map scale L)))

The function map applies the function scale to each element of the list L and returns a new list with all the results.

0
Óscar López On

Inside local you're defining both largest-val and smallest-val as procedures, but in the actual body of squash you're not calling them, instead you're using them as if they were numbers; that's what the -: expects a number as 2nd argument, given (lambda (a1) ...) error means.

There's a more serious problem, though. It seems that you intended to calculate the minimum and maximum value at each iteration, but that'll yield incorrect results. You must calculate those values only once - it'll be easier if we define a helper procedure, like this:

(define (squash L)
  (squash-helper L (apply min L) (apply max L)))

(define (squash-helper L minL maxL)
  (cond [(empty? L) '()]
        [else (cons (exact->inexact (/ (- (first L) minL) (- maxL minL)))
                    (squash-helper (rest L) minL maxL))]))

I used exact->inexact to get rid of the fractions, and there's a simpler way to find the minimum and maximum values of a list using apply together with min and max. Now the procedure works as expected:

(squash (list 100 90 70 20))
=> '(1.0 0.875 0.625 0.0)