Fundamentals of Patterns

Introduction

In previous lessons, to deal with conditions, we created if...else expressions, and their variants. Those operations ask the compiler to consider various situations, one after another. Another, sometimes better, option is to ask the compiler to directly locate the specific situation you want to deal with. This approach is referred to as pattern matching.

Practical LearningPractical Learning: Matching a String

  1. Start Microsoft Visual Studio (if Microsoft Visual Studio was already opened, on its main menu, click File -> New -> Project...) and create a new Console App that supports .NET 7.0 (Standard Term Support) named TaxPreparation05
  2. Change the document as follows:
    printfn "=========================================="
    printfn " - Amazing DeltaX - State Income Tax -"
    printfn "=========================================="
    
    let mutable stateName       = ""
    let mutable taxRate = 0.00
    
    printfn "Enter the information for tax preparation"
    printfn "States"
    printfn " AK - Alaska"
    printfn " AR - Arkansas"
    printfn " CO - Colorado"
    printfn " FL - Florida"
    printfn " GA - Georgia"
    printfn " IL - Illinois"
    printfn " IN - Indiana"
    printfn " KY - Kentucky"
    printfn " MA - Massachusetts"
    printfn " MI - Michigan"
    printfn " MO - Missouri"
    printfn " MS - Mississippi"
    printfn " NV - Nevada"
    printfn " NH - New Hampshire"
    printfn " NC - North Carolina"
    printfn " PA - Pennsylvania"
    printfn " SD - South Dakota"
    printfn " TN - Tennessee"
    printfn " TX - Texas"
    printfn " UT - Utah"
    printfn " WA - Washington"
    printfn " WY - Wyoming"
    printf "Enter State Abbreviation: "
    let abbr : string = stdin.ReadLine()
    printfn "--------------------------------------------"
            
    printf "Gross Salary:             "
    let grossSalary = float(stdin.ReadLine())
    
    let taxAmount = grossSalary * taxRate / 100.00
    let netPay    = grossSalary - taxAmount
    
    printfn "============================================"
    printfn " - Amazing DeltaX - State Income Tax -"
    printfn "--------------------------------------------"
    printfn $"Gross Salary: {grossSalary:f}"
    printfn $"State:        {stateName}"
    printfn "Tax Rate:     %0.2f%c" taxRate '%'
    printfn "--------------------------------------------"
    printfn $"Tax Amount:   {taxAmount:f}"
    printfn $"Net Pay:      {netPay:f}"
    printfn "============================================"
  3. To execute the project, on the main menu, click Debug -> Start Without Debugging
  4. For the State Abbreviation, type FL and press Enter
  5. For the Gross Salary, type 1558.85 and press Enter:
    ==========================================
     - Amazing DeltaX - State Income Tax -
    ==========================================
    Enter the information for tax preparation
    States
     AK - Alaska
     AR - Arkansas
     CO - Colorado
     FL - Florida
     GA - Georgia
     IL - Illinois
     IN - Indiana
     KY - Kentucky
     MA - Massachusetts
     MI - Michigan
     MO - Missouri
     MS - Mississippi
     NV - Nevada
     NH - New Hampshire
     NC - North Carolina
     PA - Pennsylvania
     SD - South Dakota
     TN - Tennessee
     TX - Texas
     UT - Utah
     WA - Washington
     WY - Wyoming
    Enter State Abbreviation: FL
    --------------------------------------------
    Gross Salary: 1558.85
    ============================================
     - Amazing DeltaX - State Income Tax -
    --------------------------------------------
    Gross Salary: 1558.85
    State:
    Tax Rate:     0.00%
    --------------------------------------------
    Tax Amount:   0.00
    Net Pay:      1558.85
    ============================================
    Press any key to close this window . . .
  6. Press any key to close the window and return to your programming environment
  7. To execute the project again, on the main menu, click Debug -> Start Without Debugging
  8. For the State Abbreviation, type KY and press Enter
  9. For the Gross Salary, type 1558.85 and press Enter:
    ==========================================
     - Amazing DeltaX - State Income Tax -
    ==========================================
    Enter the information for tax preparation
    States
     AK - Alaska
     AR - Arkansas
     CO - Colorado
     FL - Florida
     GA - Georgia
     IL - Illinois
     IN - Indiana
     KY - Kentucky
     MA - Massachusetts
     MI - Michigan
     MO - Missouri
     MS - Mississippi
     NV - Nevada
     NH - New Hampshire
     NC - North Carolina
     PA - Pennsylvania
     SD - South Dakota
     TN - Tennessee
     TX - Texas
     UT - Utah
     WA - Washington
     WY - Wyoming
    Enter State Abbreviation: KY
    --------------------------------------------
    Gross Salary:             1558.85
    ============================================
     - Amazing DeltaX - State Income Tax -
    --------------------------------------------
    Gross Salary: 1558.85
    State:
    Tax Rate:     0.00%
    --------------------------------------------
    Tax Amount:   0.00
    Net Pay:      1558.85
    ============================================
    Press any key to close this window . . .
  10. Press any key to close the window and return to your programming environment

Matching an Expression

If you have an expression that can produce many outcomes but only one of them must be considered, you can validate it using the match keyword. The formula to follow is:

match expression with
    | condition-1 -> result-1
    | condition-2 -> result-2
    | . . .
    | condition-x -> result-x

The match and the with keywords are required. The expression can be the name of a variable whose value will be tested. The results of testing the expression will each be validated in the next lines. Each line starts with | (a pipe) and a value from the expression. This produces a result from the condition X, which is followed by the -> operator. Each condition has a body. The body is introduced by the -> operator. On the right side of that -> operator, define what you want process for that particular condition.

Matching an Integer

One of the types of values you can try to match is an integer. In most cases, a match statement behaves like an if conditional statement. Consider the following code that uses an if conditional statement:

let number = 248
let mutable conclusion = "That number is not right."

if number = 248 then
    conclusion <- "That number is correct."
        
printfn "%s" conclusion
printfn "========================================"

This would produce:

That number is correct.
========================================
Press any key to close this window . . .

The same code can be written with a match statement as follows:

let number = 248;
let conclusion = "That number is not right."

match number with
    | 248 ->
        conclusion = "That number is correct."

printfn "%s" conclusion
printfn "========================================"

We know that an if conditional statement can use one or more else options. Here is an example:

let mutable incomeTax = 0.00

printfn "Payroll Evaluation"
printfn "============================================"
printfn "Enter the information to evaluate taxes"
printfn "Frequency by which the payroll is processed:"
printfn "1 - Weekly"
printfn "2 - Biweekly"
printfn "3 - Semimonthly"
printfn "4 - Monthly"
printf "Enter Payroll Frequency: "
let frequency = int(stdin.ReadLine())
printfn "--------------------------------------------"

printf "Gross Salary:            "
let grossSalary = float(stdin.ReadLine())

if frequency = 1 then
    incomeTax <- 271.08 + (grossSalary * 24.00 / 100.00)
elif frequency = 2 then
        incomeTax <- 541.82 + (grossSalary * 24.00 / 100.00)
elif frequency = 3 then
    incomeTax <- 587.12 + (grossSalary * 24.00 / 100.00)
elif frequency = 4 then
    incomeTax <- 1174.12 + (grossSalary * 24.00 / 100.00)

printfn "============================================"
printfn "Payroll Evaluation"
printfn "--------------------------------------------"
printfn "Gross Salary: %18.02f" grossSalary
printfn "Income Tax:   %18.02f" incomeTax
printfn "============================================"

Here is an example of running the program:

Payroll Evaluation
============================================
Enter the information to evaluate taxes
Frequency by which the payroll is processed:
1 - Weekly
2 - Biweekly
3 - Semimonthly
4 - Monthly
Enter Payroll Frequency: 3
--------------------------------------------
Gross Salary:            1552.86
============================================
Payroll Evaluation
--------------------------------------------
Gross Salary:            1552.86
Income Tax:               959.81
============================================

Press any key to close this window . . .

In the same way, each section of a matching pattern can use a natural number. Here is an example:

let mutable incomeTax = 0.00

printfn "Payroll Evaluation"
printfn "============================================"
printfn "Enter the information to evaluate taxes"
printfn "Frequency by which the payroll is processed:"
printfn "1 - Weekly"
printfn "2 - Biweekly"
printfn "3 - Semimonthly"
printfn "4 - Monthly"
printf "Enter Payroll Frequency: "
let frequency = int(stdin.ReadLine())
printfn "--------------------------------------------"

printf "Gross Salary:            "
let grossSalary = float(stdin.ReadLine())

match frequency with
| 1 ->
    incomeTax <- 271.08 + (grossSalary * 24.00 / 100.00)
| 2 ->
        incomeTax <- 541.82 + (grossSalary * 24.00 / 100.00)
| 3 ->
    incomeTax <- 587.12 + (grossSalary * 24.00 / 100.00)
| 4 ->
    incomeTax <- 1174.12 + (grossSalary * 24.00 / 100.00)

printfn "============================================"
printfn "Payroll Evaluation"
printfn "--------------------------------------------"
printfn "Gross Salary: %18.02f" grossSalary
printfn "Income Tax:   %18.02f" incomeTax
printfn "============================================"

Matching a String

Besides a natural number, a matching pattern can compare its expression to strings. In this case, each section uses a string or an expression that evaluates to a string.

Practical LearningPractical Learning: Matching by Some Strings

  1. Change the document as follows:
    printfn "=========================================="
    printfn " - Amazing DeltaX - State Income Tax -"
    printfn "=========================================="
    
    let mutable stateName : string = ""
    let mutable taxRate   = 0.00
    
    printfn "Enter the information for tax preparation"
    printfn "States"
    printfn " AK - Alaska"
    printfn " AR - Arkansas"
    printfn " CO - Colorado"
    printfn " FL - Florida"
    printfn " GA - Georgia"
    printfn " IL - Illinois"
    printfn " IN - Indiana"
    printfn " KY - Kentucky"
    printfn " MA - Massachusetts"
    printfn " MI - Michigan"
    printfn " MO - Missouri"
    printfn " MS - Mississippi"
    printfn " NV - Nevada"
    printfn " NH - New Hampshire"
    printfn " NC - North Carolina"
    printfn " PA - Pennsylvania"
    printfn " SD - South Dakota"
    printfn " TN - Tennessee"
    printfn " TX - Texas"
    printfn " UT - Utah"
    printfn " WA - Washington"
    printfn " WY - Wyoming"
    printf "Enter State Abbreviation: "
    let abbr : string = stdin.ReadLine()
    printfn "--------------------------------------------"
            
    printf "Gross Salary:             "
    let grossSalary = float(stdin.ReadLine())
    
    match abbr with
        | "CO" ->
            taxRate   <- 4.63
            stateName <- "Colorado"
            
        | "FL" ->
            taxRate   <- 0.00
            stateName <- "Florida"
            
        | "IL" ->
            taxRate   <- 4.95
            stateName <- "Illinois"
            
        | "IN" ->
            taxRate   <- 3.23
            stateName <- "Indiana"
            
        | "KY" ->
            taxRate   <- 5.00
            stateName <- "Kentucky"
            
        | "MA" ->
            taxRate   <- 5.00
            stateName <- "Massachusetts"
            
        | "MI" ->
            taxRate   <- 4.25
            stateName <- "Michigan"
            
        | "NC" ->
            taxRate   <- 5.25
            stateName <- "North Carolina"
            
        | "NH" ->
            taxRate   <- 5.00
            stateName <- "New Hampshire"
            
        | "NV" ->
            taxRate   <- 0.00
            stateName <- "Nevada"
            
        | "PA" ->
            taxRate   <- 3.07
            stateName <- "Pennsylvania"
            
        | "TN" ->
            taxRate   <- 1.00
            stateName <- "Tennessee"
            
        | "TX" ->
            taxRate   <- 0.00
            stateName <- "Texas"
            
        | "UT" ->
            taxRate   <- 4.95
            stateName <- "Utah"
            
        | "WA" ->
            taxRate   <- 0.00
            stateName <- "Washington"
            
        | "WY" ->
            taxRate   <- 0.00
            stateName <- "Wyoming"
    
    let taxAmount = grossSalary * taxRate / 100.00
    let netPay    = grossSalary - taxAmount;
    
    printfn "============================================"
    printfn " - Amazing DeltaX - State Income Tax -"
    printfn "--------------------------------------------"
    printfn $"Gross Salary: {grossSalary:f}"
    printfn $"State:        {stateName}"
    printfn "Tax Rate:     %0.2f%c" taxRate '%'
    printfn "--------------------------------------------"
    printfn $"Tax Amount:   {taxAmount:f}"
    printfn $"Net Pay:      {netPay:f}"
  2. To execute the project, on the main menu, click Debug -> Start Without Debugging
  3. For the State Abbreviation, type CO and press Enter
  4. For the Gross Salary, type 1558.85 and press Enter:
    ==========================================
     - Amazing DeltaX - State Income Tax -
    ==========================================
    Enter the information for tax preparation
    States
     AK - Alaska
     AR - Arkansas
     CO - Colorado
     FL - Florida
     GA - Georgia
     IL - Illinois
     IN - Indiana
     KY - Kentucky
     MA - Massachusetts
     MI - Michigan
     MO - Missouri
     MS - Mississippi
     NV - Nevada
     NH - New Hampshire
     NC - North Carolina
     PA - Pennsylvania
     SD - South Dakota
     TN - Tennessee
     TX - Texas
     UT - Utah
     WA - Washington
     WY - Wyoming
    Enter State Abbreviation: CO
    --------------------------------------------
    Gross Salary:             1558.85
    ============================================
     - Amazing DeltaX - State Income Tax -
    --------------------------------------------
    Gross Salary: 1558.85
    State:        Colorado
    Tax Rate:     4.63%
    --------------------------------------------
    Tax Amount:   72.17
    Net Pay:      1486.68
    ============================================
    Press any key to close this window . . .
  5. Press any key to close the window and return to your programming environment
  6. To execute the project again, on the main menu, click Debug -> Start Without Debugging
  7. For the State Abbreviation, type KY and press Enter
  8. For the Gross Salary, type 1558.85 and press Enter:
    ==========================================
     - Amazing DeltaX - State Income Tax -
    ==========================================
    Enter the information for tax preparation
    States
     AK - Alaska
     AR - Arkansas
     CO - Colorado
     FL - Florida
     GA - Georgia
     IL - Illinois
     IN - Indiana
     KY - Kentucky
     MA - Massachusetts
     MI - Michigan
     MO - Missouri
     MS - Mississippi
     NV - Nevada
     NH - New Hampshire
     NC - North Carolina
     PA - Pennsylvania
     SD - South Dakota
     TN - Tennessee
     TX - Texas
     UT - Utah
     WA - Washington
     WY - Wyoming
    Enter State Abbreviation: KY
    --------------------------------------------
    Gross Salary:             1558.85
    ============================================
     - Amazing DeltaX - State Income Tax -
    --------------------------------------------
    Gross Salary: 1558.85
    State:        Kentucky
    Tax Rate:     5.00%
    --------------------------------------------
    Tax Amount:   77.94
    Net Pay:      1480.91
    ============================================
    Press any key to close this window . . .
  9. Press any key to close the window and return to your programming environment
  10. Create a new Console App that supports the .NET 6.0 (Long-Term Support) and named SimpleInterest1

Matching an Enumeration

A matching pattern can use an enumeration. In this case, each section can use a member of an enumeration.

Practical LearningPractical Learning: Matching an Enumeration

  1. Change the Program.cs document as follows:
    type CompoundFrequency =
    | Daily
    | Weekly
    | Monthly
    | Quaterly
    | Semiannually
    | Annually
    
    let mutable frequency = 0.00
    let mutable interestRate = 0.00
    let periods = 42
    let mutable compounding = CompoundFrequency.Monthly
    
    printfn "Simple Interest"
    printfn "--------------------------------------------------------"
    printfn "Provide the values that will be used to evaluate a loan."
    printf "Principal: "
    let principal = float(stdin.ReadLine())
    printfn "--------------------------------------------------------"
    printfn "Compound Frequencies"
    printfn "1. Weekly"
    printfn "2. Monthly"
    printfn "3. Quaterly"
    printfn "4. Semiannually"
    printfn "5. Annually"
    printf "Type your choice: "
    let choice = stdin.ReadLine()
    
    if choice = "1" then
        compounding <- CompoundFrequency.Weekly
    elif choice = "2" then
        compounding <- CompoundFrequency.Monthly
    elif choice = "3" then
        compounding <- CompoundFrequency.Quaterly
    elif choice = "4" then
        compounding <- CompoundFrequency.Semiannually
    elif choice = "5" then
        compounding <- CompoundFrequency.Annually
    else
        compounding <- CompoundFrequency.Daily
    
    match compounding with
        | CompoundFrequency.Daily ->
            interestRate <- 8.95
            frequency    <- 365.00
            
        | CompoundFrequency.Weekly ->
            interestRate <- 52.00
            frequency    <- 11.95
            
        | CompoundFrequency.Monthly ->
            interestRate <- 12.00
            frequency    <- 14.75
            
        | CompoundFrequency.Quaterly ->
            interestRate <- 4.00
            frequency    <- 18.85
            
        | CompoundFrequency.Semiannually ->
            interestRate <- 2.00
            frequency    <- 22.55
            
        | CompoundFrequency.Annually ->
            interestRate <- 1.00
            frequency    <- 26.25
    
    let iRate = interestRate / float 100
    let per = float periods / frequency
    let interestAmount = principal * iRate * per
    
    printfn "========================================================="
    printfn "Simple Interest"
    printfn "--------------------------------------------------------"
    printfn "Principal:          %0.02f" principal
    printfn "Interest Rate:      %0.02f %c" interestRate '%'
    printfn "Periods:            %i months" periods
    printfn "-------------------------------"
    printfn "Interest Amount:    %0.02f" interestAmount
    printfn "Future Value:       %0.02f" (principal + interestAmount)
    printfn "========================================================="
  2. To execute, press Ctrl + F5
  3. When requested, type the Principal as 7248.95 and press Enter
  4. For the compound frequency, type 2 and press Enter:
    Simple Interest
    --------------------------------------------------------
    Provide the values that will be used to evaluate a loan.
    Principal: 7248.95
    --------------------------------------------------------
    Compound Frequencies
    1. Weekly
    2. Monthly
    3. Quaterly
    4. Semiannually
    5. Annually
    Type your choice: 2
    =========================================================
    Simple Interest
    --------------------------------------------------------
    Principal:          7248.95
    Interest Rate:      14.75%
    Periods:            42 months
    -------------------------------
    Interest Amount:    3742.27
    Future Value:       10991.22
    =========================================================
    Press any key to close this window . . .
  5. Press T to close the window and return to your programming environment
  6. To open a recent project, on the main menu, click File -> Recent Projects and Solutions, and click TaxPreparation05

Conditionally Matching

Matching a Boolean Variable

Besides natural numbers and strings, a matching pattern can process logical expressions. To make it happen, you can first declare a Boolean variable. You can also use an expression that holds a Boolean value. Pass the Boolean value or expression to match. For each section of the matching pattern, use one of the possible Boolean outcomes. At a minimum, if you are using a Boolean variable, one section can use true and the other can process the false outcome. Here is an example:

let mutable conclusion = ""
let emplTitle          = "Manager"
let emplIsManager      = emplTitle = "Manager"

match emplIsManager with
    | true ->
        conclusion <- "The employee is a manager."

    | false ->
        conclusion <- "The staff member is a regular employee."

printfn "%s" conclusion
printfn "========================================"

This would produce:

The employee is a manager.
========================================
Press any key to close this window . . .

Matching a Boolean Expression

In the previous example, we included a Boolean variable to match. Otherwise, if you have a Boolean expression, you can pass it directly to match. Here is an example:

let mutable conclusion = ""
let emplTitle          = "Manager"

match emplTitle = "Manager" with
    | true ->
        conclusion <- "The employee is a manager."

    | false ->
        conclusion <- "The staff member is a regular employee."

printfn "%s" conclusion
printfn "========================================"

In the same way, since every conditional operation produces a Boolean value, it can be used for a matching pattern. Here is an example:

let timeWorked = 30.00

// If TimeWorked >= 40
match timeWorked >= 40.00 with
    | true ->
        printfn "The employee works full-time."
        
    | false ->
        printfn "The staff member worked part-time."

printfn "========================================"

As a result, you will see that in many cases, an if conditional statement such such as this:

printfn "FUN DEPARTMENT STORE"
printfn "======================================================="
printfn "Payroll Preparation"
printfn "-------------------------------------------------------"
printfn "Enter the following pieces of information"
printfn "-------------------------------------------------------"
printfn "Employee Information"
printfn "-------------------------------------------------------"
printf "First Name:    "
let firstName : string = stdin.ReadLine()
printf "Last Name:     "
let lastName : string = stdin.ReadLine()
printf "Hourly Salary: "
let hSalary = float(stdin.ReadLine());
printfn "-------------------------------------------------------"
printfn "Time worked"
printfn "-------------------------------------------------------"

printf "Monday:        "
let mon = float(stdin.ReadLine())

printf "Tuesday:       "
let tue = float(stdin.ReadLine())

printf "Wednesday:     "
let wed = float(stdin.ReadLine())

printf "Thursday:      "
let thu = float(stdin.ReadLine())

printf "Friday:        "
let fri = float(stdin.ReadLine())

let timeWorked = mon + tue + wed + thu + fri

let mutable regTime  = 0.00
let mutable overtime = 0.00
let mutable regPay   = 0.00
let mutable overPay  = 0.00

if timeWorked <= 40.00 then
    regTime   <- timeWorked
    regPay    <- hSalary * timeWorked
    overtime  <- 0.00
    overPay   <- 0.00
else
    regTime   <- 40.00
    regPay    <- hSalary * 40.00
    overtime  <- timeWorked - 40.00
    overPay   <- hSalary * 1.50 * overtime

let netPay = regPay + overPay

printfn "+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+"
printfn "FUN DEPARTMENT STORE"
printfn "======================================================="
printfn "Payroll Evaluation"
printfn "======================================================="
printfn "Employee Information"
printfn "-------------------------------------------------------"
printfn $"Full Name:     {firstName} {lastName}"
printfn $"Hourly Salary: {hSalary:f}"
printfn "======================================================="
printfn "Time Worked Summary"
printfn "--------+---------+-----------+----------+-------------"
printfn " Monday | Tuesday | Wednesday | Thursday | Friday"
printfn "--------+---------+-----------+----------+-------------"
printfn $"  {mon:f}  |   {tue:f}  |    {wed:f}   |  {thu:f}   |  {fri:f}"
printfn "========+=========+===========+==========+============="
printfn "                                   Pay Summary"
printfn "-------------------------------------------------------"
printfn "                                   Time     Pay"
printfn "-------------------------------------------------------"
printfn "                     Regular: %10.2f%10.2f" regTime regPay
printfn "-------------------------------------------------------"
printfn "                     overtime:%10.2f%10.2f" overtime overPay
printfn "======================================================="
printfn "                     Net Pay: %20.2f" netPay
printfn "======================================================="

can also be implemented as a matching pattern like this:

printfn "FUN DEPARTMENT STORE"
printfn "======================================================="
printfn "Payroll Preparation"
printfn "-------------------------------------------------------"

. . .

match timeWorked <= 40.00 with
| true ->
    regTime   <- timeWorked
    regPay    <- hSalary * timeWorked
    overtime  <- 0.00
    overPay   <- 0.00
| false ->
    regTime   <- 40.00
    regPay    <- hSalary * 40.00
    overtime  <- timeWorked - 40.00
    overPay   <- hSalary * 1.50 * overtime

let netPay = regPay + overPay

. . .

Here is an example of running the program:

FUN DEPARTMENT STORE
=======================================================
Payroll Preparation
-------------------------------------------------------
Enter the following pieces of information
-------------------------------------------------------
Employee Information
-------------------------------------------------------
First Name:    Jacob
Last Name:     Melman
Hourly Salary: 22.84
-------------------------------------------------------
Time worked
-------------------------------------------------------
Monday:        8
Tuesday:       7.50
Wednesday:     9.50
Thursday:      10.50
Friday:        8
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
FUN DEPARTMENT STORE
=======================================================
Payroll Evaluation
=======================================================
Employee Information
-------------------------------------------------------
Full Name:     Jacob Melman
Hourly Salary: 22.84
=======================================================
Time Worked Summary
--------+---------+-----------+----------+-------------
 Monday | Tuesday | Wednesday | Thursday | Friday
--------+---------+-----------+----------+-------------
  8.00  |   7.50  |    9.50   |  10.50   |  8.00
========+=========+===========+==========+=============
                                   Pay Summary
-------------------------------------------------------
                                   Time     Pay
-------------------------------------------------------
                     Regular:      40.00    913.60
-------------------------------------------------------
                     overtime:      3.50    119.91
=======================================================
                     Net Pay:              1033.51
=======================================================

Press any key to close this window . . .

Practical LearningPractical Learning: Matching Unknown Outcomes

  1. To execute the project again, on the main menu, click Debug -> Start Without Debugging
  2. For the State Abbreviation, type USA and press Enter
  3. For the Gross Salary, type 1000 and press Enter:
    ==========================================
     - Amazing DeltaX - State Income Tax -
    ==========================================
    Enter the information for tax preparation
    States
     AK - Alaska
     AR - Arkansas
     CO - Colorado
     FL - Florida
     GA - Georgia
     IL - Illinois
     IN - Indiana
     KY - Kentucky
     MA - Massachusetts
     MI - Michigan
     MO - Missouri
     MS - Mississippi
     NV - Nevada
     NH - New Hampshire
     NC - North Carolina
     PA - Pennsylvania
     SD - South Dakota
     TN - Tennessee
     TX - Texas
     UT - Utah
     WA - Washington
     WY - Wyoming
    Enter State Abbreviation: USA
    --------------------------------------------
    Gross Salary:             1000
    Unhandled exception. Microsoft.FSharp.Core.MatchFailureException: The match cases were incomplete
       at . . .\Program.fs:line 102
    
    Press any key to close this window . . .
  4. Notice that the program produces an error.
    Press any key to close the window and return to your programming environment

Default Selection

Introduction

When you create a match statement, you provide the values that must each be processed. Unfortunately, in most cases, at least one value is left out. As a result, you must tell the compiler what to do for (the) value(s) that cannot be processed. To assist you with this, the F# language provides an operator as the underscore _. In some cases, you can have a situation where you want to deal with only the default case. In that case, the formula to follow is:

match expression with | _ ->statement(s)

In most cases, you will deaul with known values and at lease one unknown value. For that scenario, the formula to follow is:

match expression with
    | condition-1 -> result-1
    | condition-2 -> result-2
    | . . .
    | condition-x -> result-x
    | _ ->statement(s)

Processing a Default Value

Consider a program with the following code:

let number = 1
let mutable conclusion = "That number is not right."

if number = 1 then conclusion <- "That number is correct."
        
printfn "%s" conclusion
printfn "===================================="

This would produce:

That number is correct.
====================================

Press any key to close this window . . .

As mentioned earlier, you can create a matching pattern that has only a default section. That situation is the same as having a simple if condition. As a result, the above code can be converted into a matching pattern with a default section as follows:

let number = 1
let mutable conclusion = "That number is not right."

match number with | _ -> conclusion <- "That number is correct."
        
printfn "%s" conclusion
printfn "========================================"

Processing Unknown Values

Consider the following program:

let mutable startingSalary = 16_000
let mutable employmentStatus = ""

printfn "Employment Status:"
printfn "f - Full-Time"
printfn "p - Part-Time"
printfn "c - Contractor"
printfn "--------------------------"
printf "Enter Employment Status: ";
let status = char(stdin.ReadLine())

match status with
| 'f' ->
    startingSalary <- 40_000
    employmentStatus <- "Full-Time"
| 'p' ->
    employmentStatus <- "Part-Time"
    startingSalary <- 32_000
| 'c' ->
    startingSalary <- 24_000
    employmentStatus <- "Contractor"

printfn "Employee Record"
printfn "----------------------------------"
printfn "Employment Status:      %s" employmentStatus
printfn "Starting Yearly Salary: %i" startingSalary
printfn "=================================="

Notice that this code produces a warning. Here is an example of running the program:

Employment Status:
f - Full-Time
p - Part-Time
c - Contractor
--------------------------
Enter Employment Status: p
Employee Record
----------------------------------
Employment Status:      Part-Time
Starting Yearly Salary: 32000
==================================

Press any key to close this window . . .

Here is another example of running the program:

Employment Status:
f - Full-Time
p - Part-Time
c - Contractor
--------------------------
Enter Employment Status: c
Employee Record
----------------------------------
Employment Status:      Contractor
Starting Yearly Salary: 24000
==================================

Press any key to close this window . . .

Here is another example of running the program:

Employment Status:
f - Full-Time
p - Part-Time
c - Contractor
--------------------------
Enter Employment Status: f
Employee Record
----------------------------------
Employment Status:      Full-Time
Starting Yearly Salary: 40000
==================================

Press any key to close this window . . .

Here is one more example of running the program:

Employment Status:
f - Full-Time
p - Part-Time
c - Contractor
--------------------------
Enter Employment Status: u
Unhandled exception. Microsoft.FSharp.Core.MatchFailureException: The match cases were incomplete
   at . . .\Program.fs:line 21

Press any key to close this window . . .

Notice that, this time, when we provide a value that is not expected, the program produces an error. As seen so far, in most cases, your matching statements will deal with different values. When there is at least one value that you did not consider, you should include the underscore section. Here is an example:

let mutable startingSalary = 16_000
let mutable employmentStatus = ""

printfn "Employment Status:"
printfn "f - Full-Time"
printfn "p - Part-Time"
printfn "c - Contractor"
printfn "----------------------------------"
printf "Enter Employment Status: ";
let status = char(stdin.ReadLine())

match status with
| 'f' ->
    startingSalary <- 40_000
    employmentStatus <- "Full-Time"
| 'p' ->
    employmentStatus <- "Part-Time"
    startingSalary <- 32_000
| 'c' ->
    startingSalary <- 24_000
    employmentStatus <- "Contractor"
| _ ->
    startingSalary <- 22_000
    employmentStatus <- "Unknown"

printfn "Employee Record"
printfn "----------------------------------"
printfn "Employment Status:      %s" employmentStatus
printfn "Starting Yearly Salary: %i" startingSalary
printfn "=================================="

Here is an example of running the program:

Employment Status:
f - Full-Time
p - Part-Time
c - Contractor
----------------------------------
Enter Employment Status: a
Employee Record
----------------------------------
Employment Status:      Unknown
Starting Yearly Salary: 22000
==================================

Press any key to close this window . . .

If you create an underscore section in your matching pattern, that underscore section should always be the last (unlike C# where the default section can appear anywhere, such as in the beginning of the cases or between any two cases).

Ignoring the Default Outcome

Although you should always anticipate the default case, sometimes, you can ignore it or deal with it in other section of your program. Still, in mose cases, if you don't create an underscore case, you program will present a warning, which could result in an error if the user provides a value that doesn't one of those the program is prepared for. To let you create a default case that doesn't present functionality for it, the F# language provides the empty parentheses operator. To use it, for the body of the last case, simply type () and don't provide any other code for it. Here is an example:

let mutable startingSalary = 16_000
let mutable employmentStatus = "Unknown"

printfn "Employment Status:"
printfn "f - Full-Time"
printfn "p - Part-Time"
printfn "c - Contractor"
printfn "----------------------------------"
printf "Enter Employment Status: ";
let status = char(stdin.ReadLine())

match status with
| 'f' ->
    startingSalary <- 40_000
    employmentStatus <- "Full-Time"
| 'p' ->
    employmentStatus <- "Part-Time"
    startingSalary <- 32_000
| 'c' ->
    startingSalary <- 24_000
    employmentStatus <- "Contractor"
| _ -> ()

printfn "Employee Record"
printfn "----------------------------------"
printfn "Employment Status:      %s" employmentStatus
printfn "Starting Yearly Salary: %i" startingSalary
printfn "=================================="

Here is an example of running the program:

Employment Status:
f - Full-Time
p - Part-Time
c - Contractor
----------------------------------
Enter Employment Status: q
Employee Record
----------------------------------
Employment Status:      Unknown
Starting Yearly Salary: 16000
==================================

Press any key to close this window . . .

Practical LearningPractical Learning: Addressing the Default Outcome

  1. Change the document as follows:
    printfn "=========================================="
    printfn " - Amazing DeltaX - State Income Tax -"
    printfn "=========================================="
    
    let mutable stateName       = ""
    let mutable taxRate = 0.00
    
    printfn "Enter the information for tax preparation"
    printfn "States"
    printfn " AK - Alaska"
    printfn " AR - Arkansas"
    printfn " CO - Colorado"
    printfn " FL - Florida"
    printfn " GA - Georgia"
    printfn " IL - Illinois"
    printfn " IN - Indiana"
    printfn " KY - Kentucky"
    printfn " MA - Massachusetts"
    printfn " MI - Michigan"
    printfn " MO - Missouri"
    printfn " MS - Mississippi"
    printfn " NV - Nevada"
    printfn " NH - New Hampshire"
    printfn " NC - North Carolina"
    printfn " PA - Pennsylvania"
    printfn " SD - South Dakota"
    printfn " TN - Tennessee"
    printfn " TX - Texas"
    printfn " UT - Utah"
    printfn " WA - Washington"
    printfn " WY - Wyoming"
    printf "Enter State Abbreviation: "
    let abbr : string = stdin.ReadLine()
    printfn "--------------------------------------------"
            
    printf "Gross Salary:             "
    let grossSalary = float(stdin.ReadLine())
    
    match abbr with
        | "CO" ->
            taxRate   <- 4.63
            stateName <- "Colorado"
            
        | "FL" ->
            taxRate   <- 0.00
            stateName <- "Florida"
            
        | "IL" ->
            taxRate   <- 4.95
            stateName <- "Illinois"
            
        | "IN" ->
            taxRate   <- 3.23
            stateName <- "Indiana"
            
        | "KY" ->
            taxRate   <- 5.00
            stateName <- "Kentucky"
            
        | "MA" ->
            taxRate   <- 5.00
            stateName <- "Massachusetts"
            
        | "MI" ->
            taxRate   <- 4.25
            stateName <- "Michigan"
            
        | "NC" ->
            taxRate   <- 5.25
            stateName <- "North Carolina"
            
        | "NH" ->
            taxRate   <- 5.00
            stateName <- "New Hampshire"
            
        | "NV" ->
            taxRate   <- 0.00
            stateName <- "Nevada"
            
        | "PA" ->
            taxRate   <- 3.07
            stateName <- "Pennsylvania"
            
        | "TN" ->
            taxRate   <- 1.00
            stateName <- "Tennessee"
            
        | "TX" ->
            taxRate   <- 0.00
            stateName <- "Texas"
            
        | "UT" ->
            taxRate   <- 4.95
            stateName <- "Utah"
            
        | "WA" ->
            taxRate   <- 0.00
            stateName <- "Washington"
            
        | "WY" ->
            taxRate   <- 0.00
            stateName <- "Wyoming"
    
        | _ -> ()
    
    let taxAmount = grossSalary * taxRate / 100.00
    let netPay    = grossSalary - taxAmount
    
    printfn "============================================"
    printfn " - Amazing DeltaX - State Income Tax -"
    printfn "--------------------------------------------"
    printfn $"Gross Salary: {grossSalary:f}"
    printfn $"State:        {stateName}"
    printfn "Tax Rate:     %0.2f%c" taxRate '%'
    printfn "--------------------------------------------"
    printfn $"Tax Amount:   {taxAmount:f}"
    printfn $"Net Pay:      {netPay:f}"
    printfn "============================================"
  2. To execute the project again, on the main menu, click Debug -> Start Without Debugging
  3. For the State Abbreviation, type USA and press Enter
  4. For the Gross Salary, type 100.00 and press Enter:
    ==========================================
     - Amazing DeltaX - State Income Tax -
    ==========================================
    Enter the information for tax preparation
    States
     AK - Alaska
     AR - Arkansas
     CO - Colorado
     FL - Florida
     GA - Georgia
     IL - Illinois
     IN - Indiana
     KY - Kentucky
     MA - Massachusetts
     MI - Michigan
     MO - Missouri
     MS - Mississippi
     NV - Nevada
     NH - New Hampshire
     NC - North Carolina
     PA - Pennsylvania
     SD - South Dakota
     TN - Tennessee
     TX - Texas
     UT - Utah
     WA - Washington
     WY - Wyoming
    Enter State Abbreviation: USA
    --------------------------------------------
    Gross Salary:             100.00
    ============================================
     - Amazing DeltaX - State Income Tax -
    --------------------------------------------
    Gross Salary: 100.00
    State:
    Tax Rate:     0.00%
    --------------------------------------------
    Tax Amount:   0.00
    Net Pay:      100.00
    ============================================
    
    Press any key to close this window . . .
  5. Press any key to close the window and return to your programming environment

Nesting Pattern Matching

You can create a pattern matching operation inside another. This is referred to as nesting. Here is an example:

// -- Annual Compound Interest - Money Borrowed
let principal = 2500
let amountCategory =
    if principal < 1000 then 1
    elif principal < 2000 then 2
    elif principal < 5000 then 3
    else 4
    
let mutable periods = 0
let mutable typeOfPeriods = 3 //"Daily" // "Monthly" "Quarterly" "Annually"
let mutable compounded = "Quarterly"
let numberOfYears = 6
let mutable interestRate = 0.00

match typeOfPeriods with
| 1 ->
    compounded <- "Daily"
    periods <- 360
    match amountCategory with
    | 1 -> interestRate <- 2.25 / 100.00
    | 2 -> interestRate <- 3.45 / 100.00
    | 3 -> interestRate <- 4.35 / 100.00
    | _ -> interestRate <- 6.05 / 100.00
| 2 ->
    compounded <- "Monthly"
    periods <- 12
    match amountCategory with
    | 1 -> interestRate <- 2.35 / 100.00
    | 2 -> interestRate <- 2.95 / 100.00
    | 3 -> interestRate <- 4.05 / 100.00
    | _ -> interestRate <- 6.85 / 100.00
| 3 ->
    compounded <- "Quarterly"
    periods <- 4
    match amountCategory with
    | 1 -> interestRate <- 2.25 / 100.00
    | 2 -> interestRate <- 4.30 / 100.00
    | 3 -> interestRate <- 5.55 / 100.00
    | _ -> interestRate <- 7.35 / 100.00
| 4 ->
    compounded <- "Annually"
    periods <- 1
    match amountCategory with
    | 1 -> interestRate <- 3.15 / 100.00
    | 2 -> interestRate <- 4.35 / 100.00
    | 3 -> interestRate <- 6.25 / 100.00
    | _ -> interestRate <- 8.05 / 100.00
| _ -> periods <- 0
       interestRate <- 0.00

// Calculation
let compoundInYears = float periods * float numberOfYears
let n1 = float 1 + (interestRate / float periods)
let futureValue = float principal * (n1 ** compoundInYears)

printfn "Deposit Summary"
printfn "--------------------------"
printfn "Amount Deposited: %d" principal
printfn "Interest Rate:    %0.02f%c" (interestRate * 100.00) '%' 
printfn "Periods:          %i years" periods
printfn "Compounded:       %s" compounded
printfn "--------------------------"
printfn "Future Value:    %0.02f" futureValue
printfn "=========================="

This would produce:

Deposit Summary
--------------------------
Amount Deposited: 2500
Interest Rate:    5.55%
Periods:          4 years
Compounded:       Quarterly
--------------------------
Future Value:    3479.89
==========================
Press any key to close this window . . .

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2009-2024, FunctionX Friday 06 October 2023 Next