跳轉到內容

F# 程式設計/控制流

來自華夏公益教科書
前一章:可變資料 索引 下一章:陣列
F# : 控制流

在所有程式語言中,控制流指的是程式碼中做出的決策,這些決策影響了應用程式中語句執行的順序。F# 的命令式控制流元素與其他語言中遇到的類似。

指令式程式設計概述

[編輯 | 編輯原始碼]

大多數來自 C#、Java 或 C++ 背景的程式設計師熟悉指令式程式設計風格,這種風格在應用程式中使用迴圈、可變資料和帶有副作用的函式。雖然 F# 主要鼓勵使用函數語言程式設計風格,但它也具有允許程式設計師以更命令式風格編寫程式碼的結構。在以下情況下,指令式程式設計很有用

  • 與 .NET Framework 中的大多數物件互動,這些物件本質上都是命令式的。
  • 與高度依賴副作用的元件互動,例如 GUI、I/O 和套接字。
  • 程式碼片段的指令碼編寫和原型設計。
  • 初始化複雜的資料結構。
  • 最佳化命令式版本的演算法比函式式版本更高效的程式碼塊。

if/then 決策

[編輯 | 編輯原始碼]

F# 的 if/then/elif/else 結構在本手冊前面已經介紹過,為了更正式地介紹它,if/then 結構有以下語法

(* 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

與所有 F# 程式碼塊一樣,if 語句的範圍擴充套件到在其下方縮排的任何程式碼。例如

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()

此程式輸出

condition = true: inside the 'if'
outside the 'if' block
--------
outside the 'if' block

使用條件

[編輯 | 編輯原始碼]

F# 有三個布林運算子

符號 描述 示例
&& 邏輯與(中綴,短路)
true && false (* returns false *)
|| 邏輯或(中綴,短路)
true || false (* returns true *)
not 邏輯非
not false (* returns true *)

&&|| 運算子是短路的,這意味著 CLR 將執行最少的評估以確定條件是否成功或失敗。例如,如果 && 左側評估為 false,則無需評估右側;類似地,如果 || 左側評估為 true,則無需評估表示式的右側。

下面是 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()

alwaysTruealwaysFalse 方法分別返回 truefalse,但它們還會在每次評估函式時向控制檯列印一條訊息。

此程式輸出以下內容

Always true
Always false
alwaysTrue && alwaysFalse: false
-------
Always false
alwaysFalse && alwaysTrue: false
-------
Always true
alwaysTrue || alwaysFalse: true
-------
Always false
Always true
alwaysFalse || alwaysTrue: true
-------

如上所示,表示式 alwaysTrue && alwaysFalse 評估表示式的兩側。alwaysFalse && alwaysTrue 只評估表示式的左側;由於左側返回 false,因此無需評估右側。

for 迴圈遍歷範圍

[編輯 | 編輯原始碼]

for 迴圈傳統上用於遍歷定義明確的整數範圍。for 迴圈的語法定義如下

for var = start-expr to end-expr do
    ... // loop body

以下是一個簡單的程式,它打印出數字 1 到 10

let main() =
    for i = 1 to 10 do
        printfn "i: %i" i
main()

此程式輸出

i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
i: 10

此程式碼從使用者那裡獲取輸入來計算平均值

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 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 迴圈遍歷集合和序列

[編輯 | 編輯原始碼]

使用以下語法遍歷專案集合通常很方便

for pattern in expr do
    ... // loop body

例如,我們可以使用 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 迴圈

[編輯 | 編輯原始碼]

顧名思義,while 迴圈會在特定條件為真時無限地重複執行程式碼塊。while 迴圈的語法定義如下

while expr do
    ... // loop body

當我們不知道要執行程式碼塊多少次時,我們會使用 while 迴圈。例如,假設我們希望使用者猜測秘密區域的密碼;使用者可能在第一次嘗試時就猜對了密碼,也可能嘗試數百萬個密碼,我們只是不知道。以下是一個簡短的程式,它要求使用者在最多 3 次嘗試中猜對密碼

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()

此程式輸出以下內容

What's the password? kitty
What's the password? monkey
You got the password right!
前一章:可變資料 索引 下一章:陣列
華夏公益教科書