ZetCode

F# 字符串插值

最后修改日期:2025 年 5 月 17 日

在本文中,我们将探讨 F# 中的字符串插值。

F# 中的字符串插值提供了一种简洁的方式将表达式嵌入到字符串字面量中。插值字符串以 $ 为前缀,并包含用 {} 包裹的表达式。此功能允许动态构建字符串,从而更轻松地创建格式化字符串,而无需繁琐的字符串连接或格式化函数。

基本字符串插值

F# 字符串插值允许直接在字符串中嵌入变量和表达式

basic_interpolation.fsx
let name = "John Doe"
let age = 34

printfn $"{name} is {age} years old"

let now = System.DateTime.Now
printfn $"""Today is {now.DayOfWeek}, it's {now.ToString("HH:mm")}"""

此示例展示了简单的变量插值和日期时间格式化。$ 前缀标记了一个插值字符串,其中包含用 {} 包裹的表达式。

λ dotnet fsi basic_interpolation.fsx
John Doe is 34 years old
Today is Sunday, it's 11:58

类型化字符串插值

F# 支持类型化的插值字符串,带有格式说明符以确保类型安全

typed_interpolation.fsx
let name = "John Doe"
let age = 34

printfn $"Name: %s{name}, Age: %d{age}"

let price = 49.99
let quantity = 3
printfn $"Total: %f{price * float quantity}"

格式说明符(字符串为 %s,整数为 %d,浮点数为 %f)可确保类型安全和正确的格式。

λ dotnet fsi typed_interpolation.fsx
Name: John Doe, Age: 34
Total: 149.970000

格式字符串

插值字符串支持 .NET 的数字和日期格式字符串

format_strings.fsx
let now = System.DateTime.Now

printfn $"Short date: {now:d}"
printfn $"Long date: {now:D}"
printfn $"Short time: {now:t}"
printfn $"Long time: {now:T}"

let value = 1234.5678
printfn $"Currency: {value:C}"
printfn $"Scientific: {value:E2}"
printfn $"Fixed-point: {value:F2}"

标准的 .NET 格式说明符可在插值表达式中使用。

λ dotnet fsi format_strings.fsx
Short date: 19. 1. 2024
Long date: Friday, January 19, 2024
Short time: 11:19
Long time: 11:19:22
Currency: $1,234.57
Scientific: 1.23E+003
Fixed-point: 1234.57

对齐和填充

可以使用对齐说明符在插值字符串中对齐值

alignment.fsx
type User = { FirstName: string; LastName: string; Salary: int }

let users = [
    { FirstName = "John"; LastName = "Doe"; Salary = 1230 }
    { FirstName = "Lucy"; LastName = "Novak"; Salary = 670 }
    { FirstName = "Ben"; LastName = "Walter"; Salary = 2050 }
]

for user in users do
    let name = $"{user.FirstName} {user.LastName}"
    printfn $"|{name,-15}|{user.Salary,8}|"

负数对齐表示左对齐,正数表示右对齐。数字指定总宽度。

λ dotnet fsi alignment.fsx
|John Doe       |    1230|
|Lucy Novak     |     670|
|Ben Walter     |    2050|

插值中的表达式

复杂的表达式可以嵌入到插值字符串中

expressions.fsx
let x = 5
let y = 10

printfn $"Sum: {x + y}"
printfn $"Product: {x * y}"
printfn $"""Comparison: {if x < y then "x < y" else "x >= y"}"""

let numbers = [1..5]
printfn $"Count: {numbers.Length}, Sum: {List.sum numbers}"

任何有效的 F# 表达式都可以在插值花括号内使用。

λ dotnet fsi expressions.fsx
Sum: 15
Product: 50
Comparison: x < y
Count: 5, Sum: 15

多行插值字符串

F# 支持使用三引号语法进行多行插值字符串

multiline.fsx
let name = "Alice"
let score = 95

let grade = if score >= 90 then "A" else "B"
let status = if score >= 70 then "PASS" else "FAIL"

let message = $"""
    Report for {name}:
    - Score: {score}
    - Grade: {grade}
    - Status: {status}
    """

printfn "%s" message

三引号会保留字符串字面量中的缩进和换行符。

λ dotnet fsi multiline.fsx
Report for Alice:
- Score: 95
- Grade: A
- Status: PASS

逐字插值字符串

在 F# 中,插值字符串 $"..." 允许变量替换,而逐字字符串 @"..." 则会保留反斜杠等转义序列。

要正确格式化路径,请单独使用用于反斜杠的逐字字符串和用于替换的插值字符串

verbatim.fsx
let path = @"C:\Users\John"  // Verbatim string (preserves backslashes)
let file = "document.txt"

let fullPath = $"Full path: {path}\\{file}"  // Double backslash to escape in interpolated string

printfn "%s" fullPath

逐字字符串对于处理文件路径、正则表达式模式和转义序列很有用,而插值字符串则简化了变量替换。

λ dotnet fsi verbatim.fsx
Full path: C:\Users\John\document.txt

带集合的插值字符串

集合可以在插值字符串中进行处理

collections.fsx
let fruits = ["apple"; "banana"; "orange"]

printfn $"""Fruits: {String.concat ", " fruits}"""
printfn $"Count: {List.length fruits}"
printfn $"First: {List.head fruits}"

let numbers = [1..5]
printfn $"Sum: {List.sum numbers}, Avg: {List.averageBy float numbers}"

集合操作可以在插值表达式中自然地工作。

λ dotnet fsi collections.fsx
Fruits: apple, banana, orange
Count: 3
First: apple
Sum: 15, Avg: 3.0

条件格式化

条件逻辑可以在插值字符串中使用

conditional.fsx
let items = [
    "ring", 2
    "lamp", 1
    "chair", 3
]

for (name, count) in items do
    printfn $"""{count} {name}{if count = 1 then "" else "s"}"""

let temp = 22.5
printfn $"""Temperature: {temp}°C ({if temp > 20.0 then "Warm" else "Cool"})"""

括号有助于将条件运算符与格式字符串分开。

λ dotnet fsi conditional.fsx
2 rings
1 lamp
3 chairs
Temperature: 22.5°C (Warm)

最佳实践

遵循这些指南以实现有效的字符串插值

best_practices.fsx
// 1. Use typed interpolation for safety
let userId = "A123"
let attempts = 3
printfn $"User %s{userId} has %d{attempts} attempts left"

// 2. Keep complex logic outside strings
let calculateTotal price qty = price * float qty
let itemPrice = 12.99
let quantity = 5
printfn $"Total: {calculateTotal itemPrice quantity:C}"

// 3. Format for readability
let data = [(1, "A"); (2, "B"); (3, "C")]
for (id, value) in data do
    printfn $"|{id,3}|{value,-5}|"

// 4. Prefer interpolation over concatenation
let name = "Alice"
let age = 30
// Good:
printfn $"Name: {name}, Age: {age}"
// Avoid:
printfn "%s" ("Name: " + name + ", Age: " + string age)

F# 字符串插值提供了一种强大的方法来创建包含嵌入式表达式的字符串。$ 前缀可轻松插入变量,而类型化插值(%s%d)可确保类型安全。多行和逐字字符串可处理复杂的格式需求,使字符串构建更清晰、更易于维护。

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。