# 可变性 [](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/18_mutability.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) 正如我们已经看到的,所有 Erg 变量都是不可变的。但是,Erg 对象具有可变性的概念 以下面的代码为例 ```python a = [1, 2, 3] a = a + [4, 5, 6] print! a # [1, 2, 3, 4, 5, 6] ``` 上面的代码实际上不能被 Erg 执行。这是因为它不可重新分配 可以执行此代码 ```python b = ![1, 2, 3] b.concat! [4, 5, 6] print! b # [1, 2, 3, 4, 5, 6] ``` `a, b` 的最终结果看起来一样,但它们的含义却大不相同 虽然 `a` 是表示 `Nat` 数组的变量,但第一行和第二行指向的对象是不同的。名称`a`相同,但内容不同 ```python a = [1, 2, 3] print! id! a # 0x000002A798DFE940 _a = a + [4, 5, 6] print! id! _a # 0x000002A798DFE980 ``` `id!` 过程返回对象驻留的内存地址 `b` 是一个 `Nat` "动态" 数组。对象的内容发生了变化,但变量指向的是同一个东西 ```python b = ![1, 2, 3] print! id! b # 0x000002A798DFE220 b.concat! [4, 5, 6] print! id! b # 0x000002A798DFE220 ``` ```python i = !0 if! True. do! do! i.inc!() # or i.add!(1) do pass print! i # 1 ``` `!` 是一个特殊的运算符,称为 __mutation 运算符__。它使不可变对象可变 标有"!"的对象的行为可以自定义 ```python Point = Class {.x = Int; .y = Int} # 在这种情况下 .x 是可变的,而 .y 保持不变 Point! = Class {.x = Int!; .y = Int} Point!. inc_x! ref!(self) = self.x.update! x -> x + 1 p = Point!.new {.x = !0; .y = 0} p.inc_x!() print! p.x # 1 ``` ## 常量 与变量不同,常量在所有范围内都指向同一事物 常量使用 `=` 运算符声明 ```python PI = 3.141592653589 match! x: PI => print! "this is pi" ``` 常量在全局以下的所有范围内都是相同的,并且不能被覆盖。因此,它们不能被 ``=`` 重新定义。此限制允许它用于模式匹配 `True` 和 `False` 可以用于模式匹配的原因是因为它们是常量 此外,常量总是指向不可变对象。诸如 `Str!` 之类的类型不能是常量 所有内置类型都是常量,因为它们应该在编译时确定。可以生成非常量的类型,但不能用于指定类型,只能像简单记录一样使用。相反,类型是其内容在编译时确定的记录 ## 变量、名称、标识符、符号 让我们理清一些与 Erg 中的变量相关的术语 变量是一种为对象赋予名称以便可以重用(或指向该名称)的机制 标识符是指定变量的语法元素 符号是表示名称的语法元素、记号 只有非符号字符是符号,符号不称为符号,尽管它们可以作为运算符的标识符 例如,`x` 是一个标识符和一个符号。`x.y` 也是一个标识符,但它不是一个符号。`x` 和 `y` 是符号 即使 `x` 没有绑定到任何对象,`x` 仍然是一个符号和一个标识符,但它不会被称为变量 `x.y` 形式的标识符称为字段访问器 `x[y]` 形式的标识符称为下标访问器 变量和标识符之间的区别在于,如果我们在 Erg 的语法理论意义上谈论变量,则两者实际上是相同的 在 C 中,类型和函数不能分配给变量; int 和 main 是标识符,而不是变量(严格来说可以赋值,但有限制) 然而,在Erg语中,"一切都是对象"。不仅函数和类型,甚至运算符都可以分配给变量