CS52 - Spring 2017 - Class 6
Example code in this lecture
directions.sml
student.sml
Lecture notes
admin
- assignment 2 due tomorrow at 5pm
- assignment 3 out soon
midterm
- logistics
- in class on Tuesday
- pencil and paper exam (practice writing a few functions in SML on paper)
- closed books, notes, computer, etc.
- *except*, can bring one piece of standard sized paper, single sided, with notes
- Coverage: SML
- evaluate the function/what does the function do
- type signature
- code up functions
- Extra mentors on Sunday and Monday for review support
- Will post *some* samples problems
- Intro SML reading and books also have others
academic honesty
quick poll
- CS62 previously?
- CS62 next semester?
- CS55 or math103 previously?
- CS55 next semester?
recursion patterns
difference between [] and [[]]
decimal numbers
- most commonly used number systems have a "base"
- each digit in the number can range from 0...base-1
- each digit in the number has an index, starting at the right-most digit with 0 and working up to the left
- the contribution of the digit is:
base^index * value
- by summing up the contribution of all of the digits, we get the value of the number
- what is the base for our numbering system?
- 10
- for example
- 245 = 5*10^0 + 4*10^1 + 2*10^2
- 80498 = 8*10^0 + 9*10^1 + 4*10^2 + 0*10^3 + 8*10^4
other bases
- we can represent values by any base we'd like!
- A common one is CS, base 2, binary numbers:
- digits can only be a 0 or a 1
- for example, the following binary numbers:
- 101 = 1*2^0 + 0*2^2 + 1*2^2 = 5
- 11 = 1*2^0 + 1*2^1 = 3
- 10111 = 1*2^0 + 1*2^1 + 1*2^2 + 0*2^3 + 1*2^4 = 23
- 100001 = 1*2^0 + 1*2^5 = 33
- we can use any base we'd like, though
- base 9
- digits are numbers from 0 through 8
- 267 = 7*9^0 + 6*9^1 + 2*9^2 = 7 + 54 + 162 = 223
- base 200
- digits are numbers from 0 through 199
- 267 = 7*200^0 + 6*200^1 + 2*200^2 = 81,207
- not that even numbers written the same way can be interpreted differently!
- 2 55 167 = 167*200^0 + 55*200^2 + 2*200^2 = 91,167
types seen so far
- basic types: int, bool, real, char, string
- compound types: lists and tuples
- combine existing types to make a new type
we can define our own types!
- simplest version: an enumeration type that can take a fixed set of values
datatype <name_of_type> = Option1 | Option2 | ...;
- <name_of_type> is the new type's name
- by convention, this will be lowercased
- Options are the different values is can take (called "constructors")
- by convention, these will be capitalized (but not all caps)
- define a new datatype direction that can have four possible values:
> datatype direction = North | South | East | West;
datatype direction = East | North | South | West
- we can now use any of these for "values" and SML will know what type they are
> North;
val it = North : direction
> East;
val it = East : direction
- notice that SML figures out that they are of type "direction"
- these are literal values, like writing 0 or 1
- we can also use these inside functions, for example the turnRight and turnLeft functions in
directions.sml code
- we must pattern match on *all* of the different values
- if we don't, we would get non-exhaustive search
- What will be the type signature?
- fun rotateRight = fn: direction -> direction
- direction is just like any other type
- We can call this function and pass it any direction and it will give us back another direction:
> turnRight North;
val it = East : direction
> turnRight West;
val it = North : direction
> turnRight (turnRight North);
val it = South : direction
- write a function called isVerticalDirection that takes as input a direction returns true of the direction is a vertical direction (North/South) and false otherwise
- What will be the type signature?
val isVerticalDirection = fn: direction -> bool
- look at isVerticalDirection function in
directions.sml code
- We could have done an exhaustive list, but instead use x to capture East and West
- we can do this since the patterns are matched from the top down
types that hold values
- the basic datatype declaration is useful in some cases, but is fairly limited in that we have to explicitly define all the values
- we can also construct datatypes whose values can take arguments!
datatype <name_of_type> = <constructor_expression1> | <constructor_expression2> | ...
where <constructor_expression> looks like:
<name> of <type>
(or is also just a zero parameter constructor like above, e.g. North)
- for example, look at the student type declaration in
student.sml code
- four different types of values
- Firstyear, Sophomore, Junior, Senior
- each type also can be instantiated with a string value
> Firstyear "Dave";
val it = Firstyear "Dave" : student
> Sophomore "Lucy";
val it = Sophomore "Lucy" : student
- by creating the type, we have created 4 "constructors" of the type
- just like in Java, constructors are used to create objects/values
- This allows us to write functions that can then access the values
- Look at the studentToString function in
student.sml code
- Again, we pattern match against the four type values
- The difference is that if we want to get at the value inside, we pattern match the constructor
- this is similar to getting at the head of the list or getting at the values in a tuple
- what is the type signature of studentToString?
- val studentToString = fn: student -> string
> studentToString (Firstyear "dave");
val it = "First year dave" : string
- write a function isGraduating that takes a student and returns true if that student is graduating (i.e is a senior) and false otherwise
- type signature?
val isGraduating = fn: student -> bool
- look at isGraduatingLong function in
student.sml code
- again, pattern match agains the constructors
- don't really need x at all (could have just written _ here)
- is there a better (more succinct) way of writing this function?
- look at isGraduating function in
student.sml code
- pattern match against Senior
- and then just x as a catchall for everything else
- notice that if you don't need to get at the value you can just match it to a variable
- look at the funFacts function in
student.sml code
- what does it do? what is its type signature?
- val funFacts = fn: student -> string
- how does SML know that the input type is of type student?
- because the input type of both isGraduating and studentToString is student
- takes in a student and produces a string describing that student
> funFacts (Firstyear "dave");
val it = "First year dave has more college fun coming!" : string
> funFacts (Senior "lucy");
val it = "Senior lucy is graduating!" : string
- notice that we do NOT always need to pattern match against the type
- write a function called isOlder that takes as input *two* students and returns true if the first student is strictly older than the second (academically speaking)
- what is the type signature?
val isOlder = fn: student -> student -> bool
- how many different combinations might we need to check?
- each input parameter can take on the four different years, so 4*4 = 16!
- look at isOlder function in
student.sml code
- With a bit of smart checking, we're able to reduce it to 8 patterns that we need to check
- Firstyear isn't older than anyone (pattern 1)
- Sophomores are older than first years (pattern 2), but no one else (pattern 3)
- Juniors are older than first years (pattern 4) and sophomores (pattern 5), but no one else (pattern 6)
- Seniors are not strictly older than other seniors (pattern 7), but are than everyone else (pattern 8)