Skip to main content

Lab: Using Racket's structured types

Held
Friday, 1 March 2019
Writeup due
Monday, 4 March 2019
Summary
We consider how and why to use structured data in Racket.
Prerequisites
Precondition checking. Hash tables.

Important syntax and procedures

(struct name (field1 field2 ...)) - Creates a new structured type, with methods named name, name? name-field1, name-field2, and so on and so forth.

Preparation

a. Start DrRacket.

b. Make sure that you have the latest version of the loudhum package by opening a terminal window and typing /home/rebelsky/bin/csc151/update. (Alternately, select File > Install Package…, enter “https://github.com/grinnell-cs/loudhum.git” and follow the instructions.)

c. Don’t forget to add (require loudhum) to the definitions pane.

d. If we did not review the self checks at the start of class, review the self checks with your partner.

Exercises

Exercise 1: Representing times

As long as we’re representing dates, we should probably take the time to represent times. Create a structured type, time, with three fields: hours, minutes, and seconds. You need not add the husk to times (at least not yet).

Exercise 2: From times to strings and back again.

a. Write a procedure, (time->string atime), that takes a time as a parameter and returns the time as a string of the form HH:MM:SS.

b. Write a procedure (string->time str), that takes a string of the form HH:MM:SS as a parameter and returns a time structure.

Exercise 3: Representing chirps

Chirp is a new Internet startup that lets you sent notes to your friends, which they call “chirps”. (Creativity is not their strong suit.) Create a structured type, chirp-kernel, with the following fields.

  • id, a symbol we’ll use to identify the chirp.
  • author, a string that identifies the author of the chirp.
  • contents, a string that contains the body of the chirp.
  • tags, a list of strings
  • date, a date that represents when the chirp was chirped.
  • time, a time that represents the time the chirp was chirped.

Exercise 4: A chirp husk

Create a procedure, (chirp author contents tags date time), that takes four parameters, verifies their types, and then creates a chirp kernel using those four parameters. What about the identifier? You should generate that automatically with (gensym "chirp"). (gensym is a procedure that generates a unique symbol.)

Exercise 5: Limiting access

One outstanding question that we had with date and date-kernel was how we prevent the client from using date-kernel, rather than date. In this exercise, we’ll explore one approach.

a. At the end of the corresponding reading, we described a husk-and-kernel-style technique for ensuring that our dates had a particular form. Copy that code into a new DrRacket window. Make sure to include date->string and string->date, too.

b. Add the following to the top of the file (after the #lang racket). These commands tell Racket which procedures and values that are defined in the file are accessible to other programs.

(provide date
         date?
         date-year
         date-month
         date-day
         date->string
         string->date)

c. Save the file as “/home/username/Desktop/dates.rkt”, substituting your own username.

d. Open a new DrRacket window and type the following at the top (after the #lang racket) and then click Run. (Once again, you should substitute your own username.)

(require (file "/home/username/Desktop/dates.rkt"))

e. What do you expect to get when you type the following in the interactions pane of the new windows.

> (define date1 (date 2019 03 01))
?
> date1
?
> (define date2 (date 2019 22 01))
?
> date2
?

f. Check your answer experimentally.

g. You should have seen that date1 is a date-kernel structure and that date2 does not exist because the second call to date is invalid. Since date1 is valid, we should be able to extract its fields. What do you expect the results of the following to be?

> (date-year date1)
?
> (date-month date1)
?
> (date-day date1)
?
> (date? date1)
?

h. Check your answer experimentally.

i. We’ve seen that date1 is a date-kernel structure.

> date1
#<date-kernel>

What do you expect the result of the following to be?

> (date-kernel? date1
?
> (date-kernel-year date1)
?
> (date-kernel-month date1)
?
> (date-kernel-day date1)
?

j. Check your answer experimentally.

k. What do you expect to happen if we try to create a date with date-kernel?

> (define date3 (date-kernel 2019 03 01))
> date3

l. Check your answer experimentally.

m. What does this exercise suggest?

Exercise 6: Lists of structs

Typically, we want more than one of any struct we create. Consider, for example, the chirp structure we designed earlier. In effect, the Chirp service creates something like a list of chirps. So let’s store them as a list.

> (define chirp-list
    (chirp "grinco" "We have installed Chirp on campus."
           (list "grinnell" "chirp")
           (date 2019 02 27) (time 11 03 00))
    (chirp "grinco" "We are so cutting edge!"
           (list "grinnell" "chirp")
           (date 2019 02 27) (time 11 04 00))
    (chirp "rebelsky" "I love CSC 151."  (list "strange" "csc")
           (date 2019 03 01) (time 10 00 15))
    (chirp "rebelsky" "That wasn't me."  (list "strange" "other")
           (date 2019 03 01) (time 10 01 23))
    (chirp "rebelsky" "I think my son hacked my chirpstream."  
           (list "strange" "paranoid")
           (date 2019 03 01) (time 10 01 55))
    (chirp "kington" "Welcome to GrinCo!" (list "grinnell" "raykay")
           (date 2019 02 28) (time 15 02 05))
    (chirp "kington" "I love CSC 151." (list "strange" "csc" "raykay")
           (date 2019 03 01) (time 10 01 55))
    (chirp "rebelsky" "I think my son hacked President Kington's chirpstream."  
           (list "paranoid")
           (date 2019 03 01) (time 10 03 00))
    (chirp "grinco" "We are shutting down Chirp on campus"
           (list "grinnell" "chirp")
           (date 2019 03 01) (time 11 03 00)))

a. Write an expression that identifies all of the chirps from rebelsky and creates a list of their contents

> (map chirp-contents (filter _________ chirp-list))

b. Write an expression that determines how many chirps in the list are by kington. (Recall that (tally lst pred?) counts the number of values in a list that meet a particular predicate.

Exercise 7: Hashing structs

One of the disadvantages of lists is that they are relatively slow to search. That’s one of the reasons we use hash tables instead. For example, we might hash chirps by their unique identifier.

Write a procedure, (store-chirp! hash chrp) that stores a chirp in a hash table.

> (define chirps (hash-new))
> (define sample (chirp "someone" "something" (list "chirp")
                        (date 2019 02 28) (time 20 21 22)))
> (hash-has-key? chirps 'chirp108319)
#f
> (store-chirp! hash sample)
> (hash-has-key? chirps 'chirp108319)
#t
> (chirp-contents (hash-ref chirps 'chirp108319))
"something"

For those with extra time

If you find that you have extra time, you might consider attempting one or more of the following exercises.

Extra 1: Protecting your time

Rewrite the time struct using a husk-and-kernel approach that ensures that the hours, minutes, and seconds are reasonable.

Extra 2: Comparing times

Write a procedure, (time-before? time1 time2), that returns true (#t) if time1 comes before time2 and false otherwise.

Extra 3: Displaying chirps

Write a procedure (chirp->string chrp), that takes a chirp as a parameter and creates a dstring that represents it in a useful way.

> (define mychirp
    (chirp "rebelsky" "I love CSC 151."  (list "strange" "csc")
           (date 2019 03 01) (time 10 00 15)))
> (display (chirp->string mychirp))
Output! chirp12312: At 10:00:15 on 2019-01-01, rebelsky said "I love CSC 151".

Acknowledgements

This lab was (mostly) newly written in spring 2019.

The Preparation section was taken from some other lab. (Most labs have the same preparation section.)