We define a structure representing a brand of airplane with the followind data definition:
(define-struct plane_brand (manufacturer speed seats service ))
;; A plane_brand is:
;; (make-plane_brand symbol num num num)
;;where manufacturer is the maker of the brand,
;; speed is the cruising speed,
;; seats is the seating capacity, and service is the number
;; of miles before an overhaul is required .
; Here we create ("instantiate") some concrete brands with actual values in the fields ("instances" of the brand):
(make-plane_brand 'Boeing 550 282 15000) ;; DC-10
(make-plane_brand 'Boeing 505 141 10000) ;; MD-80
(make-plane_brand 'Aerospatiale 300 46 5000) ;;ATR-72
(make-plane_brand 'Airbus 540 250 12000) ;; A320
; We can use accessor functions to access the values stored inside an instantiation:
(symbol=? 'Boeing (plane_brand-manufacturer (make-plane_brand 'Boeing 550 282 15000)))
(= 505 (plane_brand-speed (make-plane_brand 'Boeing 505 141 10000)))
(= 46 (plane_brand-seats (make-plane_brand 'Aerospatiale 300 46 5000)))
(= 12000 (plane_brand-service (make-plane_brand 'Airbus 540 250 12000)))
;In addition, Scheme automatically creates a comparison function that tests if its input is of that structure's type (note the use of eq? rather than =):
(eq? true (plane_brand? (make-plane_brand 'Boeing 550 282 15000)))
(eq? false (plane_brand? 'Something_else))
; We can now write a function to process a plane_brand. For instance, how long can a plane fly between service overhauls?
;; serviceTime: plane_brand --> num or symbol
;; Calculates the time that a plane_brand can fly at
;; cruising speed between service overhauls.
;; Returns 'Invalid_input! if the supplied expression is not a plane_brand(define (serviceTime brand)
(cond
[(plane_brand? brand) (/ (plane_brand-service brand) (plane_brand-speed brand))]
[else 'Invalid_input!])) ; fail safe clause;; Test code
(= (/ 2000 101) (serviceTime (make-plane_brand 'Boeing 505 141 10000)))(= (/ 200 9 ) (serviceTime (make-plane_brand 'Airbus 540 250 12000)))(symbol=? 'Invalid_input! (serviceTime 'Cessna))
In our never-ending striving for abstract representations, let's take a step back and look at the functions we've written that use structures.
What can we say about the essence of functions that work on plane_brand structures? Is there anything that is common to all such functions?
Look at the various parts of the function and what they are dealing with and express them as a design template:
(define (aFunc aPlane_Brand ... )
(cond
[(plane_brand? aPlane_Brand)( ...(plane_brand-manufacturer aPlane_Brand)... (plane_brand-speed aPlane_Brand) ...(plane_brand-seats aPlane_Brand) ...(plane_brand-service aPlane_Brand)...)]
[else error_result]))
In words, a function that works on a plane_brand
In light of these earth-shaking revelations, we modify our original design recipe:
Let's define another type of structure:
(define-struct auto_brand (manufacturer seats warranty))
;; An auto_brand is:
;; (make-auto_brand symbol num num )
;;where manufacturer is the maker of the brand,
;; seats is the seating capacity, and warranty is the number
;; of miles covered by the warranty.
auto_brand and plane_brand are different, but yet similar in some ways. Ignoring specifics for a minute, let us consider the simple (simple is always good in programming) fact that both represent brands of some sort of vehicle. How can we represent that?
We'd like to say something like this sort of abstract data definition:
;; A vehicle_brand is struct that could be
;; - an auto_brand or
;; - a plane_brand
So, let's just say it like that, ok?
Now let's consider a function on vehicle_brand. Let's modify our original serviceTime function:
;; vehicleServiceTime: vehicle_brand --> num or symbol
;; Calculates the time that a vehicle_brand can go at
;; cruising speed between service overhauls.
;; Returns 'Invalid_input! if the supplied expression is not a vehicle_brand(define (vehicleServiceTime brand)
(cond
[(plane_brand? brand) (/ (plane_brand-service brand) (plane_brand-speed brand))]
[(auto_brand? brand) (/ (auto_brand-warranty brand) 55)]
[else 'Invalid_input!])) ; fail safe clause;; Test code -- the old tests should still work
(= (/ 2000 101) (vehicleServiceTime (make-plane_brand 'Boeing 505 141 10000)))(= (/ 200 9 ) (vehicleServiceTime (make-plane_brand 'Airbus 540 250 12000)))(symbol=? 'Invalid_input! (serviceTime 'Cessna))
;; Some new tests
(= (/ 10000 11) (vehicleServiceTime (make-auto_brand 'Toyota 4 50000)))
(= (/ 6000 11) (vehicleServiceTime (make-auto_brand 'GM 2 30000)))
We can see that the data analysis of an abstract data structure affects our design template:
(define (aFunc aBrand ... )(cond
[(plane_brand? aBrand)( ...(plane_brand-manufacturer aBrand)... (plane_brand-speed aBrand) ...(plane_brand-seats aBrand) ...(plane_brand-service aBrand)...)]
[(auto_brand? aBrand)( ...(auto_brand-manufacturer aBrand)... (auto_brand-warranty aBrand) ...(auto_brand-seats aBrand))]
[else error_result]))
This is called "Data-Driven Design". Notice how the code body is practically writing itself, based on the data analyis.
©2002 Stephen Wong