union case Shape.Square: edge: float -> Shape
Multiple items
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
union case Shape.Rectangle: width: float * height: float -> Shape
union case Shape.Circle: radius: float -> Shape
type Tree =
| Empty
| Node of value: int * left: Tree * right: Tree
Full name: Index.Tree
union case Tree.Empty: Tree
union case Tree.Node: value: int * left: Tree * right: Tree -> Tree
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val tree : Tree
Full name: Index.tree
val printf : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printf
val sprintfResult : string
Full name: Index.sprintfResult
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val countInternal : tree:Tree -> int
Full name: Index.countInternal
val tree : Tree
val left : Tree
val right : Tree
val ( example 1.1 ) : int
Full name: Index.( example 1.1 )
val sumLeaves : tree:Tree -> int
Full name: Index.sumLeaves
val ( exercise 1.1 ) : int
Full name: Index.( exercise 1.1 )
val firstList : int list
Full name: Index.firstList
val secondList : int list
Full name: Index.secondList
val concatenatedList : int list
Full name: Index.concatenatedList
val collectLeaves : tree:Tree -> int list
Full name: Index.collectLeaves
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
val v : int
val ( example 1.2 ) : int list
Full name: Index.( example 1.2 )
val collectInOrder : tree:Tree -> int list
Full name: Index.collectInOrder
val ( exercise 1.2 ) : int list
Full name: Index.( exercise 1.2 )
val isSorted : tree:Tree -> bool
Full name: Index.isSorted
type bool = System.Boolean
Full name: Microsoft.FSharp.Core.bool
val ( exercise 1.3 ) : bool
Full name: Index.( exercise 1.3 )
val incrementValues : tree:Tree -> Tree
Full name: Index.incrementValues
val ( example 1.4 ) : int list
Full name: Index.( example 1.4 )
val insertBST : value:int -> tree:Tree -> Tree
Full name: Index.insertBST
val value : int
val ( exercise 1.4 ) : int list
Full name: Index.( exercise 1.4 )
type Figure =
| Two
| Three
| Four
| Five
| Six
| Seven
| Eight
| Nine
| Ten
| Jack
...
Full name: Index.Figure
union case Figure.Two: Figure
union case Figure.Three: Figure
union case Figure.Four: Figure
union case Figure.Five: Figure
union case Figure.Six: Figure
union case Figure.Seven: Figure
union case Figure.Eight: Figure
union case Figure.Nine: Figure
union case Figure.Ten: Figure
union case Figure.Jack: Figure
union case Figure.Queen: Figure
union case Figure.King: Figure
union case Figure.Ace: Figure
type Suit =
| Diamonds
| Spades
| Hearts
| Clubs
Full name: Index.Suit
union case Suit.Diamonds: Suit
union case Suit.Spades: Suit
union case Suit.Hearts: Suit
union case Suit.Clubs: Suit
type Card = Figure * Suit
Full name: Index.Card
type Hand = Card list
Full name: Index.Hand
val oddNumbers : int list
Full name: Index.oddNumbers
Multiple items
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
val filter : predicate:('T -> bool) -> list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.filter
val n : int
val kingSpades : Figure * Suit
Full name: Index.kingSpades
val figure : Figure
Full name: Index.figure
val suit : Suit
Full name: Index.suit
val isKingSpades : bool
Full name: Index.isKingSpades
val queenHearts : Figure * Suit
Full name: Index.queenHearts
val queen : Figure
Full name: Index.queen
val fst : tuple:('T1 * 'T2) -> 'T1
Full name: Microsoft.FSharp.Core.Operators.fst
val hearts : Suit
Full name: Index.hearts
val snd : tuple:('T1 * 'T2) -> 'T2
Full name: Microsoft.FSharp.Core.Operators.snd
val threeKings : (Figure * Suit) list
Full name: Index.threeKings
val ( example 2.1 ) : bool
Full name: Index.( example 2.1 )
val forall : predicate:('T -> bool) -> list:'T list -> bool
Full name: Microsoft.FSharp.Collections.List.forall
val figure : Figure
val suit : Suit
val handFlush : (Figure * Suit) list
Full name: Index.handFlush
val isFlush : hand:Hand -> bool
Full name: Index.isFlush
val hand : Hand
val ( exercise 2.1 ) : bool
Full name: Index.( exercise 2.1 )
val mapModThree : bool list
Full name: Index.mapModThree
val map : mapping:('T -> 'U) -> list:'T list -> 'U list
Full name: Microsoft.FSharp.Collections.List.map
val groupModThree : (int * int list) list
Full name: Index.groupModThree
val groupBy : projection:('T -> 'Key) -> list:'T list -> ('Key * 'T list) list (requires equality)
Full name: Microsoft.FSharp.Collections.List.groupBy
val ( example 2.2 ) : (char * int) list
Full name: Index.( example 2.2 )
val word : string
System.String.ToCharArray() : char []
System.String.ToCharArray(startIndex: int, length: int) : char []
val letter : char
val words : string list
property List.Length: int
val handFullHouse : (Figure * Suit) list
Full name: Index.handFullHouse
val isFullHouse : hand:Hand -> bool
Full name: Index.isFullHouse
val ( exercise 2.2 ) : bool
Full name: Index.( exercise 2.2 )
type Point =
{X: float;
Y: float;}
Full name: Index.Point
Point.X: float
Point.Y: float
type CenteredShape =
{Shape: Shape;
Center: Point;}
Full name: Index.CenteredShape
Multiple items
CenteredShape.Shape: Shape
--------------------
type Shape =
| Square of edge: float
| Rectangle of width: float * height: float
| Circle of radius: float
Full name: Index.Shape
CenteredShape.Center: Point
val point : Point
Full name: Index.point
val shape : CenteredShape
Full name: Index.shape
type Shape =
| Square of edge: float
| Rectangle of width: float * height: float
| Circle of radius: float
Full name: Index.Shape
val pointX : float
Full name: Index.pointX
val shapeField : Shape
Full name: Index.shapeField
CenteredShape.Shape: Shape
val shapesAreEqual : bool
Full name: Index.shapesAreEqual
val forthAndBack : float list
Full name: Index.forthAndBack
val x : float
val sqrt : value:'T -> 'U (requires member Sqrt)
Full name: Microsoft.FSharp.Core.Operators.sqrt
val withCenterIn : point:Point -> shapes:CenteredShape list -> CenteredShape list
Full name: Index.withCenterIn
val point : Point
val shapes : CenteredShape list
val shape : CenteredShape
val ( example 2.3 ) : CenteredShape list
Full name: Index.( example 2.3 )
val isCircumcircle : centeredCircle:CenteredShape -> centeredShape:CenteredShape -> bool
Full name: Index.isCircumcircle
val centeredCircle : CenteredShape
val centeredShape : CenteredShape
val ( exercise 2.3 ) : bool list
Full name: Index.( exercise 2.3 )
val first : CenteredShape
val second : CenteredShape
val zeroSquare : CenteredShape
Full name: Index.zeroSquare
val squareMoved : CenteredShape
Full name: Index.squareMoved
val circleWithSameCenter : CenteredShape
Full name: Index.circleWithSameCenter
val translate : vectorPoint:Point -> shape:CenteredShape -> CenteredShape
Full name: Index.translate
val vectorPoint : Point
val ( example 2.4 ) : CenteredShape list
Full name: Index.( example 2.4 )
val scale : magnitude:float -> centeredShape:CenteredShape -> CenteredShape
Full name: Index.scale
val magnitude : float
val ( exercise 2.4 ) : CenteredShape list
Full name: Index.( exercise 2.4 )
Multiple items
val char : char
--------------------
type char = System.Char
Full name: Microsoft.FSharp.Core.char
val zero : int
namespace System
type Convert =
static val DBNull : obj
static member ChangeType : value:obj * typeCode:TypeCode -> obj + 3 overloads
static member FromBase64CharArray : inArray:char[] * offset:int * length:int -> byte[]
static member FromBase64String : s:string -> byte[]
static member GetTypeCode : value:obj -> TypeCode
static member IsDBNull : value:obj -> bool
static member ToBase64CharArray : inArray:byte[] * offsetIn:int * length:int * outArray:char[] * offsetOut:int -> int + 1 overload
static member ToBase64String : inArray:byte[] -> string + 3 overloads
static member ToBoolean : value:obj -> bool + 17 overloads
static member ToByte : value:obj -> byte + 18 overloads
...
Full name: System.Convert
System.Convert.ToInt32(value: System.DateTime) : int
(+0 other overloads)
System.Convert.ToInt32(value: string) : int
(+0 other overloads)
System.Convert.ToInt32(value: decimal) : int
(+0 other overloads)
System.Convert.ToInt32(value: float) : int
(+0 other overloads)
System.Convert.ToInt32(value: float32) : int
(+0 other overloads)
System.Convert.ToInt32(value: uint64) : int
(+0 other overloads)
System.Convert.ToInt32(value: int64) : int
(+0 other overloads)
System.Convert.ToInt32(value: int) : int
(+0 other overloads)
System.Convert.ToInt32(value: uint32) : int
(+0 other overloads)
System.Convert.ToInt32(value: uint16) : int
(+0 other overloads)
type Char =
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 1 overload
static val MaxValue : char
static val MinValue : char
static member ConvertFromUtf32 : utf32:int -> string
static member ConvertToUtf32 : highSurrogate:char * lowSurrogate:char -> int + 1 overload
static member GetNumericValue : c:char -> float + 1 overload
...
end
Full name: System.Char
System.Char.IsDigit(c: char) : bool
System.Char.IsDigit(s: string, index: int) : bool
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val digit : string
Full name: Index.digit
active recognizer Digit: char -> int option
Full name: Index.( |Digit|_| )
val x : int
System.Int32.ToString() : string
System.Int32.ToString(provider: System.IFormatProvider) : string
System.Int32.ToString(format: string) : string
System.Int32.ToString(format: string, provider: System.IFormatProvider) : string
val matchNext5 : list:int list -> int list
Full name: Index.matchNext5
Multiple items
val list : int list
--------------------
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
val rest : int list
val ( example 3.1 ) : int list
Full name: Index.( example 3.1 )
val parseScore : chars:char list -> Option<int> list
Full name: Index.parseScore
val chars : char list
Multiple items
val char : value:'T -> char (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.char
--------------------
type char = System.Char
Full name: Microsoft.FSharp.Core.char
module Option
from Microsoft.FSharp.Core
val ( exercise 3.1 ) : Option<int> list
Full name: Index.( exercise 3.1 )
val onlyEvenNumber : optNumber:int option -> string
Full name: Index.onlyEvenNumber
val optNumber : int option
val onlyEvenNumbers : string list
Full name: Index.onlyEvenNumbers
val numTriangle : numbers:int list -> int list
Full name: Index.numTriangle
val numbers : int list
val first : int
val second : int
val triangle : int list
Full name: Index.triangle
val countScore : scores:int list -> int
Full name: Index.countScore
val scores : int list
val ( exercise 3.2 ) : int list
Full name: Index.( exercise 3.2 )
val sequence : optionals:'a option list -> 'a list option
Full name: Index.sequence
val optionals : 'a option list
type 'T option = Option<'T>
Full name: Microsoft.FSharp.Core.option<_>
val sequence' : ('b list option -> 'b option list -> 'b list option)
val acc : 'b list option
val optionals : 'b option list
val map : mapping:('T -> 'U) -> option:'T option -> 'U option
Full name: Microsoft.FSharp.Core.Option.map
val rev : list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.rev
val h : 'b
val t : 'b option list
val acc : 'b list
val oneOption : string list option
Full name: Index.oneOption
val bowlingScore : score:string -> Option<int>
Full name: Index.bowlingScore
val score : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
val ( homework 1 ) : Option<int> list
Full name: Index.( homework 1 )
val parseScoreTail : chars:char list -> acc:Option<int> list -> Option<int> list
Full name: Index.parseScoreTail
val acc : Option<int> list
val countScoreTail : scores:int list -> acc:int -> int
Full name: Index.countScoreTail
val acc : int
val bowlingScoreTail : score:string -> Option<int>
Full name: Index.bowlingScoreTail
val ( homework 2 ) : Option<int>
Full name: Index.( homework 2 )
F# CAMP
Functional Data Structures
1:
|
git clone https://github.com/theimowski/fsharp-workshops-data.git
|
or download ZIP from here, then in Command Prompt:
1:
2:
|
cd fsharp-workshops-data
.\build.cmd KeepRunning
|
slides are regenerated when the script (.\slides\index.fsx) is saved
==> NOTE: this is a different GIT repository, don't mix it with the one from previous workshops
Agenda
- Sum Types (Discriminated Unions)
- Product Types (Tuples, Records)
- Lists
- Homework
Sum Types
Discriminated Unions
New Stuff 1.1
Discriminated Unions reminder
1:
2:
3:
4:
5:
|
type Shape =
| Square of edge : float
// `*` in type declarations stands for tuples
| Rectangle of width : float * height : float
| Circle of radius : float
|
Binary Tree as DU
1:
2:
3:
|
type Tree =
| Empty
| Node of value: int * left: Tree * right: Tree
|
Binary Tree as DU
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
let tree =
Node (8,
Node (3,
Node (1, Empty, Empty),
Node (6,
Node (4, Empty, Empty),
Node (7, Empty, Empty))),
Node (10,
Empty,
Node (14,
Node (13, Empty, Empty),
Empty)))
|
printf and sprintf for debugging
1:
2:
3:
|
printf "This is what a tree looks like: %A" tree
let sprintfResult = sprintf "%A" tree
|
Value of sprintfResult
"Node
(8,
Node
(3,Node (1,Empty,Empty),Node (6,Node (4,Empty,Empty),Node (7,Empty,Empty))),
Node (10,Empty,Node (14,Node (13,Empty,Empty),Empty)))"
|
Example 1.1
Counting internal nodes (nodes that have at least one non-empty child)
1:
2:
3:
4:
5:
6:
7:
8:
|
let rec countInternal (tree: Tree) : int =
match tree with
| Empty -> 0
| Node (_, Empty, Empty) -> 0
| Node (_, left, right) ->
1 + countInternal left + countInternal right
let ``example 1.1`` = countInternal tree
|
Value of example 1.1
Sidenote: How to make countInternal
tail-recursive?
Exercise 1.1
Sum values of all leaves in tree
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let rec sumLeaves (tree: Tree) : int =
0
let ``exercise 1.1`` = sumLeaves tree
|
Value of exercise 1.1
New Stuff 1.2
List concatenation operator
1:
2:
3:
|
let firstList = [1;3;5]
let secondList = [2 .. 10]
let concatenatedList = firstList @ secondList
|
Value of concatenatedList
[1; 3; 5; 2; 3; 4; 5; 6; 7; 8; 9; 10]
|
In-Order traversal
Example 1.2
Collecting leaf values from tree into a list
1:
2:
3:
4:
5:
6:
7:
8:
|
let rec collectLeaves (tree : Tree) : list<int> =
match tree with
| Empty -> []
| Node (v, Empty, Empty) -> [v]
| Node (_, left, right) ->
collectLeaves left @ collectLeaves right
let ``example 1.2`` = collectLeaves tree
|
Value of example 1.2
Exercise 1.2
Collect all values from tree into a list in-order
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let rec collectInOrder (tree : Tree) : list<int> =
[]
let ``exercise 1.2`` = collectInOrder tree
|
Value of exercise 1.2
Exercise 1.3
Check if tree is sorted
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let isSorted (tree: Tree) : bool =
false
let ``exercise 1.3`` = isSorted tree
|
Value of exercise 1.3
Example 1.4
Manipulating the tree (immutability)
1:
2:
3:
4:
5:
6:
7:
|
let rec incrementValues (tree: Tree) : Tree =
match tree with
| Empty -> Empty
| Node (v, left, right) ->
Node (v + 1, incrementValues left, incrementValues right)
let ``example 1.4`` = incrementValues tree |> collectInOrder
|
Value of example 1.4
Exercise 1.4
Insert element into Binary Search Tree
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let rec insertBST (value: int) (tree: Tree) : Tree =
Empty
let ``exercise 1.4`` = insertBST 5 tree |> collectInOrder
|
Value of exercise 1.4
Summary: Sum Types (Discriminated Unions)
- DUs represent distinct cases that sum up to the represented Type
- DUs types can be defined in recursive way (e.g. Node in Tree)
- DUs are immutable - can make a copy, but not mutate
Product Types
Tuples, Records
New Stuff 2.1
Modelling cards
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
type Figure =
| Two | Three | Four | Five
| Six | Seven | Eight | Nine
| Ten | Jack | Queen | King | Ace
type Suit = Diamonds | Spades | Hearts | Clubs
// Type alias for a tuple
type Card = Figure * Suit
// Type alias for a list
type Hand = list<Card>
|
Anonymous (lambda) functions
1:
2:
3:
|
let oddNumbers =
[1 .. 10]
|> List.filter (fun n -> n % 2 = 1)
|
Value of oddNumbers
Pattern matching tuples
1:
2:
3:
4:
5:
6:
|
let kingSpades = King, Spades
let (figure, suit) = kingSpades
let isKingSpades =
match kingSpades with
| King, Spades -> true
| _ -> false
|
Value of figure
Value of suit
Value of isKingSpades
Tuple helper functions
1:
2:
3:
|
let queenHearts = Queen, Hearts
let queen = fst queenHearts
let hearts = snd queenHearts
|
Value of queen
Value of hearts
Example 2.1
Checking all cards
1:
2:
3:
4:
5:
6:
7:
8:
|
let threeKings =
[King, Clubs
King, Diamonds
King, Hearts]
let ``example 2.1`` =
threeKings
|> List.forall (fun (figure,suit) -> figure = King)
|
Value of example 2.1
Poker hands
Exercise 2.1
Check if hand is Flush
--------------- Your code goes below ---------------
1:
2:
3:
4:
5:
6:
|
let handFlush = [King,Clubs;Queen,Clubs;Nine,Clubs;Eight,Clubs;Five,Clubs]
let isFlush (hand: Hand) : bool =
false
let ``exercise 2.1`` = isFlush handFlush
|
Value of exercise 2.1
New Stuff 2.2
List.Map
1:
2:
3:
|
let mapModThree =
[1 .. 10]
|> List.map (fun n -> n % 3 = 0)
|
Value of mapModThree
[false; false; true; false; false; true; false; false; true; false]
|
List.GroupBy
1:
2:
3:
|
let groupModThree =
[1 .. 10]
|> List.groupBy (fun n -> n % 3)
|
Value of groupModThree
[(1, [1; 4; 7; 10]); (2, [2; 5; 8]); (0, [3; 6; 9])]
|
Example 2.2
Counting occurences
1:
2:
3:
4:
|
let ``example 2.2`` =
["Ananas";"Banan";"Agrest";"Cukinia";"Cebula";"Aronia"]
|> List.groupBy (fun word -> word.ToCharArray().[0])
|> List.map (fun (letter,words) -> (letter,words.Length))
|
Value of example 2.2
[('A', 3); ('B', 1); ('C', 2)]
|
Exercise 2.2
Check if hand is Full House
--------------- Your code goes below ---------------
1:
2:
3:
4:
5:
6:
|
let handFullHouse = [King,Clubs;King,Spades;Nine,Clubs;Nine,Diamonds;Nine,Spades]
let isFullHouse (hand: Hand) : bool =
false
let ``exercise 2.2`` = isFullHouse handFullHouse
|
Value of exercise 2.2
New Stuff 2.3
Records
1:
2:
3:
4:
5:
6:
7:
|
type Point =
{ X : float
Y : float }
type CenteredShape =
{ Shape : Shape
Center : Point }
|
Record fields (labeled)
1:
2:
3:
4:
5:
|
let point = { X = 2.0; Y = 4.5 }
let shape = { Shape = Square 3.0; Center = point }
let pointX = point.X
let shapeField = shape.Shape
|
Value of pointX
Value of shapeField
Record structural equality
1:
2:
|
let shapesAreEqual =
shape = { Shape = Square 3.0; Center = point }
|
Value of shapesAreEqual
Power and square root
1:
2:
3:
4:
|
let forthAndBack =
[ 1.0 .. 10.0 ]
|> List.map (fun x -> x ** 2.0)
|> List.map (fun x -> sqrt x)
|
Value of forthAndBack
[1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 9.0; 10.0]
|
Example 2.3
Finding shapes with center in specific point
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
let withCenterIn point shapes =
shapes
|> List.filter (fun shape -> shape.Center = point)
let ``example 2.3`` =
[{ Shape = Circle (sqrt 2.0); Center = { X = 0.0; Y = 0.0 }}
{ Shape = Square 2.0; Center = { X = 0.0; Y = 0.0 }}
{ Shape = Rectangle (3.,4.); Center = { X = 0.0; Y = 1.0 }}]
|> withCenterIn { X = 0.0; Y = 0.0 }
|
Value of example 2.3
[{Shape = Circle 1.414213562;
Center = {X = 0.0;
Y = 0.0;};}; {Shape = Square 2.0;
Center = {X = 0.0;
Y = 0.0;};}]
|
Exercise 2.3
Check if first shape is circumcircle of second shape.
First shape must be a circle, second a square or rectangle
--------------- Your code goes below ---------------
1:
2:
3:
4:
5:
|
let isCircumcircle
(centeredCircle: CenteredShape)
(centeredShape: CenteredShape)
: bool =
false
|
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
|
let ``exercise 2.3`` =
[({ Shape = Circle (sqrt 2.0); Center = { X = 0.0; Y = 0.0 }},
{ Shape = Square 2.0; Center = { X = 0.0; Y = 0.0 }})
({ Shape = Circle (sqrt 2.0); Center = { X = 1.0; Y = 0.0 }},
{ Shape = Square 2.0; Center = { X = 0.0; Y = 0.0 }})
({ Shape = Circle 2.5; Center = { X = 0.0; Y = 0.0 }},
{ Shape = Rectangle (3.,4.); Center = { X = 0.0; Y = 0.0 }})
({ Shape = Circle 2.5; Center = { X = 0.0; Y = 0.0 }},
{ Shape = Rectangle (3.,4.); Center = { X = 0.0; Y = 1.0 }})]
|> List.map (fun (first,second) -> isCircumcircle first second)
|
Value of exercise 2.3
[false; false; false; false]
|
New Stuff 2.4
Record copy-and-update expression
1:
2:
3:
4:
5:
6:
7:
|
let zeroSquare = { Shape = Square 2.0; Center = { X = 0.0; Y = 0.0 } }
let squareMoved =
{ zeroSquare with Center = { X = 2.0; Y = 1.0 } }
let circleWithSameCenter =
{ zeroSquare with Shape = Circle 3.0 }
|
Value of squareMoved
{Shape = Square 2.0;
Center = {X = 2.0;
Y = 1.0;};}
|
Value of circleWithSameCenter
{Shape = Circle 3.0;
Center = {X = 0.0;
Y = 0.0;};}
|
Example 2.4
Translate centered shape
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
let translate (vectorPoint: Point) (shape: CenteredShape) : CenteredShape =
{ shape with Center =
{ X = shape.Center.X + vectorPoint.X;
Y = shape.Center.Y + vectorPoint.Y }}
let ``example 2.4`` =
[{ Shape = Circle (sqrt 2.0); Center = { X = 0.0; Y = 0.0 }}
{ Shape = Square 2.0; Center = { X = 0.0; Y = 3.0 }}
{ Shape = Rectangle (3.,4.); Center = { X = 0.0; Y = 1.0 }}]
|> List.map (translate { X = -2.0; Y = -3.0 })
|
Value of example 2.4
[{Shape = Circle 1.414213562;
Center = {X = -2.0;
Y = -3.0;};}; {Shape = Square 2.0;
Center = {X = -2.0;
Y = 0.0;};}; {Shape = Rectangle (3.0,4.0);
Center = {X = -2.0;
Y = -2.0;};}]
|
Exercise 2.4
Scale centered shape
--------------- Your code goes below ---------------
1:
2:
|
let scale (magnitude: float) (centeredShape: CenteredShape) : CenteredShape =
centeredShape
|
1:
2:
3:
4:
5:
|
let ``exercise 2.4`` =
[{ Shape = Circle (sqrt 2.0); Center = { X = 0.0; Y = 0.0 }}
{ Shape = Square 1.0; Center = { X = 0.0; Y = 3.0 }}
{ Shape = Rectangle (3.,4.); Center = { X = 0.0; Y = 1.0 }}]
|> List.map (scale 2.0)
|
Value of exercise 2.4
[{Shape = Circle 1.414213562;
Center = {X = 0.0;
Y = 0.0;};}; {Shape = Square 1.0;
Center = {X = 0.0;
Y = 3.0;};}; {Shape = Rectangle (3.0,4.0);
Center = {X = 0.0;
Y = 1.0;};}]
|
Summary: Product Types (Tuples, Records)
- Type aliases are used for better understanding of code
- Tuples represent a product of two (or more) types
- Records also represent product of subtypes and provide additional functionality
- Tuples are fine to represent intermediate results, Records better for modelling
Bowling scoring
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
|
"XXXXXXXXXXXX" // 12 rolls: 12 strikes
10+10+10 + 10+10+10 + 10+10+10 + 10+10+10 + 10+10+10 + ... = 300
"9-9-9-9-9-9-9-9-9-9-" // 20 rolls: 10 pairs of 9 and miss
9 + 9 + 9 + 9 + 9 + 9 + 9 + 9 + 9 + 9 = 90
"5/5/5/5/5/5/5/5/5/5/5" // 21 rolls: 10 pairs of 5 and spare, with a final 5
10+5 + 10+5 + 10+5 + 10+5 + 10+5 + ... = 150
"X9/5/72XXX9-8/9/X"
10+9+1 + 9+1+5 + 5+5+7 + 7+2 + 10+10+10 +
10+10+9 + 10+9+0 + 9+0 + 8+2+9 + 9+1+10 = 187
"X4/2-" // What is the score?
|
New Stuff 3.1
Active patterns
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
|
let (|Digit|_|) char =
let zero = System.Convert.ToInt32 '0'
if System.Char.IsDigit char then
Some (System.Convert.ToInt32 char - zero)
else
None
let digit =
match '5' with
| Digit x -> "a digit " + x.ToString()
| _ -> "not a digit"
|
Value of digit
Example 3.1
List pattern match on next value
1:
2:
3:
4:
5:
6:
7:
8:
|
let rec matchNext5 list =
match list with
| [] -> []
| x :: 5 :: rest -> 0 :: 5 :: matchNext5 rest
| x :: rest -> x :: matchNext5 rest
let ``example 3.1`` =
matchNext5 [1..10]
|
Value of example 3.1
[1; 2; 3; 0; 5; 6; 7; 8; 9; 10]
|
Exercise 3.1
Implement parseScore
.
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let rec parseScore (chars: list<char>) : list<Option<int>> =
[]
let ``exercise 3.1`` = parseScore ['X';'4';'/';'2';'-';'N']
|
Value of exercise 3.1
New Stuff 3.2
Pattern match guards (when
keyword)
1:
2:
3:
4:
5:
6:
7:
8:
|
let onlyEvenNumber optNumber =
match optNumber with
| Some n when n % 2 = 0 -> "ok"
| _ -> "wrong"
let onlyEvenNumbers =
[Some 2; Some 3; Some 4; Some 5; None]
|> List.map onlyEvenNumber
|
Value of onlyEvenNumbers
["ok"; "wrong"; "ok"; "wrong"; "wrong"]
|
Symbol alias in pattern matching
1:
2:
3:
4:
5:
6:
7:
8:
|
let rec numTriangle numbers =
match numbers with
| first :: (second :: _ as rest) ->
first + second :: numTriangle rest
| _ ->
[]
let triangle = numTriangle [1 .. 5]
|
Value of triangle
Exercise 3.2
Implement countScore
--------------- Your code goes below ---------------
1:
2:
|
let rec countScore (scores: list<int>) : int =
0
|
1:
2:
3:
4:
5:
6:
|
let ``exercise 3.2`` =
[[10;10;10;10;10;10;10;10;10;10;10;10]
[9;0;9;0;9;0;9;0;9;0;9;0;9;0;9;0;9;0;9;0]
[5;5;5;5;5;5;5;5;5;5;5;5;5;5;5;5;5;5;5;5;5]
[10;9;1;5;5;7;2;10;10;10;9;0;8;2;9;1;10]]
|> List.map countScore
|
Value of exercise 3.2
Summary: Lists
- List are idiomatic for F#
- Pattern matching combined with recursion allow to represent complex list algorithms in elegant way
sequence function
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
let sequence (optionals: list<option<'a>>) : option<list<'a>> =
let rec sequence' acc optionals =
match optionals, acc with
| [],_ ->
Option.map List.rev acc
| Some h :: t, Some acc ->
sequence' (Some (h :: acc)) t
| _ ->
None
sequence' (Some []) optionals
let oneOption = sequence [Some "abc"; Some "def"; Some "ghi"]
|
Value of oneOption
Some ["abc"; "def"; "ghi"]
|
Homework 1
Implement bowlingScore
.
Hint: Use sequence
to convert from list of options to option of list
1:
2:
3:
4:
5:
6:
7:
8:
|
let bowlingScore (score: string) : Option<int> =
Some 0
let ``homework 1`` =
["XXXXXXXXXXXX"
"9-9-9-9-9-9-9-9-9-9-"
"5/5/5/5/5/5/5/5/5/5/5"
"X9/5/72XXX9-8/9/X" ] |> List.map bowlingScore
|
Value of homework 1
[Some 0; Some 0; Some 0; Some 0]
|
Homework 2
Write new, tail-recursive versions of parseScore
and countScore
.
Implement bowlingScoreTail
to use those 2 new functions
1:
2:
3:
4:
5:
|
let rec parseScoreTail
(chars: list<char>)
(acc : list<Option<int>>)
: list<Option<int>> =
[]
|
1:
2:
|
let rec countScoreTail (scores: list<int>) (acc : int) : int =
0
|
1:
2:
3:
4:
|
let bowlingScoreTail (score: string) : Option<int> =
Some 0
let ``homework 2`` = bowlingScoreTail "XXXXXXXXXXXX"
|
Value of homework 2
Summary
- Sum Types (Discriminated Unions)
- Product Types (Tuples, Records)
- Lists