barerd
barerd

Reputation: 855

How to stop a conditional statement in racket without using error?

I am trying to implement the guess my number game in racket, and in a functional way, for educational purposes. My problem is that I have to stop the function with an error as in (error "YOU GUESSED!"). The error function is what I could find in the racket documentation. Is there an errorless way to stop this conditional?

Note: I read about stop-when, but I don't want to go into big-bang yet.

(define guess-my-number (lambda ()
  (letrec ([my-number (random 100)]
           [choices-left 7]
           [game (lambda (choices-left)
                (if (> choices-left 0)
                    (let ([user-guess (read)])
                      (cond [(> user-guess my-number) (display "That was too high!")]
                            [(< user-guess my-number) (display "That was too small!")]
                            [(= user-guess my-number) (error "YOU GUESSED!")])
                    (game (- choices-left 1)))
                    (display "Here ends the game!!")))])
    (game choices-left))))

Upvotes: 1

Views: 867

Answers (2)

Greg Hendershott
Greg Hendershott

Reputation: 16260

Chris' answer is perfect. This is really a comment -- except it's a bit long and you can't do code blocks in comments here.

The way you're coding it is a great way to understand how Racket works at a "low level", and is a very traditional way to code this in Scheme.

Another traditional way in Scheme is to use a couple features:

  • The shorthand way to define a function.

  • A named let, using the typical name loop.

That version:

(define (guess-my-number-v2)
  (let ([number (random 100)])
    (let loop ([guesses-left 7])
      (cond [(> guesses-left 0)
             (define user-guess (read))
             (cond [(> user-guess number)
                    (display "That was too high!")
                    (loop (sub1 guesses-left))]
                   [(< user-guess number)
                    (display "That was too low!")
                    (loop (sub1 guesses-left))]
                   [else (display "YOU GUESSED!")])]
            [else
             (displayln "Here ends the game!!")]))))

What's interesting is that this expands to almost exactly the same thing as your first version.

The (let loop (guesses-left 7]) ...) form is essentially defining a function named loop, much like your original one named game. (In fact you could say (let game ([guesses-left 7]) ...), too.)

One benefit is that it's not as deeply indented and doesn't march off the right side of the screen.

Finally here's a version that's a bit more "Racket-y" in making more use of define:

(define (guess-my-number-v3)
  (define number (random 100))
  (define (guess guesses-left)
    (cond [(> guesses-left 0)
           (define user-guess (read))
           (cond [(> user-guess number)
                  (display "That was too high!")
                  (guess (sub1 guesses-left))]
                 [(< user-guess number)
                  (display "That was too low!")
                  (guess (sub1 guesses-left))]
                 [else (display "YOU GUESSED!")])]
          [else
           (displayln "Here ends the game!!")]))
  (guess 7))

Although this is in more of a "modern" Racket style, it's not inherently "better". It, too expands to almost exactly the same thing as the original. In fact most Racket programmers would find all 3 styles easy to understand.

Anyway, all 3 versions "loop" by recursively calling a function, as you were already doing. The way to "terminate the loop" is simply... don't call the function again. That's the key point in Chris' answer.

Upvotes: 2

C. K. Young
C. K. Young

Reputation: 223133

Yes. Do the (game (- choices-left 1)) recursion inside the cond branches where you want to reloop:

(define guess-my-number (lambda ()
  (letrec ([my-number (random 100)]
           [choices-left 7]
           [game (lambda (choices-left)
                   (if (> choices-left 0)
                       (let ([user-guess (read)])
                         (cond [(> user-guess my-number) (display "That was too high!")
                                                         (game (- choices-left 1))]
                               [(< user-guess my-number) (display "That was too small!")
                                                         (game (- choices-left 1))]
                               [(= user-guess my-number) (display "YOU GUESSED!")]))
                       (display "Here ends the game!!")))])
    (game choices-left))))

Upvotes: 1

Related Questions