From Textbook: Standard Data Types
Lists
Building Lists
The empty list is written
[]
and is pronounced “nil”, a name that comes from Lisp. Given a listlst
and elementelt
, we can prependelt
tolst
by writingelt::lst
. The double-colon operator is pronounced “cons”
cons
always prepend things, so cons
is actually right-associative. The following code has the same effect.
1 | 1::2::3::[];; |
All the elements of a list must have the same type. The word list
itself here is not a type. For example, given int
, it produces the type int list
. You could think of type constructors as being like functions that operate on types, instead of functions that operate on values. (We mentioned this idea of thinking constructor as a function on type in CS2112)
Accessing Lists
The following code computes the sum of a list.
1 | let rec sum lst = |
The following code computes the length of a list. _
, the underscore character is used when we want to indicate the presence of some value in a pattern without actually giving it a name.
1 | let rec length lst = |
The following code appends one list onto the beginning of another list.
1 | let rec append lst1 lst2 = |
Note: every natural number is either 0 or is 1 greater than some other natural number n, and so a proof by induction has a base case for 0 and an inductive case for n+1. Likewise all our functions have a base case for the empty list and a recursive case for the list that has one more element than another list. This similarity is no accident. There is a deep relationship between induction and recursion; we’ll explore that relationship in more detail later in the course.
Mutating Lists
Values in OCaml are immutable. The following code increments the head by 1.
1 | let inc_first lst = |
This code looks extremely similar with C or Java operating on pointers. The implementation of list in OCaml works in the way that it shares the tail list t
between the old list and the new list, such that the amount of memory in use does not increase (beyond the one extra piece of memory needed to store h+1
). The reason that it’s quite safe for the compiler to implement sharing is exactly that list elements are immutable.
Pattern Matching with Lists
Basics
Each of the clauses pi -> ei
is called a branch or a case of the pattern match. The p
‘s here are a new syntactic form called a pattern.
- a variable name, e.g.
x
- the underscore character
_
, which is called the wildcard (we don’t care what it is) - the empty list
[]
p1::p2
[p1; ...; pn]
1 | let length_is lst n = |
The code above always returns true, because suppose that the length of lst
is 5. Then the pattern match becomes: match 5 with n -> true | _ -> false
. And n
matches 5. A variable pattern matches any value and here produces the binding n->5
. The correct codes are written below.
1 | let length_is lst n = |
However, this doesn’t mean patterns are not the variable values as in switch
statement. Yes they are general “patterns”. But you can match them to specific values. e.g.
1 | match 5 with |
Advanced Pattern Matching
p1 | ... | pn
: an “or” pattern; matching against it succeeds if a match succeeds against any of the individual patternspi
, which are tried in order from left to right. All the patterns must bind the same variables.(p : t)
: a pattern with an explicit type annotation.c
: here,c
means any constant, such as integer literals, string literals, and booleans.'ch1'..'ch2'
: here,ch
means a character literal. For example,'A'..'Z'
matches any uppercase letter.p when e
: matchesp
but only ife
evaluates totrue
.
Tuples and Records
both represent heterogeneous types of values, both sizes are fixed
Records
Works like struct
in C++. Each field is identified by names.
Definition
1 | type student ={name: string; gpa : float; year :int;} (*defining a type*) |
Patter Matching
1 | match rbg with |
Tuples
Tuples are identified by position, instead of naming the components.
Definition
1 | let t = (10,"am") (*t has type: int * string*) |
Pattern Matching
if we use a pattern in a let
expression (or definition), we are really just doing pattern matching with a single clause.
1 | let tick t = |
Pattern Matching in a Nutshell
1 | (* Pokemon types *) |