Control Flow

F Sharp Programming · Wikibooks

0%

Contents

    Text

    Size
    100%
    Theme
    Typeface
    Line length
    Spacing

    From Wikibooks, under CC BY-SA 4.0, as the source states it. This page at Wikibooks.

    Control Flow
    Previous: Mutable Data Index Next: Arrays
    F# : 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!
    
    Previous: Mutable Data Index Next: Arrays