函数与控制流
你现在知道如何在变量中存储数据并用运算符转换它们了。但是,一个只有语句列表的程序就像一份没有分段的食谱——它能用,但难以复用和阅读。函数让你可以 命名 一段代码,这样你就能在任何地方调用它。控制流让你 选择 哪些代码块运行,以及何时 重复 它们。两者结合,就能把扁平的脚本变成结构化的程序。
在本章中,我们将讨论:
- 使用
fn定义和调用函数 - 从函数返回值(隐式和显式)
- 使用
if/else if/else进行条件分支 - 使用
for cond { ... }进行条件循环 - 使用
for i in 0..n { ... }进行计数循环 - 使用
for item in list { ... }遍历集合 - 使用
break和continue控制循环 - 从函数中提前返回
定义函数
函数 是一个命名的、可复用的代码块。你定义一次,可以调用任意多次。在 Auto 中,使用 fn 关键字定义函数:
fn greet(name String) {
print("Hello, " + name + "!")
}这创建了一个名为 greet 的函数,它接受一个参数——name——类型为 String。类型标注放在参数名之后,没有冒号。函数体用花括号包裹。
要 调用 函数,写出函数名并在括号中传入参数:
greet("Alice")这会运行 greet 内部的代码,name 被设为 "Alice"。
几点需要注意:
- 参数有类型提示。 调用者必须传入正确类型的值。
fn greet(name String)表示name必须是String类型。 - 返回类型由编译器推断。 你不需要写
-> void或-> String。编译器通过检查函数体来确定返回类型。 - 类型不加括号。 是
name String,而不是name: String或name: String。
让我们看看函数的实际使用。
看看每种目标语言如何处理函数内的字符串拼接:
- Rust 使用
println!("Hello, {}!", name)配合格式化字符串——比在String上使用+更高效。 - Python 保持简单:
"Hello, " + name + "!"。Python 的+直接用于字符串。 - C 使用
printf("Hello, %s!\n", name)配合格式说明符。 - TypeScript 使用与 Auto 相同的
+拼接,因为 JavaScript 原生支持。
每种语言都有自己的字符串组合方式,但函数的结构——名称、参数、函数体——是通用的。
返回值
函数不仅能 执行 操作,还能 产生 值。在 Auto 中,函数返回其最后一个表达式的值。不需要 return 关键字:
fn add(a int, b int) {
a + b
}这个函数计算 a + b 并返回结果。函数体中的最后一个表达式就是返回值。这是一种 基于表达式 的设计,灵感来源于 Rust 等语言。
你可以将结果存入变量,或直接传给另一个函数:
var result = add(3, 4) // result 是 7
print(add(10, 20)) // 打印 30各语言处理返回值的关键区别:
- Auto 和 Rust 都使用基于表达式的函数体——最后一个表达式就是返回值,不需要
return关键字。Rust 确实需要显式的返回类型标注(-> i64),转译器会自动添加。 - Python、C 和 TypeScript 都需要
return关键字。转译器会自动插入。
这是 Auto 的一个设计选择:基于表达式的函数减少了视觉噪音,同时保持语义清晰。编译器总是可以通过查看最后一个表达式来确定函数的返回值。
条件分支
程序需要做决策。if 关键字让你只在条件为 true 时运行一段代码:
if n > 0 {
print("positive")
}你可以用 else if 链接多个条件,用 else 提供默认分支:
if n > 0 {
print("positive")
} else if n < 0 {
print("negative")
} else {
print("zero")
}Auto 从上到下评估条件。第一个为 true 的条件运行其代码块,其余的跳过。如果都不匹配,运行 else 块。
条件不需要括号。花括号是必需的。
五种语言的结构几乎相同。主要区别在语法层面:
- Python 使用
elif而不是else if,使用冒号加缩进而不是花括号。 - C 要求每个条件都加括号,每个代码块都加花括号。
- Rust 和 TypeScript 看起来几乎与 Auto 一模一样——Rust 中括号是可选的,但 TypeScript 中是必需的。
条件循环
Auto 使用 for 表示所有循环。当你写 for 后跟一个条件时,它的行为类似于 while 循环——只要条件为 true 就重复执行:
for count < 5 {
print(count)
count = count + 1
}这读起来很自然:"只要 count 小于 5,就继续执行。" Auto 使用 for 而不是 while 是一个刻意的设计选择——一个关键字用于所有循环,保持语言精简。
转译时,目标语言使用各自的 while 关键字:
- Rust:
while count < 5 { ... } - Python:
while count < 5: ... - C:
while (count < 5) { ... } - TypeScript:
while (count < 5) { ... }
这个循环打印 0、1、2、3、4——每行一个数字。注意这个模式:
- 在循环前初始化变量:
var count = 0 - 每次迭代开始时检查条件:
count < 5 - 在循环体内更新变量:
count = count + 1
如果你忘了第 3 步,循环会永远运行下去。这是一个常见的 bug,所以当你提前知道迭代次数时,计数循环(下一节)是更好的选择。
计数循环
当你确切知道要重复多少次时,使用带 .. 范围运算符的计数循环:
for i in 0..5 {
print(i)
}这表示"对于从 0 到(但不包括)5 的每个值 i,执行循环体。"它打印 0、1、2、3、4。
范围 0..5 右侧是 不包含 的——它包括 0 但不包括 5。这与 Rust 的范围语法一致。如果你想从 1 到 10,写 1..11。
循环变量 i 在每次迭代中自动创建。你不需要用 var 声明它。
每种目标语言用不同方式表达相同的计数循环:
- Rust 使用与 Auto 相同的
0..5范围语法。转译器直接复制。 - Python 将
0..5转换为range(0, 5)——Python 内置的整数序列生成函数。 - C 将范围展开为经典的
for循环:for (long long i = 0; i < 5; i++)。转译器根据范围计算起始值、结束值和步长。 - TypeScript 生成类似的经典
for循环:for (let i = 0; i < 5; i++)。
遍历集合
你还可以用 for 遍历列表(或任何集合)的元素:
var fruits = ["apple", "banana", "cherry"]
for fruit in fruits {
print(fruit)
}每次迭代,变量 fruit 取列表中的下一个值。这是处理集合中每个元素最可读的方式。
每种语言的迭代方式不同:
- Rust 使用
for fruit in &fruits按引用迭代,借用每个元素而不获取所有权。 - Python 使用
for fruit in fruits——简洁直接,最接近 Auto 的语法。 - C 回退到基于索引的循环:
for (int i = 0; i < fruits_len; i++)。C 没有内置的集合迭代器,所以转译器自动生成索引变量和长度检查。 - TypeScript 使用
for...of,它遍历值(与for...in不同,后者遍历键)。
break 和 continue
两个关键字让你在循环内有更精细的控制:
break立即退出循环。不再执行后续迭代。continue跳过当前迭代的剩余部分,跳到下一次迭代。
for i < 10 {
if i == 3 {
continue // 跳过打印 3
}
if i == 7 {
break // 完全停止循环
}
print(i)
}这个程序打印:1、2、4、5、6。
逐步分析:
i从 0 开始,递增后变为 1。不是 3,不是 7。打印1。i变为 2。打印2。i变为 3。触发continue。跳过print。回到循环顶部。i变为 4。打印4。i变为 5。打印5。i变为 6。打印6。i变为 7。触发break。循环立即结束。
break 和 continue 在所有四种循环形式中都有效——for cond、for i in range 和 for item in collection。它们是通用的循环控制语句。
条件返回
函数可以根据条件返回不同的值。在 Auto 中,由于函数是基于表达式的,你可以写一个求值为结果的条件:
fn is_even(n int) {
if n % 2 == 0 {
true
} else {
false
}
}if 的每个分支都是一个表达式。函数返回被执行的那个分支的值——true 或 false。这之所以有效,是因为函数体中的最后一个表达式是返回值,而 if/else 本身也是一个表达式。
输出:
true
false转译器在各语言中以不同方式处理布尔返回值:
- Auto 和 Rust 都将
if/else视为表达式。两种语言都不需要return关键字(不过 Rust 输出会添加-> bool返回类型)。 - Python 需要
return True和return False——Python 的if是语句,不是表达式,所以return是必需的。 - C 需要
return true和return false,并通过三元运算符打印布尔值,因为printf没有%bool格式说明符。 - TypeScript 需要
return true和return false,并使用===进行严格相等检查。
这种模式——if/else 的每个分支都是一个值——在 Auto 中很常见。你会到处看到它,从简单的 is_even 检查到更复杂的逻辑选择。
总结
在本章中,你学习了如何用函数和控制流来构建程序:
- 函数 使用
fn定义,接受带类型的参数,返回最后一个表达式的值。Auto 不需要return关键字。 - 返回值 由编译器推断。基于表达式的函数体保持代码简洁。
- 条件分支 使用
if、else if和else。条件不需要括号。花括号是必需的。 - 条件循环 使用
for cond { ... }——Auto 中相当于while的写法。转译器在所有目标语言中输出while。 - 计数循环 使用
for i in 0..n { ... }配合不包含右端的范围。转译器转换为各语言的本地语法。 - 集合遍历 使用
for item in list { ... }处理每个元素。 break和continue提供精细的循环控制——提前退出或跳过一次迭代。- 条件返回 让
if/else分支直接产生函数的返回值。
有了函数和控制流,你现在可以编写做决策、重复工作并将代码组织成可复用块的程序了。在下一章中,我们将了解如何使用结构体将相关数据组合在一起。