0

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 Answers2

1

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)
Óscar López
  • 232,561
  • 37
  • 312
  • 386
1

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.

soegaard
  • 30,661
  • 4
  • 57
  • 106
  • I have a question about the code, does the code compute each value in the list L only once. I don't want any values in the list to be computed more than once. I'm fairly confident that this code isn't doing that but I just want ensure I'm correct in my assumption, regardless the answer is great I'm just trying to better understand imperative programming with Racket – CanadianBeaver Jul 30 '19 at 23:27
  • Yes. Only once. – soegaard Jul 30 '19 at 23:31
  • However the smallest and greatest values are computed each time you call scale. So you can compute them first (in a local definition) and then use them in the definition of scale. – soegaard Jul 30 '19 at 23:36
  • How do I store these values as constants though, I changed my code to (define largest-val (foldr max (first L) (rest L))) and (define smallest-val (foldr min (first L) (rest L))) and (define (scale t) (/ (- t (smallest-val)) (- (largest-val) (smallest-val)))) and the code isn't working so I'm wondering if I have to change the definition of the largest-val and smallest-val code – CanadianBeaver Jul 31 '19 at 00:07
  • I should've mentioned that the error I received using the first example is function call: expected a function after the open parenthesis, but received 20 – CanadianBeaver Jul 31 '19 at 00:23
  • The error says it all. With `(define smallest-val (foldr min (first L) (rest L))) ` you compute a value, here 20, and `smallest-val` is now bound to 20. Then later you write `(smallest-val)` which now means `(20)`. But in Racket a parentheses starts a function call and the first thing in a function call must be a function. -- The solution is remove the parentheses: `(define (scale t) (/ (- t smallest-val) (- largest-val smallest-val))) ` – soegaard Jul 31 '19 at 10:16