As previously stated F# allows different styles of programming that is you can use of or all of the following:
In F# you have choices and you are free to mix and match these styles to suit your needs. In this article we will look at flow control statements, which although quite familiar still have a certain shall we say F#ness about them.
In this article we will be looking at the following flow control statements:
- If Then Else
If Then Else
You would have undoubtedly used If-Then-Else flow control expressions in many other languages. What differs in F# is that If Then Else is a statement and not an expression, and as such has certain rules associated with it, such as:
- It must produce a value, which is the value of the last expression in the tree
- The types produced in each branch must be the same
- If there is no explicit else branch the return type is unit, which means if the type of the then branch is anything other than unit, you will need to provide a else branch
- You may use the elif keyword to chain If Then Else together
Lets see some examples of this in action shall we:
From this example it can be seen that we do indeed need to provide the same types in both branches, otherwise we get a compiler error
And here we have another example where we have not supplied an else at all, so we must return unit, but we have broken this rule by attempting to return a string, which again gives us a compiler error
Here is the above example fixed with no compiler warnings:
let returnAString x = if x then "cat" else "dog"
Here is an example that shows a if-elif-else combination.
let compareTheNumbers x y = if x = y then "=" elif x < y then "<" else ">" printfn "%d %s %d" 10 (compareTheNumbers 10 20) 20 printfn "%d %s %d" 20 (compareTheNumbers 20 10) 10 printfn "%d %s %d" 20 (compareTheNumbers 20 20) 2
Which gives this output when run:
Sometimes a better approach would be to not use an If Elif Else at all, but rather use a standard pattern match. Here is an example where we have rewritten the last example but have changed it into a function that takes a tuple and uses pattern matching to achieve the same results as if we had of used a If Elif
let compareTheNumbers x = match x with | (x,y) when x = y -> "=" | (x,y) when x < y -> "<" | (_,_) -> ">" printfn "%d %s %d" 10 (compareTheNumbers (10,20)) 20 printfn "%d %s %d" 20 (compareTheNumbers (20,10)) 10 printfn "%d %s %d" 20 (compareTheNumbers (20,20)) 20
This loop construct is used to iterate over an enumerable collection such as a list/sequence or array. It is similar to foreach in C#.
Here is an example
let sumTheList theList = let mutable total=0 for i in theList do total <- total + i total let theSum = (sumTheList [1..10]) printfn "The sum of [1..10] is %A" theSum
Though as before there are better (more functional) approaches one could take. For example we could use the many functions already existing in the list/sequence modules, which may alleviate the need to start using loops at all. Here is the above example rewritten using the the standard List module List.sum function.
let theSum = List.sum [1..10] printfn "The sum of [1..10] is %A" theSum
It is also possible to do for loops to a certain end condition, and it is also possible to do for loops down to a certain end condition. Here is a small example which demonstrates both of these:
let forUpFunction() = printfn "for i = 1 to 10 do\r\n" for i = 1 to 10 do printf "%d " i printfn "\r\n" let forDownFunction() = printfn "for i = 10 downto 1 do\r\n" for i = 10 downto 1 do printf "%d " i forUpFunction() forDownFunction()
Which when run gives the following output
F# also comes with a familiar While/Do loop construct, which you all would have seen/used in other languages.
Here is a trivial example that prints the index of a number within a source list. This is obviously just for demonstration purposes and you would not use this in real life, you would use List.find and that would be job done, I did however want to demonstrate a while/do loop, so please forgive me.
Here is the code:
let findANumberInAList theList theNum = let mutable index = 0; let mutable found=false let mutable current =0; let listHasItem = (List.exists (fun el -> el = theNum) theList) if listHasItem then while not found do current <- List.nth theList index if(current = theNum) then printfn "Found %A in source list at index %A" theNum index found <- true index <- index + 1 let sourceList = [1..10] printfn "SourceList = %A\r\n" sourceList findANumberInAList [1..10] 4
Which when run produces the following output