Reputation: 855
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
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
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