F# 表达式
最后修改日期:2025 年 5 月 17 日
在本文中,我们将探讨 F# 中的表达式。表达式是 F# 程序的基本构建块,代表计算并产生值。
在 F# 中,表达式是任何代码片段,执行时都会计算出一个特定的值。与命令式语言中的语句不同,F# 将所有代码结构都视为表达式,这意味着每个操作都会产生一个值。这包括简单的字面量、复杂的计算、函数调用,甚至包括条件语句等控制流结构。
表达式在 F# 中起着至关重要的作用,它允许开发人员以模块化和可预测的方式构建逻辑。由于表达式始终返回值,代码保持简洁,并避免了不必要的副作用。例如,像 if x > 0 then "Positive" else "Negative"
这样的条件表达式会产生一个字符串结果,无需显式的 return 语句。
F# 中的所有控制流结构都作为表达式工作,包括循环和模式匹配。match
表达式可以对一个值进行求值,并根据预定义的 case 生成输出。例如,match n with | 0 -> "Zero" | 1 -> "One" | _ -> "Other"
根据给定输入返回一个字符串。
表达式可以组合形成更复杂的表达式,支持函数式编程原则,如组合。函数在 F# 中是表达式,这意味着它们可以作为参数传递,作为结果返回,并以各种方式应用来构建动态逻辑。
计算表达式提供了附加功能,为异步编程、序列操作和查询处理等任务实现了结构化控制流。这些表达式在保持函数式纯粹性的同时,简化了副作用的管理。
F# 基本表达式
F# 中的简单表达式包括字面量和基本操作。
// Literal expressions let number = 42 let text = "Hello" let truth = true // Arithmetic expressions let sum = 5 + 3 * 2 let power = 2.0 ** 8.0 // String expressions let greeting = text + ", F#!" let interpolated = $"The answer is {number}" printfn "%d" sum printfn "%f" power printfn "%s" greeting printfn "%s" interpolated
我们演示了 F# 中的各种基本表达式。
let sum = 5 + 3 * 2
算术表达式遵循标准的运算符优先级规则。
λ dotnet fsi basic.fsx 11 256.000000 Hello, F#! The answer is 42
F# 条件表达式
If/then/else 结构是返回值的表达式。
let describeNumber n = if n % 2 = 0 then "even" else "odd" let result = describeNumber 7 printfn "7 is %s" result // Ternary-style expression let max a b = if a > b then a else b printfn "Max of 5 and 3 is %d" (max 5 3) // Nested conditionals let rating score = if score >= 90 then "A" elif score >= 80 then "B" elif score >= 70 then "C" else "F" printfn "Score 85 gets: %s" (rating 85)
该示例展示了 if/then/else 结构如何在 F# 中作为表达式。
if n % 2 = 0 then "even" else "odd"
整个 if 表达式求值为 "even" 或 "odd"。
λ dotnet fsi conditional.fsx 7 is odd Max of 5 and 3 is 5 Score 85 gets: B
F# 块表达式
表达式可以分组到包含多个语句的块中。
let calculateTotal price quantity = // This is a block expression let subtotal = price * quantity let tax = subtotal * 0.08m subtotal + tax // Last expression is the return value let total = calculateTotal 25.0m 3m printfn "Total: %M" total // Another block expression example let message = let name = "Alice" let age = 30 $"{name} is {age} years old" printfn "%s" message
该示例演示了具有多个 let 绑定的块表达式。
let subtotal = price * quantity let tax = subtotal * 0.08m subtotal + tax
该块求值为最后一个表达式 (subtotal + tax)。
λ dotnet fsi blocks.fsx Total: 81.000000 Alice is 30 years old
F# 函数表达式
函数是 F# 中的一流表达式。它们可以使用函数表达式或 lambda 表达式定义。函数可以作为参数传递给其他函数,从函数返回,并存储在数据结构中。
// Function as an expression let square x = x * x // Lambda expression let cube = fun x -> x * x * x // Higher-order function let applyTwice f x = f (f x) let result1 = applyTwice square 2 let result2 = applyTwice cube 2 printfn "Square twice: %d" result1 printfn "Cube twice: %d" result2 // Function composition let negate x = -x let squareThenNegate = negate >> square printfn "Composed: %d" (squareThenNegate 3)
在本例中,我们在 F# 中使用了各种函数表达式。applyTwice
函数接收一个函数和一个参数,将该函数应用两次到该参数上,并返回结果。negate
函数对其参数取负。squareThenNegate
函数使用函数组合运算符 (>>) 组合了 negate
和 square
函数。
let cube = fun x -> x * x * x
Lambda 表达式是匿名函数,可以分配给名称。
λ dotnet fsi functions.fsx Square twice: 16 Cube twice: 256 Composed: -9
F# match 表达式
F# 中的模式匹配与 match 表达式一起非常强大。它允许您解构数据类型并匹配特定模式。match 表达式类似于其他语言中的 switch 语句,但更具表现力。
let describeNumber n = match n with | 0 -> "Zero" | 1 -> "One" | x when x < 0 -> "Negative" | x when x % 2 = 0 -> "Even" | _ -> "Odd" printfn "0: %s" (describeNumber 0) printfn "42: %s" (describeNumber 42) printfn "-5: %s" (describeNumber -5) printfn "7: %s" (describeNumber 7) // Matching on tuples let pointCategory (x, y) = match x, y with | 0, 0 -> "Origin" | _, 0 -> "X-axis" | 0, _ -> "Y-axis" | _ -> "Other" printfn "(0,5): %s" (pointCategory (0, 5))
该示例演示了对整数和元组的匹配。match 表达式使用守卫 (when) 为模式添加额外的条件。下划线模式 (_) 是一个通配符,匹配未被先前模式匹配的任何内容。
match n with | 0 -> "Zero" | 1 -> "One"
Match 表达式求值为匹配分支中的表达式。
λ dotnet fsi match.fsx 0: Zero 42: Even -5: Negative 7: Odd (0,5): Y-axis
F# 序列表达式
序列表达式按需生成序列。它们使用 seq
关键字定义,并且可以包含循环和条件。它们对于生成无限序列或依赖于外部数据源的序列很有用。序列是按需计算的,这意味着值仅在需要时生成。这允许高效的内存使用,并在某些情况下可以提高性能。
// Basic sequence expression let numbers = seq { 1..10 } // Sequence with filtering let evens = seq { for n in 1..20 do if n % 2 = 0 then yield n } // Sequence with transformation let squares = seq { for n in 1..5 do yield n * n } printfn "Numbers: %A" (numbers |> Seq.take 3) printfn "Evens: %A" (evens |> Seq.take 3) printfn "Squares: %A" (squares |> Seq.toList) // More complex sequence let coordinates = seq { for x in 1..3 do for y in 1..3 do yield (x, y) } printfn "Coordinates: %A" (coordinates |> Seq.toList)
该示例展示了如何使用 seq 表达式创建序列。seq 表达式允许您使用 for 循环和 yield 关键字定义值序列。yield
关键字用于在序列中生成值。该示例演示了一个基本序列、一个过滤序列和一个转换序列。
seq { for n in 1..20 do if n % 2 = 0 then yield n }
序列表达式按需(惰性地)生成值。
λ dotnet fsi sequences.fsx Numbers: seq [1; 2; 3] Evens: seq [2; 4; 6] Squares: [1; 4; 9; 16; 25] Coordinates: [(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]
F# 计算表达式
计算表达式为单子操作提供语法糖。它们允许您定义可用于封装异步或有状态计算的自定义工作流。计算表达式使用 let!
和 do!
关键字定义,这些关键字允许您在表达式中绑定值并执行副作用。
open System.Net.Http // Using the task computation expression (F# 6+) let fetchData (url: string) = task { let client = new HttpClient() let! response = client.GetAsync(url) let! content = response.Content.ReadAsStringAsync() return content } let fetchWebcode = fetchData "https://webcode.me" printfn "fetchWebcode result: %s" fetchWebcode.Result
在本例中,我们使用任务计算表达式从 URL 获取数据。它使我们能够以更易读的方式编写异步代码。let! 关键字用于将异步操作的结果绑定到变量。计算表达式在调用函数时执行。结果是一个可以等待或同步执行的任务。
在本文中,我们探讨了表达式在 F# 中的基本作用。理解表达式是编写惯用 F# 代码的关键,因为 F# 中的所有内容都是求值为值的表达式。