Issues with conditionals in Scheme

2019-09-18 17:41发布

问题:

I'm using Scheme with the full Swindle package, and I'm trying to use conditionals to recursively determine evenness/oddity of integers. My code is as follows:

(define (odd? x)(
            (cond 
              ((= x 0) '#f)
              ((= x 1) '#t)
              ((= (even? (- x 1)) #t) '#f)
              (else '#t))))

(define (even? x)(
              (cond
                ((= x 0) '#f)
                ((= x 2) '#t)
                ((= (odd? (- x 1)) #t) '#f)
                (else '#t))))

However, when I run (even? x) or (odd? x) [x is some number, doesn't matter what, as I get the same error] I get: application: not a procedure; expected a procedure that can be applied to arguments given: #t arguments...: [none]

Can anyone help? Thanks. I'm not 100% familiar with ideal Scheme syntax, so it might be that.

回答1:

You have an erroneous pair of parentheses surrounding the cond expression (that's causing the error reported). But also, there are way too many conditions in each procedure, and because you're using = to compare numbers with boolean values, there will be a point where a contract violation will occur. For fixing that you can replace = with equal? in here:

((equal? (even? (- x 1)) #t) '#f)

And in here:

((equal? (odd? (- x 1)) #t) '#f)

But then, the procedures will still give an incorrect result:

(even? 5)
=> #t
(odd? 7)
=> #f

Honestly, I think it'd better to simplify the implementation, that will solve all the problems. Try this instead:

(define (odd? x)
  (cond ((= x 0) #f)
        (else (even? (- x 1)))))

(define (even? x)
  (cond ((= x 0) #t)
        (else (odd? (- x 1)))))

Now we'll get correct answers:

(even? 4)
=> #t
(even? 5)
=> #f
(odd? 6)
=> #f
(odd? 7)
=> #t


回答2:

Any Scheme form in parentheses is an application, like (sin 42) or (42 sin). The first calls sin as a function with argument 42 and produces a value; the second tries to call 42 with an argument sin, but 42 is no procedure, so this causes an error. Similarly (42) is an application with no arguments; still it must have a procedure to be applied as its first part, to be called with no arguments, right? In Scheme, parentheses matter, they are not just for grouping stuff (as they may be in some other languages). So the extra parentheses cause an extra attempt to evaluate the result, which is a Boolean, i.e. not a procedure, so this is an error.

Then, '#f is just #f; similarly for #t (they both evaluate to themselves); and a condition (= test #t)1 (equal? test #t) is the same as just test (produces the same set of Boolean results) ((1 we can't use = to compare Booleans, it is supposed to be used with numbers only)):

(define (odd? x)
        (cond 
          ((= x 0) #f)
          ((= x 1) #t)
          ((even? (- x 1)) #f)
          (else #t)))

Also, (if test #f else...) is the same as (if (not test) else... #f):

(define (odd? x)
        (cond 
          ((= x 0) #f)
          ((= x 1) #t)
          ((not (even? (- x 1))) #t)
          (else #f)))                 ; (else ...) is like (#t ...)

using logical connectives, the clauses with the same outcome can be merged, and mutually exclusive clauses can be rearranged (mutual exclusivity can be achieved with explicit guards):

(define (odd? x)
        (cond 
          ((and (/= x 0)              ; a guard
                (or (= x 1) 
                    (not (even? (- x 1))))) #t)
          ((or (= x 0) #t) #f)))

but (cond (test #t) (else #f) is (if test #t #f) which is just test:

(define (odd? x) (and (> x 0)         ; `>` is better
                      (or (= x 1) 
                          (not (even? (- x 1))))))

The guard (> x 0) prevents the fall-through for negative xes.

But then, this is completely wrong. For n to be odd, n-1 must be even, not the opposite!

(define (odd? x) (and (> x 0)         ; work only for positive integers
                      (or (= x 1) 
                          (even? (- x 1)))))

We could write (not (odd? (- x 1))), but then it wouldn't be tail recursive. It would be tail recursive modulo cons (not serving as a "constructor"), but for some historic fluke of a reason2, TRMC optimization isn't required of a Scheme implementation. The last expression in and and or forms is, in fact, in tail position. But not in not. ((2 despite being described as early as 1974.))

Repeat the same for your definition of even?.



标签: scheme