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)))

bst

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

5

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

0

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

inorder

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

[1; 4; 7; 13]

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

false

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

Links

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

[1; 3; 5; 7; 9]

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

King

Value of suit

Spades

Value of isKingSpades

true

Tuple helper functions

1: 
2: 
3: 
let queenHearts = Queen, Hearts
let queen = fst queenHearts
let hearts = snd queenHearts

Value of queen

Queen

Value of hearts

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

true

Poker hands

handranks

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

false

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

false

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

2.0

Value of shapeField

Square 3.0

Record structural equality

1: 
2: 
let shapesAreEqual = 
    shape = { Shape = Square 3.0; Center = point }

Value of shapesAreEqual

true

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

Links

Lists

Bowling score kata (details)

bowling

Bowling scoring

bowling_score

 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

"a digit 5"

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

[3; 5; 7; 9]

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

[0; 0; 0; 0]

Summary: Lists

  • List are idiomatic for F#
  • Pattern matching combined with recursion allow to represent complex list algorithms in elegant way

Links

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

Some 0

Summary

  • Sum Types (Discriminated Unions)
  • Product Types (Tuples, Records)
  • Lists