跳转到内容

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!
前一章:可变数据 索引 下一章:数组
华夏公益教科书