变量与运算符
既然你已经用 "Hello, world!" 热了身,是时候学习每个程序都需要的基础构建块了:变量和运算符。在本章中,我们将讨论:
- 使用
var声明变量 - 处理数字——整数和浮点数
- 字符串字面量与拼接
- 布尔值与逻辑运算符
- 比较运算符
- 变量遮蔽——重用变量名
var — 可变变量
变量是给值起名字的方式,这样你就可以在后面使用它们。在 Auto 中,你使用 var 关键字声明变量:
var x = 5这一行同时做了三件事:
- 创建一个名为
x的变量。 - 推断类型——
5是整数,所以x的类型是int。不需要类型标注。 - 赋值——将值
5赋给x。
你不需要自己写类型。Auto 的编译器会帮你推断。这叫做 类型推断(type inference),它是让 Auto 既轻量又类型安全的特性之一。
用 var 声明的所有变量都是 可变的(mutable)——你可以在之后改变它们的值:
var count = 0
count = count + 1关于
let和var的说明:你可能注意到本章只使用了
var。这是有意的。Auto 有两个声明变量的关键字:var用于可变变量,let用于不可变变量。我们将在第 6 章讨论不可变性时介绍let。目前,所有变量都用var。
让我们看看不同类型的变量声明。
注意每种目标语言的不同处理方式:
- Rust 使用
let mut声明可变变量,并添加显式类型标注(i64、String、f64)。 - Python 简单地写
x = 5。没有关键字,没有类型。最简洁。 - C 既需要类型(
long long、const char*、double),也需要分号。 - TypeScript 走中间路线:
let x: number = 5。类型在 TypeScript 中是可选的,但转译器会包含它们。
在每种语言中,含义都一样:创建一个变量,给它一个名字,存入一个值。
数字——整数与浮点数
Auto 有两种数值类型:int 用于整数,float 用于浮点数。编译器根据字面量值决定使用哪种。
var age = 25 // int —— 没有小数点
var temperature = 36.6 // float —— 有小数点如果数字有小数点,就是 float。如果没有,就是 int。就这么简单。
算术运算符
Auto 支持你期望的标准算术运算符:
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 10 - 4 | 6 |
* | 乘法 | 6 * 7 | 42 |
/ | 除法 | 15 / 4 | 3 |
% | 取余 | 17 % 5 | 2 |
注意两个整数相除会得到整数结果——15 / 4 是 3,而不是 3.75。小数部分被截断。如果你需要浮点结果,至少有一个操作数必须是浮点数:
var precise = 15.0 / 4 // 3.75代码清单 2-2 展示了所有算术运算符的实际使用。
转译输出中有几点值得注意:
- Python 使用
//进行整数除法,而不是/。当两个操作数都是整数时,a2p转译器会将 Auto 的/转换为 Python 的//。 - TypeScript 将除法包装在
Math.trunc()中以确保整数截断,因为 JavaScript 的/总是产生浮点数。 - C 和 Rust 在两个操作数都是整数时自然执行整数除法,所以不需要特殊处理。
字符串——字面量与拼接
字符串 是字符的序列。在 Auto 中,字符串字面量写在双引号内:
var greeting = "Hello, world!"Auto 中的字符串一旦创建就不可变。你可以用 + 运算符将现有字符串组合成新字符串:
var first = "Hello"
var second = "Auto"
var combined = first + ", " + second + "!"Auto 还支持多行字符串——只需在引号内包含实际的换行:
var multi = "This is a
multiline string"换行符会保留在字符串的值中。
看看每种目标语言如何处理字符串拼接:
- Rust 使用
format!()进行高效的字符串构建——转译器避免了在String上重复使用+操作。 - Python 直接支持字符串的
+操作,并使用三重引号表示多行字符串。 - C 使用
snprintf()写入缓冲区——转译器在编译时计算所需大小。 - TypeScript 使用模板字面量(反引号)表示多行字符串,用
+进行拼接。
布尔值
布尔值只有两个可能的值:true 和 false。布尔值是程序中所有决策的基础。
var is_active = true
var is_finished = falseAuto 提供三个逻辑运算符来操作布尔值:
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
&& | 与 | true && false | false |
| ` | ` | 或 | |
! | 非 | !true | false |
这些运算符的行为完全符合逻辑预期:
&&仅当两边都为true时才为true。||当至少一边为true时就为true。!翻转值:!true变成false,!false变成true。
Python 转译器将 Auto 的符号运算符转换为 Python 的关键字等效形式:&& 变成 and,|| 变成 or,! 变成 not。Python 还使用 True 和 False(首字母大写)而不是 true 和 false。
C 通过 <stdbool.h> 支持布尔类型,但 printf 没有布尔值的格式说明符。转译器使用三元运算符将布尔值打印为字符串 "true" 或 "false"。
比较运算符
比较运算符比较两个值并返回布尔值。Auto 支持六种比较运算符:
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
== | 等于 | 10 == 20 | false |
!= | 不等于 | 10 != 20 | true |
< | 小于 | 10 < 20 | true |
> | 大于 | 10 > 20 | false |
<= | 小于或等于 | 10 <= 10 | true |
>= | 大于或等于 | 10 >= 20 | false |
这些运算符可以用于数字、字符串和布尔值。字符串比较按字典序进行。
注意 TypeScript 转译器将 == 转换为 ===,将 != 转换为 !==。TypeScript 的严格相等运算符避免了 JavaScript == 和 != 的类型强制转换陷阱,因此转译器始终使用严格版本。
变量遮蔽
Auto 允许你在同一作用域内声明一个与之前变量同名的新变量。这叫做 变量遮蔽(shadowing)。新变量 遮蔽 了旧变量——从那时起,这个名字指向新变量。
var x = 5
var x = x + 1 // x 现在是 6这与赋值不同。你不是在改变旧变量——而是在创建一个碰巧同名的新变量。因为它是新变量,你甚至可以改变类型:
var x = 5 // x 是 int
var x = "now a string" // x 现在是 string这种改变类型的遮蔽在 Auto 中完全合法。在 Python 和 Rust(使用 let)中也同样合法。C 和 TypeScript 需要变通方案,转译器会自动处理。
C 的输出最为引人注目。因为 C 是静态类型的,不支持用不同类型遮蔽变量,a2c 转译器生成了一个 标签联合体(tagged union)——一个同时携带类型标签和值的结构体。这是真实 C 程序中使用的真实技术,转译器会自动应用它,让你不必操心。
TypeScript 的输出使用联合类型(number | string)来表示可以持有任一类型的变量。
总结
在本章中,你学习了在 Auto 中处理数据的基础知识:
- 变量 使用
var声明,是可变的。编译器会推断类型——你不需要类型标注。 - 数字 有两种:
int用于整数,float用于小数。算术运算符(+、-、*、/、%)的行为符合预期。 - 字符串 使用双引号和
+运算符进行拼接。多行字符串通过实际的换行来编写。 - 布尔值 为
true或false。逻辑运算符&&、||和!用于组合布尔值。 - 比较运算符(
==、!=、<、>、<=、>=)比较值并返回布尔值。 - 变量遮蔽 允许你重用变量名,甚至可以使用不同的类型。每个
var都创建一个新变量。
现在你已经掌握了命名和转换数据的词汇。在下一章中,我们将通过编写函数——每个程序的可复用构建块——来运用这些词汇。