[previous] [up] [next]     [index]
Next: Sorting Quickly Up: A New Form of Previous: A New Form of

Modeling a Ball on a Table

Let's consider the simple-looking problem of modeling the moves of a ball across a table. Assume the ball rolls at a constant speed until drops. We can model the table with a canvas of some fixed width and height. The ball is a disk that moves across the canvas, which we express with drawing the disk, waiting, and clearing it, until it is out of bounds.


;; TeachPack: draw.ss 

(define-struct ball (x y delta-x delta-y)) ;; A ball is a structure: ;; (make-ball number number number number)

;; draw-and-clear : a-ball -> true ;; draw, sleep, clear a disk from the canvas ;; structural design, Scheme knowledge (define (draw-and-clear a-ball) (and (draw-solid-disk (make-posn (ball-x a-ball) (ball-y a-ball)) 5 RED) (sleep-for-a-while DELAY) (clear-solid-disk (make-posn (ball-x a-ball) (ball-y a-ball)) 5 RED)))

;; move-ball : ball -> ball ;; to create a new ball, modeling a move by a-ball ;; structural design, physics knowledge (define (move-ball a-ball) (make-ball (+ (ball-x a-ball) (ball-delta-x a-ball)) (+ (ball-y a-ball) (ball-delta-y a-ball)) (ball-delta-x a-ball) (ball-delta-y a-ball)))

;; Dimension of canvas (define WIDTH 100) (define HEIGHT 100) (define DELAY .1)

Figure: Auxiliaries for move-until-out

Figure [cross-reference] collects the function, structure, data, and variable definitions that model the ball:

  1. A ball is a structure with four fields: the current position and the velocity in each direction. That is, the first two numbers in a ball structure are the current position on the canvas, and the next two numbers describe how far the ball moves in the two directiones per step.
  2. The function move-ball models the physical movement of the ball. It consumes a ball and creates a new one, modeling one step.
  3. The function draw-and-clear draws the ball at its current position, then waits for a short time, and clears it again.
The variable definitions specify the dimensions of the canvas and the delay time.

To move the ball a few times we can write

(define the-ball (make-ball 10 20 -5 +17))

(and (draw-and-clear the-ball) (and (draw-and-clear (move-ball the-ball)) ...))

though this gets tedious after a while. We should instead develop a function that moves the ball until it is out of bounds.

The easy part is to define out-of-bounds?, a function that determines whether a given ball is still visible on the canvas:

;; out-of-bounds? : a-ball -> boolean
;; to determine whether a-ball is outside of the bounds 
;; domain knowledge, geometry 
(define (out-of-bounds? a-ball) 
  (not 
   (and 
     (<= 0 (ball-x a-ball) WIDTH) 
     (<= 0 (ball-y a-ball) HEIGHT)))) 
We have defined numerous functions like out-of-bounds? in the first few sections of the book.

In contrast, writing a function that draws the ball on the canvas until it is out of bounds belongs to a group of programs that we haven't encountered thus far. Let's start with the basics of the function:

;; move-until-out : a-ball -> true
;; to model the movement of a ball until it goes out of bounds 
(define (move-until-out a-ball) ...) 
Because the function consumes a ball and draws its movement on a canvas, it produces true like all other functions that draw onto a canvas. Designing it with the recipe for structures makes no sense, however. After all, it is already clear how to draw-and-clear the ball and how to move it, too. What is needed instead is a case distinction that checks whether the ball is out of bounds or not.

Let us refine the function header with an appropriate cond-expression:

(define (move-until-out a-ball)
  (cond 
    [(out-of-bounds? a-ball) ...] 
    [else ...])) 
We have already defined the function out-of-bounds? because it was clear from the problem description that ``being out of bounds'' was a separate concept.

If the ball consumed by move-until-out is outside of the canvas's boundaries, the function can produce true, following the contract. If the ball is still inside the boundaries, two things must happen. First, the ball must be drawn and cleared from the canvas. Second, the ball must be moved, and then we must do things all over again. This implies that after moving the ball, we apply move-until-out again, which means the function is recursive:

;; move-until-out : a-ball -> true
;; to model the movement of a ball until it goes out of bounds 
(define (move-until-out a-ball) 
  (cond 
    [(out-of-bounds? a-ball) true] 
    [else (and (draw-and-clear a-ball) 
               (move-until-out (move-ball a-ball)))])) 
Both (draw-and-clear a-ball) and (move-until-out (move-ball a-ball)) produce true, and both expressions must be evaluated. So we combine them with an and-expression.

We can now test the function as follows:

(start WIDTH HEIGHT)
(move-until-out (make-ball 10 20 -5 +17)) 
(stop) 
This creates a canvas of proper size and a ball that moves left and down.

A close look at the function definition reveals two peculiarities. First, although the function is recursive, its body consists of a cond-expression whose conditions have nothing to do with the input data. Second, the recursive application in the body does not consume a part of the input. Instead, move-until-out generates an entirely new and different ball structure, which represents the original ball after one step, and uses it for the recursion. Clearly, none of our design recipes could possibly produce such a definition. We have encountered a new way of programming.


Exercises Exercise 25.1.1

What happens if we place the following three expressions

(start WIDTH HEIGHT)
(move-until-out (make-ball 10 20 0 0)) 
(stop) 
at the bottom of the Definitions window and click Execute? Does the second expression ever produce a value so that the third expression is evaluated and the canvas disappears? Could this happen with any of the functions designed according to our old recipes? Solution

Exercise 25.1.2

Develop move-balls. The function consumes a list of balls and moves each one until all of them have moved out of bounds.

Hint: It is best to write this function using filter, andmap, and similar abstract functions from part [cross-reference]Solution



[previous] [up] [next]     [index]
Next: Sorting Quickly Up: A New Form of Previous: A New Form of

PLT