Module 5

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:

  1. Constructor: (make-structname value1 value2)
  2. Predicate: (structname? Any)
  3. 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) ...))
  1. Definition
  2. Interpretation
  3. Examples
  4. 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:

  1. What changes and how do we represent it?
  2. 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))