This will be an overview of basic F# syntax.
To learn F# you can start by doing:
Then do some of the following:
Other resources:
// this is a comment
(* this is also a comment *)
(* this is
a
multiple line
comment *)
Definitions are made using the let
keyword. F# will infer the type based on the values.
let a = 4
let b = 1.5
let c = "hello"
a,b,c
val it : int * float * string = (4, 1.5, "hello")
There are math keywords build into F#.
// exponential power
exp(1.0)
val it : float = 2.718281828
// raise to a power for floats
3.0 ** 2.0
val it : float = 9.0
// raise to a power for integers
pown 3 2
val it : int = 9
// modulus
8%3
val it : int = 2
Lists are defined by brackets [ ]. Also common in F# are arrays [| |] and sequences seq { }.
To define a sequence of numbers you can use start .. end
or if the increment is other than one use start .. increment .. end
.
[1..10]
val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
[10.0 .. -0.1 .. 1.0]
val it : float list = [10.0; 9.9; 9.8; 9.7; 9.6; 9.5; 9.4; 9.3; 9.2; 9.1; 9.0; 8.9; 8.8; 8.7; 8.6; 8.5; 8.4; 8.3; 8.2; 8.1; 8.0; 7.9; 7.8; 7.7; 7.6; 7.5; 7.4; 7.3; 7.2; 7.1; 7.0; 6.9; 6.8; 6.7; 6.6; 6.5; 6.4; 6.3; 6.2; 6.1; 6.0; 5.9; 5.8; 5.7; 5.6; 5.5; 5.4; 5.3; 5.2; 5.1; 5.0; 4.9; 4.8; 4.7; 4.6; 4.5; 4.4; 4.3; 4.2; 4.1; 4.0; 3.9; 3.8; 3.7; 3.6; 3.5; 3.4; 3.3; 3.2; 3.1; 3.0; 2.9; 2.8; 2.7; 2.6; 2.5; 2.4; 2.3; 2.2; 2.1; 2.0; 1.9; 1.8; 1.7; 1.6; 1.5; 1.4; 1.3; 1.2; 1.1; 1.0]
let f x = x + 1
f 4
val it : int = 5
The pipeline operator |>
is used to use the value on the left side of the operator as the input to the function on the right side of the operator. It is used the chain multiple steps together.
let addOne x = x + 1
let timesTwo x = x * 2
3 |> addOne |> timesTwo
val it : int = 8
Anonymous functions allow for the creation of "throw away" or "helper" functions.
The have the syntax of :
fun
arguments -> function_body
4 |> fun x -> x+1
val it : int = 5
http://msdn.microsoft.com/en-us/library/ee353413.aspx
The F# collection modules have many useful methods that you will need to get familiar with. (List, Array, Seq, Array2D, Set, etc.)
The following is just a sample of the methods available.
// filter - filters any values that the condition is true
[1 .. 10] |> List.filter (fun n -> n%2=0)
val it : int list = [2; 4; 6; 8; 10]
// map - maps a function to each element
[1 .. 5] |> List.map (fun n -> n*n)
val it : int list = [1; 4; 9; 16; 25]
// sum - sums the collection, elements must have an add method
[1 .. 5] |> List.sum
val it : int = 15
// sumBy - applies a function to each member before summing
[1 .. 5] |> List.sumBy (fun n -> n*n)
val it : int = 55
// Seq.unfold - returns a sequence generated by a function
Seq.unfold (fun (a,b) -> Some(a,(b,b+a))) (1,2)
|> Seq.take 5 // take first 5 items in sequence
|> Seq.toList // convert to a list
val it : int list = [1; 2; 3; 5; 8]
// infinite sequence of square integers
Seq.initInfinite (fun i -> i*i)
|> Seq.takeWhile (fun n -> n<100) // take all less than 100
|> Seq.sum // sum
val it : int = 285
Seq.unfold
is not as common as map
or sum
methods, but wanted to give a quick example. It does come up fairly regularly in Project Euler problems so a general understanding can help. When you have a numeric series that is dependent on prior terms Seq.unfold
can be helpful. If prior terms are not needed the simplicity of Seq.initInfinite
is probably perfered.
// short version of Fibonacci expansion
Seq.unfold (fun (a,b) -> Some (a,(b,a+b))) (1,1)
val it : seq<int> = seq [1; 1; 2; 3; ...]
// longer version
let nextFib (a,b) =
let result = a
let nextArgs = (b,a+b)
Some (result, nextArgs)
// Seq.unfold folder initialState
// folder is a function of (state -> (result, nextState) option )
// as long as 'Some' is returned the sequence of results is created by
// passing subsequent states into the folder function
Seq.unfold nextFib (1,1)
val it : seq<int>
The match
keyword uses pattern matching and can be thought of a case or switch statement (only more powerful).
let isEven n =
match n with
|x when x%2=0 -> true
|_ -> false
isEven 5
val it : bool = false
The rec
keyword is used to indicate that a function can be called recursively.
let rec factorial_v1 n =
match n with
|0|1 -> 1I
|n -> bigint n * factorial_v1(n-1)
List.init 5 factorial_v1
val it : Numerics.BigInteger list = [1; 1; 2; 6; 24]
// tail recursive version (generally preferred versus prior version)
let factorial_v2 n =
let rec loop x acc =
match x with
|0|1 -> acc * 1I
|n -> loop (x-1) (bigint n*acc)
loop n 1I
List.init 5 factorial_v2
val it : Numerics.BigInteger list = [1; 1; 2; 6; 24]
While not as powerful as the match
keyword, if
... then
can be simplier to implify.
let isEven n =
if n%2 = 0 then
true
else
false
isEven 100
val it : bool = true
A for ... in ... do
loop can be used within a sequence espression to create sequences/lists/arrays on the fly. If there is no if
condition on a yield
then ->
can be used as a replacement of do yield
.
[ for i in 1..10 -> i*i ]
val it : int list = [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
[ for i = 1 to 10 do yield i*i ]
val it : int list = [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]