Assigning variables in Scheme without using set

288 views Asked by At

I am trying to create a global score in my scheme program and it will either increase by 1, decrease by 1 or not change for each iteration of my function. For example, I have this in my function when I want the score to increase by 1:

(set! score (+ score 1)))

I am able to do this using set! but I need a way to achieve it without using the set! operation. Any help would be appreciated!

2

There are 2 answers

0
AudioBubble On

The trick is to realise that you can replace something like this:

(define (my-game ...)
  (let ([score 0])
    (some-kind-of-looping-construct
     ...
     (if <not-done>
         (begin
           (set! score <new-score-value>)
           (continue-loop))
         score))))

With something like this:

(define (my-game ...)
  (define (loop ... score ...)
    ...
    (if <not-done>
        (loop ... <new-score-value> ...)
        score))
  (loop ... 0 ...))

In particular you can replace any kind of looping construct which repeatedly assigns to some variable by textually (but not actually) recursive calls to some function which repeatedly binds a variable.

To be concrete about this let's imagine we have a looping construct which looks like

(loop exit-fn form ...)

So loop is the loop construct, and exit-fn is the magic thing we call to exit the loop.

And I'll assume there is some function run-game-round which takes a round number and the current score, runs a round of the game and returns the new score.

So using this construct we can write the skeleton of some kind of game loop:

(let ((round 0)
      (score 0))
  (loop exit
    (set! score (run-game-round round score))
    (set! round (+ round 1))
    (cond
      [(> score 100)
       (exit 'win)]
      [(> round 100)
       (exit 'lose)])))

And this is fairly horrible code.

But we can replace this code with this:

(begin
  (define (run-game round score)
    (cond [(> score 100)
           'win]
          [(> round 100)
           'lose]
          [else
           (run-game (+ round 1)
                     (run-game-round round score))]))
  (run-game 0 0))

And this code does exactly the same thing but there is no assignment anywhere.

0
tjorchrt On

No set!: We can input list or given number of args and use function recursive.

#lang racket
(define (play-game game-round-lst)
  (local [(define (make-score s)
            (cond
              [(equal? s 'win) 1]
              [(equal? s 'drew) 0]
              [(equal? s 'lose) -1]))]
    (apply + (map make-score game-round-lst))))

;;; TEST
(play-game '(lose win drew lose win)) ; 0
(play-game '(win lose drew win lose win lose)) ; 0

Use set! in local function. In this method you can build new game without worry about lost state in each game.

#lang racket
(define (make-game)
  (local [(define score 0)
          (define (setup-score n)
            (set! score (+ n score)))
          (define (service-manager msg)
            (cond
              [(equal? msg 'win)
               (setup-score 1)]
              [(equal? msg 'drew)
               (setup-score 0)]
              [(equal? msg 'lose)
               (setup-score -1)]
              [(equal? msg 'show-score)
               score]))]
    service-manager))

;;; TEST
(define game1 (make-game))
(define game2 (make-game))
(game1 'win)
(game2 'win)
(game1 'drew)
(game2 'drew)
(game1 'lose)
(game2 'lose)
(game1 'show-score) ; 0
(game2 'show-score) ; 0