Custom Structures
Using the define-struct function we can define custom structures with more or fewer fields than make-posn.
(define-structure structname [field1 field2 ...])
This function also creates other functions we will be using:
- Constructor:
(make-structname value1 value2) - Predicate:
(structname? Any) - Selectors:
(structname-field1 (make-structname value1 value2)) => value1, to return a value
The posn is defined like (define-struct posn [x y]).
Design recipe
(define-mystruct mystruct [field1 field2 field3])
;; A MyStruct is a (make-mystruct Type1 Type1 Type3)
;; Interpretation ...
;; - field1 is ...
;; - field2 is ...
;; - field3 is ...
(define MYSTRUCT-1 (make-mystruct ... ))
(define (mystruct-template ms)
(... (mystruct-field1 ms) ...
(mystruct-field2 ms) ...
(mystruct-field3 ms) ...))
- Definition
- Interpretation
- Examples
- Template
Signatures
Constructor:
;; make-mystruct : Type1 Type2 Type3 -> MyStruct
Predicate:
;; mystruct? : Any -> Boolean
Selectors:
mystruct-field1 : MyStruct -> Type1
mystruct-field1 : MyStruct -> Type2
mystruct-field2 : MyStruct -> Type3
Example: students
Each Northeastern student has a first name, last name, and an NUID. Design the function letter-header that accepts a student and produces the opening line of a letter (โDear Alice Doe,โ).
Start with the signature:
;; letter-header : NUStudent -> String
Create NUStudent following the design recipe since it does not exist:
(define-struct nustdudent [first last nuid])
;; An NUStudent is a (make-nustudent String String Nat)
;; Interpretation: a student at NEU
;; - first is the student's first name
;; - last is the student's last name
;; - nuid is the student's id
(define NUSTUDENT-1
(make-nustudent "Alice" "Doe" 12345))
(define (nustudent-template student)
(... (nustudent-first) ...
(nustudent-last) ...
(nustudent-nuid)))
This has four steps: definition, interpretation, examples, and template.
Now make the function:
;; letter-header : NUStudent -> String
;; Produces the opening line of a letter.
(check-expect (letter-header NUSTUDENT-1)
"Dear Alice Doe,")
(define (letter-header student)
(string-append "Dear "
(nustudent-first student) " "
(nustudent-last student) ","))
Example: passed
Design a function passed? that determines if selecting the same response for all five questions on a True/False Test gets you a passing grade (at least 3-out-of-5). Each question has a prompt and correct answer.
;; passed? : TFTest Boolean -> Boolean
Start with defining TFTest. This, however, requires another structure for questions, so design that too:
(define-struct tfq [prompt correct])
;; A TFQuestion is a (make-tfq String Boolean)
;; Interpretation: a true/false question where
;; - prompt is the question
;; - correct is the correct response
(define TFQ-1
(make-tfq "NEU has a campus in Hawaii" #false))
(define TFQ-2
(make-tfq "NEU has a campus in London" #true))
(define TFQ-3
(make-tfq "NEU has a campus in Seattle" #true))
(define TFQ-4
(make-tfq "NEU has a campus in Florida" #false))
(define TFQ-5
(make-tfq "NEU has a campus in Egypt" #false))
(define (tfq-template tfq)
(... (tfq-prompt tfq) ...
(tfq-correct tfq)))
(define-struct tft [q1 q2 q3 q4 q5])
;; A TFTest is a (make-tft TFQuestion TFQuestion TFQuestion TFQuestion TFQuestion)
;; Interpretation: a 5-question T/F test
(define TFT-1
(make-tft TFQ-1 TFQ-2 TFQ-3 TFQ-4 TFQ-5))
(define (tft-template tft)
(... (tfq-temp (tft-q1 tft)) ...
(tfq-temp (tft-q2 tft)) ...
(tfq-temp (tft-q3 tft)) ...
(tfq-temp (tft-q4 tft)) ...
(tfq-temp (tft-q5 tft)) ...))
Note the template tft-template, which also uses tfq-temp to get more data from the question structures.
Now the passed? function:
;; passed? : TFTest Boolean -> Boolean
;; If the supplied answer is use on all questions of the
;; supplied test, would a score of at least 3/5 result?
;; note: the example has only 2 options with true
(check-expect (passed? TFT-1 #true) #false)
(check-expect (passed? TFT-1 #false) #true)
(define (passed? tft ans)
;; Score each question, add them up, and check if the sum is at
;; least 3
(>=
(+ (score (tft-q1 tft) ans)
(score (tft-q2 tft) ans)
(score (tft-q3 tft) ans)
(score (tft-q4 tft) ans)
(score (tft-q5 tft) ans))
3))
We require a new score function:
;; score : TFQuestion Boolean -> {0, 1}
;; Return 1 if the question is answered correctly
;; and 0 if wrong
(check-expect (score TFQ-1 #false) 1)
(check-expect (score TFQ-1 #true) 0)
(check-expect (score TFQ-2 #true) 1)
(check-expect (score TFQ-2 #false) 0)
(define (score tfq ans)
(if
(boolean=? (tfq-correct tfq) ans)
1
0))
Example: car collision
Design a World program collision simulating two cars driving at each other. One starts to the left, and drives right; the other starts to the right and drives left. They each start with their own velocity but both accelerate at the same rate (0.1 pixels/tick per tick).
Questions:
- What changes and how do we represent it?
- What events do we have to handle?
(require 2htdp/image)
(require 2htdp/universe)
;; Design a World program collision simulating two
;; cars driving at each other. One starts to the left,
;; and drives right; the other starts to the right and
;; drives left. They each start with their own
;; velocity but both accelerate at the same rate
;; (0.1 pixels/tick per tick).
(define-struct twocars [x1 vx1 x2 vx2])
;; A TwoCars is a (make-twocars Real Real)
;; Interpretation: two cars driving at each other:
;; - x1 is the pos of the first car in pixels from the left
;; - vx1 is the velocity in pixels/ticks driving from the left
;; - x2 is the pos of the second car in pixels from the left
;; - vx2 is the velocity in pixels/ticks driving from the right
(define TC-1 (make-twocars 2 1 500 2))
(define TC-2 (make-twocars 3 1.1 498 2.1))
(define (tc-temp tc)
(... (twocars-x1 tc) ...
(twocars-vx1 tc) ...
(twocars-x2 tc) ...
(twocars-vx2 tc) ...))
;; collision : TwoCars -> TwoCars
;; Simulates two cars colliding
(define (collision initial-tc)
(big-bang initial-tc
[to-draw draw-tc]
[on-tick move-tc]))
;; draw-tc : TwoCars -> Image
;; Draw the two cars based on the current state
(check-expect
(draw-tc TC-1)
(place-image
CAR-1 2 Y-CAR
(place-image
CAR-2
500 Y-CAR
BACKGROUND)))
(define (draw-tc tc)
(place-image
CAR-1
(twocars-x1 tc) Y-CAR
(place-image
CAR-2
(twocars-x2 tc) Y-CAR
BACKGROUND)))
;; move-tc : TwoCars -> TwoCars
;; Moves and applied acceleration for the cars as
;; a tick passes
(check-expect (move-tc TC-1) TC-2)
(check-expect
(move-tc (make-twocars 0 1 100 2))
(make-twocars 1 1.1 98 2.1))
(define (move-tc tc)
(make-twocars
(+ (twocars-x1 tc) (twocars-vx1 tc) )
(+ (twocars-vx1 tc) ACCEL)
(- (twocars-x2 tc) (twocars-vx2 tc))
(+ (twocars-vx2 tc) ACCEL)))
;; Constants and Visualizations
(define ACCEL 0.1)
(define CAR-1 (rectangle 10 5 "solid" "blue"))
(define CAR-2 (rectangle 10 5 "solid" "red"))
(define Y-CAR 50)
(define BACKGROUND (empty-scene 600 400))