val example : string
Full name: Index.example
val exercise : string
Full name: Index.exercise
val value : int
Full name: Index.value
val stringValue : string
Full name: Index.stringValue
val integerValue : int
Full name: Index.integerValue
val ( I can have spaces ) : string
Full name: Index.( I can have spaces )
val helloWorld : string
Full name: Index.helloWorld
val replaced : string
Full name: Index.replaced
System.String.Replace(oldValue: string, newValue: string) : string
System.String.Replace(oldChar: char, newChar: char) : string
val substring : string
Full name: Index.substring
System.String.Substring(startIndex: int) : string
System.String.Substring(startIndex: int, length: int) : string
System.String.IndexOf(value: string) : int
System.String.IndexOf(value: char) : int
System.String.IndexOf(value: string, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int) : int
System.String.IndexOf(value: char, startIndex: int) : int
System.String.IndexOf(value: string, startIndex: int, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int, count: int) : int
System.String.IndexOf(value: char, startIndex: int, count: int) : int
System.String.IndexOf(value: string, startIndex: int, count: int, comparisonType: System.StringComparison) : int
val ( example 1.1 ) : string
Full name: Index.( example 1.1 )
System.String.ToLower() : string
System.String.ToLower(culture: System.Globalization.CultureInfo) : string
val initial : decimal
Full name: Index.initial
val afterFirstYear : int
Full name: Index.afterFirstYear
val ( exercise 1.1 ) : int
Full name: Index.( exercise 1.1 )
val range : int list
Full name: Index.range
val add : x:int -> y:int -> int
Full name: Index.add
val x : int
val y : int
val addResult : int
Full name: Index.addResult
val pipeResult : int
Full name: Index.pipeResult
val lengths : int list
Full name: Index.lengths
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 map : mapping:('T -> 'U) -> list:'T list -> 'U list
Full name: Microsoft.FSharp.Collections.List.map
val s : string
property System.String.Length: int
val isOdd : number:int -> bool
Full name: Index.isOdd
val number : int
val ( example 1.2 ) : string
Full name: Index.( example 1.2 )
val filter : predicate:('T -> bool) -> list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.filter
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
module String
from Microsoft.FSharp.Core
val concat : sep:string -> strings:seq<string> -> string
Full name: Microsoft.FSharp.Core.String.concat
val filter : (('a -> bool) -> seq<'a> -> 'a0)
val predicate : ('a -> bool)
type bool = System.Boolean
Full name: Microsoft.FSharp.Core.bool
val sequence : seq<'a>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
val isOdd : ('a -> bool)
val number : 'a
val ( exercise 1.2 ) : int
Full name: Index.( exercise 1.2 )
val writeLine : unit
Full name: Index.writeLine
namespace System
type Console =
static member BackgroundColor : ConsoleColor with get, set
static member Beep : unit -> unit + 1 overload
static member BufferHeight : int with get, set
static member BufferWidth : int with get, set
static member CapsLock : bool
static member Clear : unit -> unit
static member CursorLeft : int with get, set
static member CursorSize : int with get, set
static member CursorTop : int with get, set
static member CursorVisible : bool with get, set
...
Full name: System.Console
System.Console.WriteLine() : unit
(+0 other overloads)
System.Console.WriteLine(value: string) : unit
(+0 other overloads)
System.Console.WriteLine(value: obj) : unit
(+0 other overloads)
System.Console.WriteLine(value: uint64) : unit
(+0 other overloads)
System.Console.WriteLine(value: int64) : unit
(+0 other overloads)
System.Console.WriteLine(value: uint32) : unit
(+0 other overloads)
System.Console.WriteLine(value: int) : unit
(+0 other overloads)
System.Console.WriteLine(value: float32) : unit
(+0 other overloads)
System.Console.WriteLine(value: float) : unit
(+0 other overloads)
System.Console.WriteLine(value: decimal) : unit
(+0 other overloads)
val writeLineIsUnit : bool
Full name: Index.writeLineIsUnit
val day : string
Full name: Index.day
Multiple items
type DateTime =
struct
new : ticks:int64 -> DateTime + 10 overloads
member Add : value:TimeSpan -> DateTime
member AddDays : value:float -> DateTime
member AddHours : value:float -> DateTime
member AddMilliseconds : value:float -> DateTime
member AddMinutes : value:float -> DateTime
member AddMonths : months:int -> DateTime
member AddSeconds : value:float -> DateTime
member AddTicks : value:int64 -> DateTime
member AddYears : value:int -> DateTime
...
end
Full name: System.DateTime
--------------------
System.DateTime()
(+0 other overloads)
System.DateTime(ticks: int64) : unit
(+0 other overloads)
System.DateTime(ticks: int64, kind: System.DateTimeKind) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, calendar: System.Globalization.Calendar) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: System.DateTimeKind) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: System.Globalization.Calendar) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: System.DateTimeKind) : unit
(+0 other overloads)
property System.DateTime.Now: System.DateTime
property System.DateTime.DayOfWeek: System.DayOfWeek
type DayOfWeek =
| Sunday = 0
| Monday = 1
| Tuesday = 2
| Wednesday = 3
| Thursday = 4
| Friday = 5
| Saturday = 6
Full name: System.DayOfWeek
field System.DayOfWeek.Thursday = 4
val day2 : string
Full name: Index.day2
val today : System.DayOfWeek
val workDay : System.DayOfWeek option
Full name: Index.workDay
field System.DayOfWeek.Saturday = 6
field System.DayOfWeek.Sunday = 0
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val length : value:string -> int
Full name: Index.length
val value : string
val lengthOfWord : int
Full name: Index.lengthOfWord
val parseBool : value:string -> bool option
Full name: Index.parseBool
val lowercase : string
System.String.ToLowerInvariant() : string
val ( example 2.1 ) : bool option
Full name: Index.( example 2.1 )
val parseNumber : value:string -> Option<int>
Full name: Index.parseNumber
module Option
from Microsoft.FSharp.Core
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 ( exercise 2.1 ) : Option<int>
Full name: Index.( exercise 2.1 )
val intArray : int []
Full name: Index.intArray
val rangeArray : int []
Full name: Index.rangeArray
val isPalindrome : value:string -> bool
Full name: Index.isPalindrome
val charList : char []
System.String.ToCharArray() : char []
System.String.ToCharArray(startIndex: int, length: int) : char []
val reversed : char []
module Array
from Microsoft.FSharp.Collections
val rev : array:'T [] -> 'T []
Full name: Microsoft.FSharp.Collections.Array.rev
val ( example 2.2 ) : bool
Full name: Index.( example 2.2 )
val splitBy : separator:char -> str:string -> string list
Full name: Index.splitBy
val separator : char
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
val str : string
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
val ( exercise 2.2 ) : string list
Full name: Index.( exercise 2.2 )
type Size =
| Small
| Medium
| Large
Full name: Index.Size
union case Size.Small: Size
union case Size.Medium: Size
union case Size.Large: Size
type Shape =
| Square of edge: float
| Rectangle of width: float * height: float
| Circle of radius: float
Full name: Index.Shape
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 Result =
| Success
| ErrorMessage of string
Full name: Index.Result
union case Result.Success: Result
union case Result.ErrorMessage: string -> Result
type FruitType =
| Banana
| Apple
| Grapefruit
Full name: Index.FruitType
union case FruitType.Banana: FruitType
union case FruitType.Apple: FruitType
union case FruitType.Grapefruit: FruitType
type Meal =
| Fruit of FruitType
| Sandwich
| FastFood of string
Full name: Index.Meal
union case Meal.Fruit: FruitType -> Meal
union case Meal.Sandwich: Meal
union case Meal.FastFood: string -> Meal
val ( example 3.1 ) : Meal list
Full name: Index.( example 3.1 )
type Operator = | Int
Full name: Index.Operator
union case Operator.Int: Operator
type Symbol = | Int
Full name: Index.Symbol
union case Symbol.Int: Symbol
val formatOptionalValue : optionalValue:string option -> string
Full name: Index.formatOptionalValue
val optionalValue : string option
val formattedValues : string list
Full name: Index.formattedValues
val area : shape:Shape -> float
Full name: Index.area
val shape : Shape
val edge : float
val width : float
val height : float
val radius : float
type Math =
static val PI : float
static val E : float
static member Abs : value:sbyte -> sbyte + 6 overloads
static member Acos : d:float -> float
static member Asin : d:float -> float
static member Atan : d:float -> float
static member Atan2 : y:float * x:float -> float
static member BigMul : a:int * b:int -> int64
static member Ceiling : d:decimal -> decimal + 1 overload
static member Cos : d:float -> float
...
Full name: System.Math
field System.Math.PI = 3.14159265359
val ( example 3.2 ) : float
Full name: Index.( example 3.2 )
val apply : operator:Operator -> left:int -> right:int -> int
Full name: Index.apply
val operator : Operator
val left : int
val right : int
val ( exercise 3.2 ) : int
Full name: Index.( exercise 3.2 )
val patternMatchString : value:string -> string
Full name: Index.patternMatchString
val x : string
val matchAnimal : string
Full name: Index.matchAnimal
val matchFloatingPoint : value:string -> float
Full name: Index.matchFloatingPoint
val y : string
val isHealthy : meal:Meal -> bool
Full name: Index.isHealthy
val meal : Meal
val restaurant : string
val ( example 3.3 ) : bool
Full name: Index.( example 3.3 )
val parseSymbol : token:string -> Option<Symbol>
Full name: Index.parseSymbol
val token : string
val ( exercise 3.3 ) : Option<Symbol> list
Full name: Index.( exercise 3.3 )
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 h : 'a
val t : 'a option list
val map : mapping:('T -> 'U) -> option:'T option -> 'U option
Full name: Microsoft.FSharp.Core.Option.map
val t : 'a list
val parseSymbols : expression:string -> Option<Symbol list>
Full name: Index.parseSymbols
val expression : string
val ( exercise 3.4 ) : Option<Symbol list>
Full name: Index.( exercise 3.4 )
val countdown : counter:int -> string
Full name: Index.countdown
val counter : 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 counting : string
Full name: Index.counting
val countdownAcc : acc:string -> counter:int -> string
Full name: Index.countdownAcc
val acc : string
val countingAcc : string
Full name: Index.countingAcc
val factorial : x:int -> int
Full name: Index.factorial
val factorialOf5 : int
Full name: Index.factorialOf5
val factorialTail : acc:int -> x:int -> int
Full name: Index.factorialTail
val acc : int
val factorialOf5Tail : int
Full name: Index.factorialOf5Tail
val commaSeparated : acc:string -> list:string list -> string
Full name: Index.commaSeparated
Multiple items
val list : string list
--------------------
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
Multiple items
val single : string
--------------------
type single = System.Single
Full name: Microsoft.FSharp.Core.single
val head : string
val tail : string list
val csv : string
Full name: Index.csv
val formatOptionalInts : acc:string -> ints:int option list -> string
Full name: Index.formatOptionalInts
val ints : int option list
val rest : int option list
val optionalInts : string
Full name: Index.optionalInts
val partitionEvenOdd : even:int list -> odd:int list -> numbers:int list -> int list * int list
Full name: Index.partitionEvenOdd
val even : int list
val odd : int list
val numbers : int list
val h : int
val tail : int list
val ( example 4.1 ) : int list * int list
Full name: Index.( example 4.1 )
val compute : stack:int list -> symbols:Symbol list -> Option<int>
Full name: Index.compute
val stack : int list
val symbols : Symbol list
val ( exercise 4.1 ) : Option<int>
Full name: Index.( exercise 4.1 )
val onp : expression:string -> Option<int>
Full name: Index.onp
val ( exercise 4.2 ) : Option<int>
Full name: Index.( exercise 4.2 )
F# CAMP
Basic concepts of Functional Programming
1:
|
git clone https://github.com/theimowski/fsharp-workshops-basics.git
|
or download ZIP from here, then in Command Prompt:
1:
2:
|
cd fsharp-workshops-basics
.\build.cmd KeepRunning
|
slides are regenerated when the script (.\slides\index.fsx) is saved
Agenda
- Intro - workshop format
- Immutable values
- Expressions
- Pattern matching
- Recursion
Problem to solve
Evaluate expression written in ONP notation
Conventional notation:
ONP notation:
1:
|
2 7 + 3 / 14 3 - 4 * + 3 +
|
Parse input ==> Evaluate
New Stuff X.X
Something new
Example X.X
Some example
1:
|
let example = "example"
|
Value of example
Exercise X.X
Exercise:
--------------- Your code goes below ---------------
1:
|
let exercise = "exercise"
|
Value of exercise
Editors + tooling - demo
- Visual Studio with F# + Visual F# Power Tools
- Visual Studio Code + Ionide extension
http://fsharp.org/use/windows/
- Running code in FSI (FSharp Interactive)
- Regenerating the slides
- DEMO: running the code (up to current point) in VS and VS Code
Summary/links go at the end of each part
Most links point to Scott Wlaschin blog
New Stuff 1.1
Let bindings
Type inference
1:
2:
|
let stringValue = "Hi there"
let integerValue = 100
|
Backticks names
1:
|
let ``I can have spaces`` = "I really do"
|
Example 1.1
Processing an immutable value
System.String
is an example of immutable type in .NET
1:
2:
3:
4:
|
let helloWorld = "Hello World from F# program!"
let replaced = helloWorld.Replace("o", "u")
let substring = replaced.Substring(0, replaced.IndexOf("#") + 1)
let ``example 1.1`` = substring.ToLower()
|
Value of example 1.1
Exercise 1.1
Compound interest: compute earnings after 3 years of depositing $1000 on a 10% annual savings account:
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let initial = 1000.0M
let afterFirstYear = 0
// and so on ...
let ``exercise 1.1`` = 0
|
Value of exercise 1.1
New Stuff 1.2
Range operator
1:
|
let range = [28 .. 38]
|
[28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38]
|
Function declaration and application
1:
2:
3:
4:
|
let add x y =
x + y
let addResult = add 5 6
|
Pipe operator
1:
2:
3:
4:
|
let pipeResult =
10
|> add 15
|> add 6
|
map function
1:
2:
3:
|
let lengths =
["F#"; "is"; "the"; "best"]
|> List.map (fun s -> s.Length)
|
map
is an equivalent of Select()
in C# LINQ
Example 1.2
LINQ-like list processing
Note: a new immutable value is created in each computation step
C# version
1:
2:
3:
4:
5:
6:
7:
8:
|
let isOdd number =
number % 2 = 1
let ``example 1.2`` =
[2 .. 10]
|> List.filter isOdd
|> List.map string
|> String.concat ";"
|
Value of example 1.2
Pipe operator - think "Extension methods"
C#
1:
2:
3:
4:
5:
6:
7:
|
public static IEnumerable<'T> Where (
this IEnumberable<'T> sequence,
Func<'T, bool> predicate) { ... }
public static bool IsOdd (int number) { return number % 2 == 1; }
numbers.Where (IsOdd)
|
F#
1:
2:
3:
4:
5:
6:
7:
|
let filter
(predicate : 'a -> bool)
(sequence : seq<'a>) = ...
let isOdd number = n % 2 = 1
numbers |> filter isOdd
|
Exercise 1.2
Define isEven
function of type int -> bool
and sum even numbers from 2 up to 100.
Hint: Use List.sum
function
--------------- Your code goes below ---------------
1:
|
let ``exercise 1.2`` = 0
|
Value of exercise 1.2
Summary: Immutable Values
- By default values in F# are immutable
- "Pipe" operator (
|>
) is a nice syntactic sugar for writing a sequence of expressions
- Immutable values make it easy to write concurrent code (thread safety for free)
New Stuff 2.1
In F# everything is an expression
1:
2:
3:
4:
5:
6:
7:
|
// no statements - `Console.WriteLine` returns `Unit` ()
let writeLine = System.Console.WriteLine "Hello"
// in let bindings, `=` associates symbol with value
let writeLineIsUnit : bool =
// but anywhere else, `=` means equality test
writeLine = ()
|
If - then - else expression
1:
2:
3:
4:
5:
|
let day =
if System.DateTime.Now.DayOfWeek = System.DayOfWeek.Thursday then
"thursday"
else
"some other day of week"
|
Binding values in inside scope
1:
2:
3:
4:
5:
6:
|
let day2 =
let today = System.DateTime.Now.DayOfWeek
if today = System.DayOfWeek.Thursday then
"thursday"
else
"some other day of week"
|
Option type
1:
2:
3:
4:
5:
6:
7:
|
let workDay =
let today = System.DateTime.Now.DayOfWeek
if today <> System.DayOfWeek.Saturday &&
today <> System.DayOfWeek.Sunday then
Some today
else
None
|
Dot notation, Wrapper functions
1:
2:
3:
4:
|
let length (value : string) =
value.Length
let lengthOfWord = length "Hello word"
|
Example 2.1
Parsing boolean value
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
let parseBool (value : string) =
let lowercase = value.ToLowerInvariant()
if lowercase = "true" then
Some true
elif lowercase = "false" then
Some false
else
None
let ``example 2.1`` = parseBool "True"
|
Value of example 2.1
Exercise 2.1
Implement parseNumber
function.
You might find following functions useful:
ToCharArray()
(String member), Array.forall
, System.Char.IsDigit
, System.Int32.Parse
.
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let parseNumber (value: string) : Option<int> =
None
let ``exercise 2.1`` = parseNumber "42"
|
Value of exercise 2.1
New Stuff 2.2
Array literal
1:
2:
|
let intArray = [|2;4;5|]
let rangeArray = [|10..15|]
|
[|10; 11; 12; 13; 14; 15|]
|
Example 2.2
No return
statements. Last expression is the return value
1:
2:
3:
4:
5:
6:
|
let isPalindrome (value : string) =
let charList = value.ToCharArray()
let reversed = value.ToCharArray() |> Array.rev
charList = reversed // this boolean expression returns value
let ``example 2.2`` = isPalindrome "kajak"
|
Value of example 2.2
Exercise 2.2
Declare splitBy
function - a wrapper function arround Split
method from String
object.
Hints: Use Split
method from String
and Array.toList
function to convert array to list type.
--------------- Your code goes below ---------------
1:
2:
3:
4:
5:
6:
|
let splitBy (separator : char) (str : string) : list<string> =
[]
let ``exercise 2.2`` =
"1,3,5,8,10"
|> splitBy ','
|
Value of exercise 2.2
Summary: Expressions
- There are no statements only expressions in F#
- Therefore no need for
return
keywords - last expression is return value
Option
type is the preffered way to model missing values (as opposed to null
)
New Stuff 3.1
Discriminated Unions - Empty cases (enum style)
1:
2:
3:
4:
|
type Size =
| Small
| Medium
| Large
|
Discriminated Unions - Complex cases
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
type Shape =
| Square of edge : float
// `*` in type declarations stands for tuples
| Rectangle of width : float * height : float
| Circle of radius : float
type Result =
| Success // no string needed for success state
| ErrorMessage of string // error message needed
|
Example 3.1
Modelling with Discriminated Union Types
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
type FruitType =
| Banana
| Apple
| Grapefruit
type Meal =
| Fruit of FruitType
| Sandwich
| FastFood of string
let ``example 3.1`` =
[Sandwich; FastFood "Bar Żuławski"; Fruit Apple]
|
Value of example 3.1
[Sandwich; FastFood "Bar Żuławski"; Fruit Apple]
|
Exercise 3.1
Define Operator
and Symbol
Discriminated Union Types.
Symbol
should use Operator
as field in one case
--------------- Your code goes below ---------------
1:
2:
3:
4:
5:
6:
7:
8:
|
// `Int` is used here only so that the code compiles.
// Remove it and instead define proper Discriminated Union cases:
// Operator might be one of the following: Plus, Minus, Multiply or Divide
type Operator = Int
// Same as above:
// Symbol might be either a NumSymbol (with int) or OpSymbol (with Operator)
type Symbol = Int
|
New Stuff 3.2
Pattern matching expression
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
let formatOptionalValue optionalValue =
match optionalValue with
| Some value ->
"Value: " + value
| None ->
"No value at all!"
let formattedValues =
[Some "nice string"; None]
|> List.map formatOptionalValue
|
["Value: nice string"; "No value at all!"]
|
Example 3.2
Calculating area of Shape
with help of pattern matching
1:
2:
3:
4:
5:
6:
7:
|
let area shape =
match shape with
| Square edge -> edge ** 2.0
| Rectangle (width, height) -> width * height
| Circle radius -> System.Math.PI * (radius ** 2.0)
let ``example 3.2`` = area (Circle 10.0)
|
Value of example 3.2
Exercise 3.2
With help of pattern matching, implement apply
function.
--------------- Your code goes below ---------------
1:
2:
3:
4:
5:
|
let apply (operator : Operator) (left : int) (right : int) : int =
0
// test the function, e.g. `apply Divide 15 4`
let ``exercise 3.2`` = 0
|
Value of exercise 3.2
New Stuff 3.3
Pattern matching strings
1:
2:
3:
4:
5:
6:
7:
|
let patternMatchString value =
match value with
| "Dog" -> "Animal"
| "Cat" -> "Animal"
| x -> "Something different"
let matchAnimal = patternMatchString "Chair"
|
Nested match
expressions
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
let matchFloatingPoint value =
match value with
| "1" -> 1.0
| "2" -> 2.0
| x ->
match x with
| "1.0" -> 1.0
| "2.5" -> 2.5
| y -> 0.0
|
Example 3.3
Nested match
- checking if meal is healthy
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
|
let isHealthy meal =
match meal with
| Fruit _ -> true
| Sandwich _ -> false
| FastFood restaurant ->
// we can further use matched `restaurant` symbol
match restaurant with
| "Green Way" -> true
| _ -> false
let ``example 3.3`` =
FastFood "Bar Żuławski"
|> isHealthy
|
Value of example 3.3
Exercise 3.3
Implement parseSymbol
- try parse all operators first, and then in nested match
expression use parseNumber
function
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let parseSymbol (token : string) : Option<Symbol> =
None
let ``exercise 3.3`` = List.map parseSymbol ["+"; "/"; "12"; "uups"]
|
Value of exercise 3.3
Helper function "sequence"
if all elements are Some values, return Some of those values
otherwise if there's at least one None, return None
1:
2:
3:
4:
5:
6:
7:
8:
|
let rec sequence (optionals: list<option<'a>>) : option<list<'a>> =
match optionals with
| [] ->
Some []
| None :: _ ->
None
| Some h :: t ->
sequence t |> Option.map (fun t -> h :: t)
|
Exercise 3.4
Implement parseSymbols
. Useful functions: List.map
, sequence
as well as splitBy
and parseSymbol
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let parseSymbols (expression: string) : Option<list<Symbol>> =
None
let ``exercise 3.4`` = "1 2 / +" |> parseSymbols
|
Value of exercise 3.4
Summary: Pattern Matching
- Using Discriminated Unions is a neat way to model data
- Pattern matching is a powerful and elegant mechanism in F# for "branching" code
- F# compiler warns when it finds unhandled cases in pattern matching
New Stuff 4.1
Recursive functions
1:
2:
3:
4:
5:
6:
|
let rec countdown counter =
match counter with
| 0 -> ""
| x -> x.ToString() + ";" + countdown (counter - 1)
let counting = countdown 10
|
Tail-recursive functions with accumulator
1:
2:
3:
4:
5:
6:
|
let rec countdownAcc acc counter =
match counter with
| 0 -> acc
| x -> countdownAcc (acc + ";" + x.ToString()) (counter - 1)
let countingAcc = countdownAcc "" 10
|
Factorial
1:
2:
3:
4:
5:
6:
|
let rec factorial x =
match x with
| 1 -> 1
| _ -> (factorial (x-1)) * x
let factorialOf5 = factorial 5
|
Factorial - with accumulator
1:
2:
3:
4:
5:
6:
|
let rec factorialTail acc x =
match x with
| 1 -> acc
| _ -> factorialTail (x*acc) (x-1)
let factorialOf5Tail = factorialTail 1 5
|
Pattern matching lists
1:
2:
3:
4:
5:
6:
7:
|
let rec commaSeparated acc list =
match list with
| [] -> acc
| [single] -> acc + "," + single
| head :: tail -> commaSeparated (acc + "," + head) tail
let csv = commaSeparated "" ["some";"values";"go";"here"]
|
Pattern in pattern
1:
2:
3:
4:
5:
6:
7:
8:
|
let rec formatOptionalInts acc ints =
match ints with
| [] -> acc
| Some 0 :: rest -> formatOptionalInts (acc + " Zero") rest
| Some x :: rest -> formatOptionalInts (acc + " " + x.ToString()) rest
| None :: rest -> formatOptionalInts (acc + " NoValue!") rest
let optionalInts = formatOptionalInts "" [Some 28; Some 0; None]
|
Example 4.1
Recursive call with "accumulators"
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
let rec partitionEvenOdd even odd numbers =
match numbers with
| [] ->
(even, odd)
| h :: tail when h % 2 = 0 ->
partitionEvenOdd (h :: even) odd tail
| h :: tail when h % 2 = 1 ->
partitionEvenOdd even (h :: odd) tail
let ``example 4.1`` = partitionEvenOdd [] [] [1..10]
|
Value of example 4.1
([10; 8; 6; 4; 2], [9; 7; 5; 3; 1])
|
Homework 4.1
Implement compute
function (Wiki). Hint: ::
is "right-associative"
--------------- Your code goes below ---------------
1:
2:
3:
4:
5:
|
let rec compute (stack : list<int>) (symbols : list<Symbol>) : Option<int> =
None
// test the function, e.g. `compute [] [NumSymbol 4; NumSymbol 2; OpSymbol Multiply]`
let ``exercise 4.1`` : Option<int> = None
|
Value of exercise 4.1
Homework 4.2
Using parseSymbols
and compute
, write onp
function
--------------- Your code goes below ---------------
1:
2:
3:
4:
|
let onp (expression : string) : Option<int> =
None
let ``exercise 4.2`` = onp "2 7 + 3 / 14 3 - 4 * + 3 +"
|
Value of exercise 4.2
Summary: Recursion
- Recursion is a preferred way of processing in Functional Programming
- F# provides tail recursion feature for preventing Stack Overflows
- Recursive functions combined with pattern matching result in very concise and declarative code
Summary
- Immutable values
- Expressions
- Pattern matching
- Recursion
Next week
Functional Data Structures
+ Something more?