From ideas to code

As developers we are faced with problems that we need to solve and then implement using a programming language and a computer.

The need to

  1. Understand the problem.

    1. Figure out the important information

    2. Figure out how to use the information in order to get a solution through a well defined set of tasks

  2. Translate information from the real world into data that the computer will use

  3. Encode the tasks in the correct order to manipulate our data and get the solution

  4. Verify that our encoding does in fact solve the problem

  5. Can we improve/simplify our solution/encoding

We will follow a recipe that will guide and help us transcribe our solutions into code.

The Design Recipe

  1. Data Design

  2. Signature and Purpose

  3. Examples and Tests

  4. Function Definition

  5. Program Review

Each step produces output that is either

  • comments in your source file

  • code in your source file

Follow the steps in order. No exceptions!

We will re-implement one of the problems in Lab 1 using the Design Recipe. We first present the contents of our source file and then take each step of the Design Recipe (DR) and explain in details what needs to be done as well as the output for that step.

Problem Statement

Create a new function called tip-percentage that will take as input the bill amount and the percentage to be used for the tip.

tip-percentage
;;;; Data Definitions:
;; A BillAmount is a Number
;; INTERP: represents the bill total amount in dollars and cents

;; A Percentage is a Number
;; INTERP: represents the percantage to be applied as a tip

;; A Tip is a Number
;; INTERP: represents the total amount to be given as tip in dollars and cents

;;;; Signature
;; tip-percentage: BillAmount Percentage -> Tip

;;;; Purpose
;; GIVEN: the bill's total amount and the percentage that we
;;        would like to give as a tip
;; RETURNS: the amount given as tip

;;;; Examples
;; (tip-percentage 100 10) => 10
;; (tip-percentage 200 15) => 30

;;;; Function Definition
(define (tip-percentage bill percentage)
  (* bill
     (/ percentage 100)))

;;;; Tests
(check-expect (tip-percentage 100 10)  10)
(check-expect (tip-percentage 200 15)  30)

Data Design

Task

The first step in the DR is to understand the problem’s important pieces of information. We use the word information here to refer to the concepts used in the problem statement. These concepts are going to be described using English and refer to what we as humans understand.

For every problem ask yourself

  1. What part of the information in the problem is important?

  2. How is that information relevant to the solution?

We need to represent these piece of information as data. The word data is used to refer to information that lives our program and by extension in the computer.

Data definitions map information to data. We also must document the inverse relationship; how to go from data to information. The inverse relationship we call interpretation and we must document our interpretation of the data as well.

Example

In our tip example, the relevant pieces of information are

  1. bill amount

  2. tip

  3. percentage

These are English terms that mean something to us. We need to capture these pieces of information as data. We use data definitions to create a name for a category of data that together hold some commons characteristics.

We know that in Beginning Student Language (BSL) we have numbers. For the purpose of this class we are providing you with a set of predefined data definitions that you can directly use without providing a definition.

We decide to represent

  • bill amount as a Number

  • tip as a Number

  • percentage as a Number

Our data definition section created new data definition names (as aliases) for each piece of information.

;;;; Data Definitions:
;; A BillAmount is a Number
;; INTERP: represents the bill total amount in dollars and cents

;; A Percentage is a Number
;; INTERP: represents the percentage to be applied as a tip

;; A Tip is a Number
;; INTERP: represents the total amount to be given as tip in dollars and cents

For each piece of information we have

  1. a Data Definition showing how we represent information as data

  2. an INTERP line showing how we interpret data as information

We need to have both relationships between information and data in our code.

Code Style
  • Data Definition Names must be in CamelCase. Make sure that when you refer to a Data Definition name you write the name exactly as it appears in its Data Definition.

  • Interpretations immediately follow the Data Definition and must start with INTERP

Signature

Task

The goal of the signature is to capture using data the inputs and outputs of our proposed function.

Example

;;;; Signature
;; tip-percentage: BillAmount Percentage -> Tip

Signatures have a well defined structure,

<function_name> : <argument's-DD> ... -> <return's-DD>
Code Style
  • Signatures must be on a line by themselves.

  • Use a : to separate the name of the function from its signature

  • Use a to separate the arguments from the output

  • Use spaces to separate multiple arguments

Purpose Statement

Task

The goal do the purpose statement is to provide a description of what the function does, not how it does it, by using English and referring to information and not data. The purpose statement should explain to a non-developer what this function does using the same concepts as the problem statement.

Example

;;;; Purpose
;; GIVEN: the bill's total amount and the percentage that we
;;        would like to give as a tip
;; RETURNS: the amount given as tip
Code Style
  • Purpose statements must contain at least 2 subsections, GIVEN: and RETURNS:

  • GIVEN: must only mention information that will be given as inputs to the function

  • RETURNS: must mention information that will be expected as output. Optionally we can refer to information given as inputs to help explain the output of our function

Examples

Task

The goal of this step is to provide examples on how to use the proposed function (we have not implemented the function yet). These are not valid code yet. They are close to valid code.

Example

;;;; Examples
;; (tip-percentage 100 10) => 10
;; (tip-percentage 200 15) => 30
Code Style
  • Examples have 2 parts separate by a

  • Example function call is to the left of

  • Example output is to the right of

Tests

Task

The goal of this step is to create tests for the proposed function (we have not implemented the function yet). These are valid code and these are the tests that we are going to run in order to convince ourselves that our proposed function solves the problem. We must ensure that we have tests for as many possible cases as we can.

Typically the result of the step Examples are turned into tests and extra tests on top of those found in the Examples step of the DR are added to ensure that we have "no black lines" in our function’s definition.

Example

;;;; Tests
(check-expect (tip-percentage 100 10)  10)
(check-expect (tip-percentage 200 15)  30)
Code Style
  • Tests should be organized and one per line if possible

  • Spaces between inputs to check-expect to make it visually clear which expression is the function call and which expression is the expected output

Function Definition

Task

We now are ready to write the function.

Example

;;;; Function Definition
(define (tip-percentage bill percentage)
  (* bill
     (/ percentage 100)))
Code Style
  • Consult the class web-page under Module 0 for how to format your Racket code, when to add new lines, spaces etc.

Review

Task

The goal of this step is to go back to the beginning of our DR and perform the steps again but this time to ensure that

  1. We have the correct Data Definitions and the names for any defined Data Definitions are good names

  2. The Signature is correct according to our Function Definition and uses the correct Data Definition Names

  3. The Purpose Statement is correct given our Function Definition and makes sense given our Problem Statement

  4. Our Examples are correct given our Function Definition

  5. Our Tests are correct and take care of as many cases as we can from the Problem Statement. We have no black lines in our Function Definition

While validating our DR steps we should also consider

  1. Are my Data Definition too broad? Can I make my Data Definitions capture exactly what is needed?

  2. Is my Signature too broad?

  3. Can my Purpose Statement be clearer? Can it be simplified or re-written to avoid any confusion?

  4. Are my Examples helpful? Do my Examples show how the function is supposed to behave? Are there other examples that better show the behaviour of my function?

  5. Do my Tests cover as many cases as we can for the Problem Statement? Do we have any black lines that we need to address?

  6. Can I simplify my Function Definition? Is my Function Definition too long? Is my Function Definition formatted correctly? Are the names used for arguments "good names"?

Example

Let’s review our solution for tip-percentage

Data Definitions

We have interpreted the bill amount as a Racket Number. However, Number in Racket allows for negative numbers, complex numbers etc. This data definition seems too broad, the bill amount should not be a complex number, nor a negative number. We would like to allow for 0 as the bill amount though and it looks like there is a better, more accurate Data Definition, NonNegReal.

For the percentage to be used, we have a similar situation, Number is too broad. For percentage we would like to allow integers greater or equal to 0. NonNegReal seems to be a better choice here as well.

The tip, the result of our function, again Number seems too broad. NonNegReal seems to be a better choice here.

Signature

Our signature is correct, we take in 2 inputs. Since we created our own data definition names, our update from Number to NonNegReal does not affect our signature.

Purpose

The wording for RETURNS seems a little off. It should read

;;;; Purpose
;; GIVEN: the bill's total amount and the percentage that we
;;        would like to give as a tip
;; RETURNS: the amount to be given as tip
Examples

Look good.

Tests

We might want to add a couple of more tests that use real numbers.

;;;; Tests
(check-expect (tip-percentage 100 10)  10)
(check-expect (tip-percentage 200 15)  30)
(check-expect (tip-percentage 10.15 5) 0.5075)
(check-expect (tip-percentage 25 10.5) 2.625)
Function Definition

Looks good.

End Result after Review
tip-percentage After Review
;;;; Data Definitions:
;; A BillAmount is a NonNegReal
;; INTERP: represents the bill total amount in dollars and cents

;; A Percentage is a NonNegReal
;; INTERP: represents the percantage to be applied as a tip

;; A Tip is a NonNegReal
;; INTERP: represents the total amount to be given as tip in dollars and cents

;;;; Signature
;; tip-percentage: BillAmount Percentage -> Tip

;;;; Purpose
;; GIVEN: the bill's total amount and the percentage that we
;;        would like to give as a tip
;; RETURNS: the amount to be given as tip

;;;; Examples
;; (tip-percentage 100 10) => 10
;; (tip-percentage 200 15) => 30

;;;; Function Definition
(define (tip-percentage bill percentage)
  (* bill
     (/ percentage 100)))

;;;; Tests
(check-expect (tip-percentage 100 10)  10)
(check-expect (tip-percentage 200 15)  30)
(check-expect (tip-percentage 10.15 5) 0.5075)
(check-expect (tip-percentage 25 10.5) 2.625)

Different Kinds of Data

  1. Scalar Data

    • Single values or simple data, Number, Boolean etc.

  2. Itemization Data

    • Fixed set of mutually exclusive options.

  3. Compound Data

    • Data that we treat as a Unit but contain a fixed number of sub-components

  4. Mixed Data

    • Combination of the previous three kinds of data

  5. Recursive

  6. Mutually Recursive

Predefined

We have some predefined data definitions that you can use.

Data Design Recipe

Data and Data Definitions are central to designing your programs. We are going to introduce more and more complex data throughout the course. To help us describe, create and decompose data definitions we have a Data Design Recipe.

  1. Data Definition

  2. Constructor Template

    1. How do we create an instance of the Data Definition?

  3. Interpretation

    1. Data → Information

  4. Deconstructor Template

    1. Given an instance of this Data Definition how do we take it apart?

  5. Examples

Itemizations

Itemization Data describe data that captures a selection (one and only one) from a given fixed set of options.

For example, consider the problem of mapping your number grade to a letter grade. A letter grade is one of

  • A

  • A-

  • B+

  • B

  • B-

  • C+

  • C

  • C-

  • D

Data Definition

We can create a data definition for Letter Grades as follows

;; A LetterGrade is one of
;; - 'A
;; - 'A-
;; - 'B+
;; - 'B
;; - 'B-
;; - 'C+
;; - 'C
;; - 'C-
;; - 'D
;; INTERP: represents a letter grade for course assignments

This data definition explicitly states that a letter grade is represented as one of a specific set of Symbol s. It is not any Symbol but it has to be one of the Symbol s written down in our data definition.

Constructor Template

To create a LetterGrade we simply create a Symbol, one of the Symbol 's that is a LetterGrade. Given that this is scalar there is no special way to create a LetterGrade. We will see later examples that will have constructor templates.

Interpretation

We wrote that down

;; INTERP: represents a letter grade for course assignments

Deconstructor Template

Given an instance of a LetterGrade how are we going to take it apart? The words "take it apart" might not be appropriate for this example since LetterGrades are Symbol s and thus scalar. But we do need to figure out which exact LetterGrade we were given, so we are in a way deconstructing the LetterGrade data definition.

The way we write the deconstructor is as an "incomplete" or template and place …​ in our function definition for expression that we do not know. To put another way, we are writing a function for which we do not know the output.

Deconstructor Template
;; Deconstructor Template
;; letter-grade-fn: LetterGrade -> ???  (1)
#;(define (letter-grade-fn letter-grade)
    (cond
      [(symbol=? letter-grade 'A) ...]
      [(symbol=? letter-grade 'A-) ...]
      [(symbol=? letter-grade 'B+) ...]
      [(symbol=? letter-grade 'B) ...]
      [(symbol=? letter-grade 'B-) ...]
      [(symbol=? letter-grade 'C+) ...]
      [(symbol=? letter-grade 'C) ...]
      [(symbol=? letter-grade 'C-) ...]
      [(symbol=? letter-grade 'D) ...]))
1 We write a signature, just like we would have done for a function, but we do not know what the return value is so we write ??? in our signature
Code Style
  • We use ;# instead of ; for commenting out our deconstructor template. The ;# is also a comment but DrRacket selects the immediately following well balanced expression and comments it out. Also DrRacket will format all your ;# comments as if it was code making our templates easier to read

  • We typically name our templated-function using the name of the data definition, remove CamelCase (downcase all letters) and add - between words, and append the string -fn to it, e.g., for LetterGrade we get letter-grade-fn

  • We use ellipses …​ as placeholders. Since we do now have an output for our letter-grade-fn all answer expressions inside our cond are …​

Similarly, trying to capture Number Grades we can create a data definition

;; A NumberGrade is an Integer in the range [0,100]
;; INTERP: represents the numerical grade given to a course assignment

NumberGrade is a kind of itemization but we used [1,100] which is a mathematical notation for the numbers from 0 to 100 inclusive instead of writing all the numbers from 0 to 100 one number per line like we did for LetterGrade. Similarly, we do not write a deconstructor template since this is a range of numbers and we assume that we are all comfortable with deconstructing a range of numbers from math.

Let’s recreate our function from last week that translated our Number Grades to Letter Grades, this time of course using the Design Recipe

number-grade→letter-grade
;;;; Data Definitions

;; A LetterGrade is one of
;; - 'A
;; - 'A-
;; - 'B+
;; - 'B
;; - 'B-
;; - 'C+
;; - 'C
;; - 'C-
;; - 'D
;; INTERP: represents a letter grade for course assignments

;; Deconstructor Template
;; letter-grade-fn: LetterGrade -> ???
#;(define (letter-grade-fn letter-grade)
    (cond
      [(symbol=? letter-grade 'A) ...]
      [(symbol=? letter-grade 'A-) ...]
      [(symbol=? letter-grade 'B+) ...]
      [(symbol=? letter-grade 'B) ...]
      [(symbol=? letter-grade 'B-) ...]
      [(symbol=? letter-grade 'C+) ...]
      [(symbol=? letter-grade 'C) ...]
      [(symbol=? letter-grade 'C-) ...]
      [(symbol=? letter-grade 'D) ...]))


;; A NumberGrade is an Integer in the range [0,100]
;; INTERP: represents the numerical grade given to a course assignment


;;;; Signature
;; number-grade->letter-grade: NumberGrade -> LetterGrade
;;;; Purpose
;; GIVEN: a numerical grade of an assignment
;; RETURNS: the corresponding letter grade

;;;; Examples
;;(number-grade->letter-grade 100) => 'A
;;(number-grade->letter-grade 94) => 'A-
;;(number-grade->letter-grade 76) => 'B-


;;;; Function Definition
(define (number-grade->letter-grade number-grade)
  (cond
    [(>= 100 number-grade 95) 'A]
    [(>= 94 number-grade 90) 'A-]
    [(>= 89 number-grade 85) 'B+]
    [(>= 84 number-grade 80) 'B]
    [(>= 79 number-grade 75) 'B-]
    [(>= 74 number-grade 70) 'C+]
    [(>= 69 number-grade 65) 'C]
    [(>= 64 number-grade 60) 'C-]
    [(>= 59 number-grade) 'D]))


;;;; Tests
(check-expect (number-grade->letter-grade 100) 'A)
(check-expect (number-grade->letter-grade 93) 'A-)
(check-expect (number-grade->letter-grade 88) 'B+)
(check-expect (number-grade->letter-grade 83) 'B)
(check-expect (number-grade->letter-grade 76) 'B-)
(check-expect (number-grade->letter-grade 72) 'C+)
(check-expect (number-grade->letter-grade 66) 'C)
(check-expect (number-grade->letter-grade 61) 'C-)
(check-expect (number-grade->letter-grade 55) 'D)