411 REFERENCE · Wikibooks
F Sharp Programming/Control Flow
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
F Sharp Mutable Data Arrays
Control Flow
In all programming languages, control flow refers to the decisions made in code that affect the order in which statements are executed in an application. F#'s imperative control flow elements are similar to those encountered in other languages.
Imperative Programming in a Nutshell
Most programmers coming from a C#, Java, or C++ background are familiar with an imperative style of programming which uses loops, mutable data, and functions with side-effects in applications. While F# primarily encourages the use of a functional programming style, it has constructs which allow programmers to write code in a more imperative style as well. Imperative programming can be useful in the following situations:
Interacting with many objects in the .NET Framework, most of which are inherently imperative.
Interacting with components that depend heavily on side-effects, such as GUIs, I/O, and sockets.
Scripting and prototyping snippets of code.
Initializing complex data structures.
Optimizing blocks of code where an imperative version of an algorithm is more efficient than the functional version.
if/then Decisions
F#'s if/then/elif/else construct has already been seen earlier in this book, but to introduce it more formally, the if/then construct has the following syntaxes:
(* simple if *)
if expr then
expr
(* binary if *)
if expr then
expr
else
expr
(* multiple else branches *)
if expr then
expr
elif expr then
expr
elif expr then
expr
...
else
expr
Like all F# blocks, the scope of an if statement extends to any code indented under it. For example:
open System
let printMessage condition =
if condition then
printfn "condition = true: inside the 'if'"
printfn "outside the 'if' block"
let main() =
printMessage true
printfn "--------"
printMessage false
Console.ReadKey(true) |> ignore
main()
This program prints:
condition = true: inside the 'if'
outside the 'if' block
--------
outside the 'if' block
Working With Conditions
F# has three boolean operators:
Symbol Description Example && Logical AND (infix, short-circuited) true && false (* returns false *) || Logical OR (infix, short-circuited) true || false (* returns true *) not Logical NOT not false (* returns true *)
The && and || operators are short-circuited, meaning the CLR will perform the minimum evaluation necessary to determine whether the condition will succeed or fail. For example, if the left side of an && evaluates to false, then there is no need to evaluate the right side; likewise, if the left side of a || evaluates to true, then there is no need to evaluate the right side of the expression.
Here is a demonstration of short-circuiting in F#:
open System
let alwaysTrue() =
printfn "Always true"
true
let alwaysFalse() =
printfn "Always false"
false
let main() =
let testCases =
["alwaysTrue && alwaysFalse", fun() -> alwaysTrue() && alwaysFalse();
"alwaysFalse && alwaysTrue", fun() -> alwaysFalse() && alwaysTrue();
"alwaysTrue || alwaysFalse", fun() -> alwaysTrue() || alwaysFalse();
"alwaysFalse || alwaysTrue", fun() -> alwaysFalse() || alwaysTrue();]
testCases |> List.iter (fun (msg, res) ->
printfn "%s: %b" msg (res())
printfn "-------")
Console.ReadKey(true) |> ignore
main()
The alwaysTrue and alwaysFalse methods return true and false respectively, but they also have a side-effect of printing a message to the console whenever the functions are evaluated.
This program outputs the following:
Always true
Always false
alwaysTrue && alwaysFalse: false
-------
Always false
alwaysFalse && alwaysTrue: false
-------
Always true
alwaysTrue || alwaysFalse: true
-------
Always false
Always true
alwaysFalse || alwaysTrue: true
-------
As you can see above, the expression alwaysTrue && alwaysFalse evaluates both sides of the expression. alwaysFalse && alwaysTrue only evaluates the left side of the expression; since the left side returns false, its unnecessary to evaluate the right side.
for Loops Over Ranges
for loops are traditionally used to iterate over a well-defined integer range. The syntax of a for loop is defined as:
for var = start-expr to end-expr do
... // loop body
Here's a trivial program which prints out the numbers 1 - 10:
let main() =
for i = 1 to 10 do
printfn "i: %i" i
main()
This program outputs:
i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
i: 10
This code takes input from the user to compute an average:
open System
let main() =
Console.WriteLine("This program averages numbers input by the user.")
Console.Write("How many numbers do you want to add? ")
let mutable sum = 0
let numbersToAdd = Console.ReadLine() |> int
for i = 1 to numbersToAdd do
Console.Write("Input #{0}: ", i)
let input = Console.ReadLine() |> int
sum <- sum + input
let average = sum / numbersToAdd
Console.WriteLine("Average: {0}", average)
main()
This program outputs:
This program averages numbers input by the user.
How many numbers do you want to add? 3
Input #1: 100
Input #2: 90
Input #3: 50
Average: 80
for Loops Over Collections and Sequences
Its often convenient to iterate over collections of items using the syntax:
for pattern in expr do
... // loop body
For example, we can print out a shopping list in fsi:
> let shoppingList =
["Tofu", 2, 1.99;
"Seitan", 2, 3.99;
"Tempeh", 3, 2.69;
"Rice milk", 1, 2.95;];;
val shoppingList : (string * int * float) list
> for (food, quantity, price) in shoppingList do
printfn "food: %s, quantity: %i, price: %g" food quantity price;;
food: Tofu, quantity: 2, price: 1.99
food: Seitan, quantity: 2, price: 3.99
food: Tempeh, quantity: 3, price: 2.69
food: Rice milk, quantity: 1, price: 2.95
while Loops
As the name suggests, while loops will repeat a block of code indefinitely while a particular condition is true. The syntax of a while loop is defined as follows:
while expr do
... // loop body
We use a while loop when we don't know how many times to execute a block of code. For example, lets say we wanted the user to guess a password to a secret area; the user could get the password right on the first try, or the user could try millions of passwords, we just don't know. Here is a short program that requires a user to guess a password correctly in at most 3 attempts:
open System
let main() =
let password = "monkey"
let mutable guess = String.Empty
let mutable attempts = 0
while password <> guess && attempts < 3 do
Console.Write("What's the password? ")
attempts <- attempts + 1
guess <- Console.ReadLine()
if password = guess then
Console.WriteLine("You got the password right!")
else
Console.WriteLine("You didn't guess the password")
Console.ReadKey(true) |> ignore
main()
This program outputs the following:
What's the password? kitty
What's the password? monkey
You got the password right!
F Sharp Mutable Data Arrays