Skip to content
📖 Auto 编程语言(主书)本书目录

函数与控制流 ​

你现在知道如何在变量中存储数据并用运算符转换它们了。但是,一个只有语句列表的程序就像一份没有分段的食谱——它能用,但难以复用和阅读。函数让你可以 命名 一段代码,这样你就能在任何地方调用它。控制流让你 选择 哪些代码块运行,以及何时 重复 它们。两者结合,就能把扁平的脚本变成结构化的程序。

在本章中,我们将讨论:

  • 使用 fn 定义和调用函数
  • 从函数返回值(隐式和显式)
  • 使用 if / else if / else 进行条件分支
  • 使用 for cond { ... } 进行条件循环
  • 使用 for i in 0..n { ... } 进行计数循环
  • 使用 for item in list { ... } 遍历集合
  • 使用 break 和 continue 控制循环
  • 从函数中提前返回

定义函数 ​

函数 是一个命名的、可复用的代码块。你定义一次,可以调用任意多次。在 Auto 中,使用 fn 关键字定义函数:

auto
fn greet(name String) {
    print("Hello, " + name + "!")
}

这创建了一个名为 greet 的函数,它接受一个参数——name——类型为 String。类型标注放在参数名之后,没有冒号。函数体用花括号包裹。

要 调用 函数,写出函数名并在括号中传入参数:

auto
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 关键字:

auto
fn add(a int, b int) {
    a + b
}

这个函数计算 a + b 并返回结果。函数体中的最后一个表达式就是返回值。这是一种 基于表达式 的设计,灵感来源于 Rust 等语言。

你可以将结果存入变量,或直接传给另一个函数:

auto
var result = add(3, 4)    // result 是 7
print(add(10, 20))        // 打印 30
无需 `return` 关键字的返回值函数

各语言处理返回值的关键区别:

  • Auto 和 Rust 都使用基于表达式的函数体——最后一个表达式就是返回值,不需要 return 关键字。Rust 确实需要显式的返回类型标注(-> i64),转译器会自动添加。
  • Python、C 和 TypeScript 都需要 return 关键字。转译器会自动插入。

这是 Auto 的一个设计选择:基于表达式的函数减少了视觉噪音,同时保持语义清晰。编译器总是可以通过查看最后一个表达式来确定函数的返回值。

条件分支 ​

程序需要做决策。if 关键字让你只在条件为 true 时运行一段代码:

auto
if n > 0 {
    print("positive")
}

你可以用 else if 链接多个条件,用 else 提供默认分支:

auto
if n > 0 {
    print("positive")
} else if n < 0 {
    print("negative")
} else {
    print("zero")
}

Auto 从上到下评估条件。第一个为 true 的条件运行其代码块,其余的跳过。如果都不匹配,运行 else 块。

条件不需要括号。花括号是必需的。

使用 `if` 进行条件分支

五种语言的结构几乎相同。主要区别在语法层面:

  • Python 使用 elif 而不是 else if,使用冒号加缩进而不是花括号。
  • C 要求每个条件都加括号,每个代码块都加花括号。
  • Rust 和 TypeScript 看起来几乎与 Auto 一模一样——Rust 中括号是可选的,但 TypeScript 中是必需的。

条件循环 ​

Auto 使用 for 表示所有循环。当你写 for 后跟一个条件时,它的行为类似于 while 循环——只要条件为 true 就重复执行:

auto
for count < 5 {
    print(count)
    count = count + 1
}

这读起来很自然:"只要 count 小于 5,就继续执行。" Auto 使用 for 而不是 while 是一个刻意的设计选择——一个关键字用于所有循环,保持语言精简。

转译时,目标语言使用各自的 while 关键字:

  • Rust: while count &lt; 5 { ... }
  • Python: while count &lt; 5: ...
  • C: while (count &lt; 5) { ... }
  • TypeScript: while (count &lt; 5) { ... }
使用 `for` 进行条件循环

这个循环打印 0、1、2、3、4——每行一个数字。注意这个模式:

  1. 在循环前初始化变量:var count = 0
  2. 每次迭代开始时检查条件:count &lt; 5
  3. 在循环体内更新变量:count = count + 1

如果你忘了第 3 步,循环会永远运行下去。这是一个常见的 bug,所以当你提前知道迭代次数时,计数循环(下一节)是更好的选择。

计数循环 ​

当你确切知道要重复多少次时,使用带 .. 范围运算符的计数循环:

auto
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 声明它。

使用 `for i in range` 计数

每种目标语言用不同方式表达相同的计数循环:

  • Rust 使用与 Auto 相同的 0..5 范围语法。转译器直接复制。
  • Python 将 0..5 转换为 range(0, 5)——Python 内置的整数序列生成函数。
  • C 将范围展开为经典的 for 循环:for (long long i = 0; i &lt; 5; i++)。转译器根据范围计算起始值、结束值和步长。
  • TypeScript 生成类似的经典 for 循环:for (let i = 0; i &lt; 5; i++)。

遍历集合 ​

你还可以用 for 遍历列表(或任何集合)的元素:

auto
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 &lt; fruits_len; i++)。C 没有内置的集合迭代器,所以转译器自动生成索引变量和长度检查。
  • TypeScript 使用 for...of,它遍历值(与 for...in 不同,后者遍历键)。

break 和 continue ​

两个关键字让你在循环内有更精细的控制:

  • break 立即退出循环。不再执行后续迭代。
  • continue 跳过当前迭代的剩余部分,跳到下一次迭代。
auto
for i < 10 {
    if i == 3 {
        continue    // 跳过打印 3
    }
    if i == 7 {
        break       // 完全停止循环
    }
    print(i)
}
使用 `break` 和 `continue` 控制循环流程

这个程序打印:1、2、4、5、6。

逐步分析:

  1. i 从 0 开始,递增后变为 1。不是 3,不是 7。打印 1。
  2. i 变为 2。打印 2。
  3. i 变为 3。触发 continue。跳过 print。回到循环顶部。
  4. i 变为 4。打印 4。
  5. i 变为 5。打印 5。
  6. i 变为 6。打印 6。
  7. i 变为 7。触发 break。循环立即结束。

break 和 continue 在所有四种循环形式中都有效——for cond、for i in range 和 for item in collection。它们是通用的循环控制语句。

条件返回 ​

函数可以根据条件返回不同的值。在 Auto 中,由于函数是基于表达式的,你可以写一个求值为结果的条件:

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 输出会添加 -&gt; 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 分支直接产生函数的返回值。

有了函数和控制流,你现在可以编写做决策、重复工作并将代码组织成可复用块的程序了。在下一章中,我们将了解如何使用结构体将相关数据组合在一起。

基于 MIT 许可发布。