usernvk
usernvk

Reputation: 187

Simply Scheme. Chapter 08. Higher—Order Functions

Greets,

Summary

having trouble passing '(+) or '(-) as data to a cond (non evaluated). On their own, they return (+) or (-) which, as an argument returns the identity element (0).

HELP!

Background.

For the non standard scheme in the code.

In this book; sentences are flat lists and words are sybmols and strings. There are three higher order functions/procedures in simply.scm, part of the library to illustrate the topic, every, keep and accumulate;

  1. (every function data) [do this function to every element of data]
  2. (keep predicate? data) [keep the elements of data that pass predicate? test]
  3. (accumulate function data) [collect all data into the form of function — combine with keep to remove invalid data] eg (accumulate + (keep number? data)) [remove non numbers then add the remaining numbers together, zero if no numbers found]

Data Flow.

Exercise 8.11 is a gpa calculator procedure. By instruction, no lambda or recursion allowed (not yet taught if read sequentially).

The first implementation I tried takes multiple grades in a single sentence and outputs individual sentences, each with a single grade. It then passes this output to a helper procedure.

If the single grade output has a + or - it is separated, for example '(a+) into '(a) and '(+) and all output is then passed to a further helper procedure.

then a cond allocates scores

a 4
b 3
c 2
d 1
e 0
+ 0.33
- -0.33

This, only worked in my head (why don't computers work like minds?) When a grade like '(a+) or '(a-) is seperated, the '(a) is processed properly but the '(+) or '(-) evaluate to the identity element (0) and fail to add to the gpa.

Is there a way to make '(+) and '(-) passable as data instead of as an expression? Alternatively, can I convert them to some arbitrary data usable in the cond before they return (0)?

The current version, a lengthy cond for each grade, works, but is hideous. Makes the implementation feel like imperative instead of functional programming.

Code.

returns the wrong gpa (doesn't add 0.33 or -0.33): also, input type check in (gpa-helper) failed spectacularly.

(define (gpa gradesset)
    (/ (accumulate + (every gpa-helper gradesset)) (count gradesset)) )

(define (gpa-helper gradewrd)
    (cond   ((or (< (count gradewrd) 1) (> (count gradewrd) 2)) '(Please use valid grade input))
            ((= (count gradewrd) 1) (gpa-allocator (keep valid-grade? gradewrd)))
            ((= (count gradewrd) 2) (every gpa-helper (keep valid-grade? gradewrd)))
            (else '(Please check that all grades entered are valid)) ) )

(define (gpa-allocator gradeletter+-)
    (cond   ((equal? gradeletter+- 'a) 4)
            ((equal? gradeletter+- 'b) 3)
            ((equal? gradeletter+- 'c) 2)
            ((equal? gradeletter+- 'd) 1)
            ((equal? gradeletter+- 'e) 0)
            ((equal? gradeletter+- +) .33)
            ((equal? gradeletter+- -) (- .33))
            (else 0) ) )

(define (valid-grade? gradein)
    (if (member? gradein '(+ - a+ a a- b+ b b- c+ c c- d+ d d- e)) #t #f) )

redone version that returns a sentence of the individual scores. The 0 returned by '(+) and '(-) is visible here. Implements successful input type checking but introduces new problems. (accumulate + ing the result for one)

(define (gpa gradesset)
    (every gpa-cleaner gradesset) )

(define (gpa-cleaner gradewrd)
    (cond   ((or (< (count gradewrd) 1) (> (count gradewrd) 2)) 0)
            (else (every gpa-accumulator gradewrd)) ) )

(define (gpa-accumulator gradewrd)
    (/ (accumulate + (every gpa-helper gradewrd)) (count gradewrd)) )

(define (gpa-helper gradewrd)
    (cond   ((= (count gradewrd) 1) (gpa-allocator (keep valid-grade? gradewrd)))
            ((= (count gradewrd) 2) (every gpa-helper (keep valid-grade? gradewrd)))
            (else '(Please check that all grades entered are valid)) ) )

(define (gpa-allocator gradeletter+-)
    (cond   ((equal? gradeletter+- 'a) 4)
            ((equal? gradeletter+- 'b) 3)
            ((equal? gradeletter+- 'c) 2)
            ((equal? gradeletter+- 'd) 1)
            ((equal? gradeletter+- 'e) 0)
            ((equal? gradeletter+- +) .33)
            ((equal? gradeletter+- -) (- .33))
            (else 0) ) )

(define (valid-grade? gradein)
    (if (member? gradein '(+ - a b c d e)) #t #f) )

Using SCM version 5e7 with Slib 3b3, the additional libraries supplied with Simply Scheme (link provided under background above — simply.scm, functions.scm, ttt.scm, match.scm, database.scm) and the library where I input my answers for every exercise loaded.

Upvotes: 1

Views: 485

Answers (2)

WorBlux
WorBlux

Reputation: 1413

Oscar is right about what's wrong, but his solution uses functions not used within the simply scheme book.

Here;s my solution from when I went through that chapter in that book

(define (gpa l-grades);;letter grades
    (/  (accumulate + (every grade-value-mapper l-grades))
        (count l-grades)
 )   )



(define (grade-value-mapper l-grade)
    (let ((grade (first l-grade))
        (g-mod (lambda (x) 
            (cond   ((equal? '+ (bf l-grade))
                    (+ 1/3 x))
                ((equal? '- (bf l-grade))
                    (- 1/3 x))
                (else x)
        ))     )  )
        (cond   ((equal? (first grade) 'a) (g-mod 4))
            ((equal? (first grade) 'b) (g-mod 3))   
            ((equal? (first grade) 'c) (g-mod 2))   
            ((equal? (first grade) 'd) (g-mod 1))   
            (else 0)
 )   )   )  

Not my best work but hope it helps. The gmod you could pull out into it's own define. You would call it like so ((gmod l-grade) 4)

Or pull out more abraction

((gmod l-grade) (letter-value (first l-grade)))

I don't think the (let ... (grade ...) ...) is really doing much good. what's passed to grade-value-mapper is a single grade.

You could add the input cleaner/checker into the function grade-value-mapper as the first cond clause.

Upvotes: 1

&#211;scar L&#243;pez
&#211;scar L&#243;pez

Reputation: 236142

If you need to pass + or - as a symbol (not as a procedure), you have to quote it first:

'+
'-

For example:

((equal? gradeletter+- '+) .33)
((equal? gradeletter+- '-) -.33)

But from the context, I don't think the gpa-allocator procedure is correct. A grade can be a or a+, the conditions imply that + or - are actual grades, which is wrong.

Maybe you should represent grades as strings and check (using string-ref) the first character in the string to determine if it's #\a, #\b, #\c, #\d, #\e and (if the string's length is greater than 1) test if the second character in the string is either #\+ or #\-. Then you can determine the appropriate value of the grade by adding the two values. Alternatively, you could pass the grade as a symbol and convert it to string. This is what I mean:

(define (gpa-allocator gradeletter+-)
  (let ((grade (symbol->string gradeletter+-)))
    (+ (case (string-ref grade 0)
         ((#\a #\A) 4)
         ((#\b #\B) 3)
         ((#\c #\C) 2)
         ((#\d #\D) 1)
         ((#\e #\E) 0)
         (else 0))
       (if (> (string-length grade) 1)
           (case (string-ref grade 1)
             ((#\+) 0.33)
             ((#\-) -0.33)
             (else 0))
           0))))

Don't forget to test it:

(gpa-allocator 'A)
=> 4.0
(gpa-allocator 'A+)
=> 4.33
(gpa-allocator 'A-)
=> 3.67

Upvotes: 2

Related Questions