跳转到内容

操作员

几乎所有合约都对数据进行操作:将某些值转换成另一个值。 范围可能各不相同,但运营商是此类修改的核心。

本页按照[优先级](#precedence)的递减顺序列出了 Tact 中的所有运算符,并附有使用示例。

操作员表

下表列出了按 优先级:从高到低递减的运算符。

简要说明操作员
括号()
一元后缀!!
一元前缀[+][正]   [-][负]   [!][负]   [~][b-not]。
乘法[*][mul]   [/][div]   [%][mod]。
添加剂[+][加]   [-][分]
轮班[>>][shr]   <<
关系>   >=   <   [<=][le]。
平等[==][eq]   [=][eq]。
位与[&][b-and]。
比特 XOR^
位操作 OR[|][b-or]。
逻辑与[&&][l-and]。
逻辑或[||][l-or]
三元?:
任务[=][赋值] 和 所有增强赋值运算符

优先级

本页所有运算符的优先级从高到低依次递减。 优先级用于选择在特定情况下考虑哪个运算符。 每当出现模棱两可的情况时,Tact 会优先选择优先级较高的运算符,而不是优先级较低的运算符。

例如,减号 (-) 可被视为减法运算符或否定运算符,它将表达式的正负符号颠倒过来,反之亦然。 由于在两者有歧义的情况下,后者的优先级高于前者,Tact 将首先把 - 视为否定操作符。 如果这对给定表达式没有意义,它才会将其视为减法运算符。

请看下面的代码

5 + -5; // 在这里,减号将被视为否定运算符
5 -5; // 而在这里,尽管有格式限制,它仍将被视为减法运算符

尽管这个例子可能很简单,但忽略优先级规则往往会导致运算符出现混乱的情况。 由于括号在所有表达式和运算符中具有最高优先级,因此用括号封装每个操作可以确保正确的操作顺序。

括号,()

括号(也可称为圆括号,())与其说是实际的运算符,不如说是一种标点符号,但其[优先级](#优先级)高于任何其他运算符的优先级。 使用括号可覆盖运算顺序:

5 * 5 - 2; // 23
5 * (5 - 2); // 15

一元

这里的”一元”是指只应用于给定表达式的一个操作数。除了非空断言,所有一元运算符都具有相同的优先级

一元运算符可以是两种类型之一:

  • 前缀 - 放在表达式之前。
  • 后缀(或后缀) - 放在表达式之后。

非空断言,!!

一元双叹号(非空断言)运算符 !是一个后缀运算符,它强制执行非null值,如果可选变量不是null,则允许直接访问可选变量的值。 否则,如果编译器可以跟踪,则引发编译错误;如果不能跟踪,则抛出退出代码 128异常:空引用异常”。 可应用于任何可选变量,无论其类型是非空

加号,+

虽然 Tact 编译器的语法中指定了一元加号运算符 +,但它只作为 [二元运算符](#binary-add)存在。

否定,-

一元减号(negation)运算符 - 是一个前缀运算符,用于反转表达式的符号。 只能应用于 Int类型的值:

let fiveInt = 5;
five + -five; // 在这里,减号是一个否定运算符,而不是减法运算符
-(-1); // 双倍应用返回原始值,即 1
--1; // 1

逆,!

一元感叹号(inversion)运算符 ! 是一个前缀运算符,用于反转表达式的布尔值——将 true 变为 false,反之亦然。只能应用于 Bool 类型的值:

let iLikeTactBool = true;
!iLikeTact; // false
!false; // true
!(!false); // false
!!false; // false

双向 NOT, ~

单引号 tilde(bitwise not)运算符 ~ 是一个前缀运算符,它将表达式二进制表示中的每一位反转或_flip_,即把 11 改为 00,反之亦然。 只能应用于 Int类型的值:

let answerInt = 42;
~answer; // -43
~(~answer); // 42
~(~0); // 0
~~0; // 0

二进制

二进制运算符按优先级递减的顺序分成几个小节。 每个小节中的操作符与小节本身具有相同的 优先级

乘法

乘、除或求余数。

乘法,*

二进制星号 (multiplication) 运算符 * 用于两个值的乘法运算。 可能导致 整数溢出

只能应用于 Int类型的值:

let two: Int = 2;
two * two; // 4
0 * 1_000_000_000; // 0
-1 * 5; // -5
pow(2, 255) * pow(2, 255); // build error: integer overflow!

除法,/

二进制斜线 (division) 运算符 / 用于两个值的整除,如果结果为正,则向零截断,如果结果为负,则从零截断。这也叫向下舍入(或向 -\infty 舍入)。

如果尝试除以零,则会出现退出代码 4错误:整数溢出。

只能应用于 Int 类型的值:

let two: Int = 2;
two / 2; // 1
two / 1; // 2
-1 / 5; // -1
-1 / -5; // 0
1 / -5; // -1
1 / 5; // 0
6 / 5; // 1,四舍五入
-6 / 5; // -2,四舍五入(向-∞方向)。

Modulo, %

二进制百分号 (modulo) 运算符 % 用于获取整数除法的模数,不能与获取余数混淆。 对于符号相同的两个值,模运算和余运算是等价的,但当操作数的符号不同时,模运算的结果总是与_除数_(右边的值)的符号相同,而余运算的结果与_除数_(左边的值)的符号相同,这可能使它们相差一个单位的_除数_。

只能应用于 Int类型的值:

let two: Int = 2;
two % 2; // 0
two % 1; // 1
1 % 5; // 1
-1 % 5; // 4
1 % -5; // -4
-1 % -5; // -1

避免两者混淆的最简单方法是通过 abs(x: Int)优先使用正值:

abs(-1) % abs(-5); // 1

加法

加法或减法。

添加,+

二进制加法运算符 + 用于将数字相加。 超出 Int的最大值将导致退出代码 4错误:整数溢出”。

只能应用于 Int类型的值:

let two: Int = 2;
two + 2; // 4
-1 + 1; // 0
pow(2, 254) + pow(2, 254); // 2 * 2^254
pow(2, 255) + pow(2, 255); // 编译错误:整数溢出!
pow(2, 255) - 1 + pow(2, 255); // 2^256 - 1,Tact 中任何整数的最大值!

减去,-

二进制减号(subtraction)运算符 - 用于将数字相减。 超出 Int的最小值将导致退出代码 4错误:整数溢出。

只能应用于 Int类型的值:

let two: Int = 2;
two - 2; // 0
-1 - 1; // -2
pow(2, 254) - pow(2, 254); // 0
pow(2, 255) - pow(2, 255); // 0
pow(2, 256) - pow(2, 256); // build error: integer overflow!

位移

向左或向右移动位。

右移,>>

二进制双大于号(位向右移动)运算符 >> 返回一个整数,其二进制表示为左操作数的值向右移动了右操作数的位数。向右移位的多余位被丢弃,最左边位的副本从左边移入。这种操作也称为”符号向右移动”或”算术向右移动”,因为结果数字的符号与左操作数的符号相同。这是一种更有效的方法,即用 2n2^n 除以左操作数,其中 nn 等于右操作数

只能应用于 Int 类型的值:

let two: Int = 2;
two >> 1; // 1
4 >> 1; // 2
5 >> 1; // 2,由于整数值的下限
pow(2, 254) >> 254; // 1

左移,<<

二进制双小于号(bitwise shift left)运算符 << 返回一个整数,其二进制表示为左操作数的值向左移动右操作数的位数。向左移位的多余比特被丢弃,零比特从右边移入。这是一种更有效的方法,可以将左操作数乘以 2n2^n,其中 nn 等于右操作数。超出 Int 的最大值将导致退出代码 4错误:整数溢出。

只能应用于 Int 类型的值:

let twoInt = 2;
two << 1; // 4
1 << 5; // 1 * 2^5, 即 32
2 << 5; // 2 * 2^5, 即 64
pow(2, 254) == (1 << 254); // true
pow(2, 254) == 1 << 254// true,由于 >> 优先于 ==
pow(2, 255) == 1 << 255; // true,但我们在这里非常接近溢出,所以不需要括号!

关系

查找更大、更小或相等的数值。

大于,>

二进制_大于_运算符 > 如果左操作数大于右操作数,则返回 true,否则返回 false。 只能应用于 Int类型的值:

let two: Int = 2;
two > 2; // false
-1 > -3; // true

大于或等于,>=

二进制_大于或等于_运算符 >= 如果左操作数大于或等于右操作数,则返回 true,否则返回 false。 只能应用于 Int类型的值:

let two: Int = 2;
two >= 2; // true
-1 >= -3; // true

小于,<

二进制 less than 运算符 < 如果左操作数小于右操作数,则返回 true,否则返回 false。 只能应用于 Int类型的值:

let two: Int = 2;
two < 2; // false
-1 < -3; // false

小于或等于,<=

二进制_小于或等于_运算符 <= 如果左操作数小于或等于右操作数,则返回 true,否则返回 false。 只能应用于 Int类型的值:

let two: Int = 2;
two <= 2; // true
-1 <= -3; // false

平等与不平等,===``!=

二进制相等(equal)运算符 == 检查其两个操作数是否_equal_,返回结果类型 Bool

二元不等式(not equal)运算符 != 检查其两个操作数是否_not equal_,返回一个 Bool 类型的结果。

除了 [Cell][cell]和 [Slice][slice]类型会通过哈希值进行隐式比较外,这两种操作符都要求操作数为相同类型,并且都不执行隐式类型转换。

这两种运算符都可以应用于下列类型和值:

// Int:
2 == 3; // false
2 != 3; // true
// Bool:
true == true; // true
false != true; // true
// Address:
myAddress() == myAddress(); // true
myAddress() != myAddress(); // false
// Cell:
emptyCell() == emptyCell(); // true
emptyCell() != emptyCell(); // false
// Slice:
"A".asSlice() == "A".asSlice(); // true
"A".asSlice() != "A".asSlice(); // false
// String:
"A" == "A"; // true
"A" != "A"; // false
// map<K, V>:
let map1: map<Int, Int> = emptyMap();
let map2: map<Int, Int> = emptyMap();
map1 == map2; // true
map1 != map2; // false
// 可选项和空值本身
let nullableInt? = null;
nullable == null; // true
null == null; // true
nullable != null; // false
null != null; // false
let anotherNullable= 5;
nullable == anotherNullable; // false
nullable != anotherNullable; // true

比特 AND, &

二进制安培(比特 AND)运算符 & 应用比特 AND,对操作数的每一对相应比特执行逻辑 AND运算。 当我们要清除一个数字的选定位时,这一点非常有用,因为每个位都代表一个单独的标志或布尔状态,这使得每个整数可以 “存储 “多达 257257 个布尔值,因为 Tact 中的所有整数都是 257257- 位有符号的。

只能应用于 Int类型的值:

let two: Int = 2;
two & 1; // 0
4 & 1; // 0
3 & 1; // 1
1 & 1; // 1
255 & 0b00001111; // 15
0b11111111 & 0b00001111; // 15

比特 XOR, ^

二进制符串(位向 XOR)运算符 ^ 应用 位向 XOR,对操作数的每一对相应位执行 逻辑排他 OR运算。 如果只有一个位是 11,则每个位置的结果都是 11 ,但如果两个位都是 00 或两个位都是 11,则结果都是 00。 在这种情况下,它会对两个比特进行比较,如果两个比特不同,则给出 11 ;如果两个比特相同,则给出 00

它适用于将操作数的选定位反转(也称为切换或翻转),因为任何位都可以通过与 11进行 “XOR “来切换。

只能应用于 Int类型的值:

let two: Int = 2;
two ^ 3; // 1
4 ^ 1; // 0
3 ^ 1; // 3
1 ^ 1; // 0
255 ^ 0b00001111; // 240
0b111111 11 ^ 0b00001111; // 240

位wise OR, |

二进制条形 (bitwise OR) 运算符 | 应用 bitwise OR,对操作数的每一对相应位执行 logical OR 运算。 当我们要应用特定的 bitmask 时,这很有用。

例如,bitwise OR 通常用于 Tact 中的将基本模式与可选标记结合,方法是将特定位屏蔽到 11 ,以构建目标信息模式

只能应用于 Int类型的值:

let two: Int = 2;
two | 1; // 3
4 | 1; // 5
3 | 1; // 3
1 | 1; // 1
255 | 0b00001111; // 255
0b111111 | 0b00001111; // 255

逻辑 AND, &&

二进制逻辑 AND(逻辑连接)运算符 && 如果两个操作数都是 true,则返回 true,否则返回 false。 它是短路的,也就是说,如果左操作数是 false,它会立即将整个表达式求值为 false,而不求值右操作数。

只能应用于 Bool类型的值:

let iLikeTactBool = true;
iLikeTact && true; // true, evaluated both operands
iLikeTact && false; // false, evaluated both operands
false && iLikeTact; // false, didn't evaluate iLikeTact

逻辑 OR, ||

二元逻辑 OR(逻辑析取)运算符 || 只有当两个操作数都是 false 时,才返回 false,否则返回 true。 它是短路的,也就是说,如果左操作数是 true,它会立即将整个表达式评估为 true,而不评估右操作数。

只能应用于 Bool类型的值:

let iLikeSnailsBool = false;
iLikeSnails || true; // true, evaluated both operands
iLikeSnails || false; // false, evaluated both operands
true || iLikeSnails; // true, didn't evaluate iLikeSnails

三元,?:

条件(ternary)运算符是唯一一个包含三个操作数的 Tact 运算符:一个条件,后面跟一个问号(?),然后是如果条件被评估为true时要执行的表达式,后面跟一个冒号(:),最后是如果条件被评估为false时要执行的表达式。 该运算符常用于替代 if...else 语句。

条件必须解析为 [布尔][布尔] 类型:

// condition
// ↓
true ?"incredibly so""absolutely not"; // "incredibly so"
// --------------- ----------------
// ↑ ↑
// | alternative, when condition is false
// |
// consequence, when condition is true
2 + 2 == 4 ? true : false; // true

三元运算符是除[赋值相关运算符](#赋值)外唯一具有右关联性的运算符。 这意味着,在模棱两可的情况下,Tact 会优先选择最长的匹配序列。 简而言之,这使得三元运算符的无括号嵌套成为可能,但仅限于替代情况(冒号 : 后面的部分):

// 在其他情况下不需要额外的括号
false ?1 : (false ? 2 : 3); // 3
false ?1 : false ?2 : 3; // 也是 3
false ?1 : true ?2 : 3; // 2
// 后果情况需要额外的括号(介于 ? 和 : 之间的部分)
false ?(false ? 1 : 2) : 3; // 3
false ?1 : 2 : 3; // SYNTAX ERROR!
true ?(false ? 1 : 2) : 3; // 2

赋值,=

赋值操作符 = 用于为变量或 MessageStruct 的属性赋值。 赋值是一个语句,不返回值。

let someVarInt = 5; // 这里使用了赋值运算符 =...
someVar = 4; // ... 这里
someVar = (someVar = 5); // SYNTAX ERROR!

增强赋值运算符

增强(或复合)赋值运算符,如 +=,将操作与 [赋值](#assignment)结合起来。 增强赋值是一个语句,不返回值。

扩充赋值在语义上等同于常规赋值,只是多了一个操作:

let valueInt = 5;
// this:
value += 5;
// 等同于 this:
value = value + 5

增强赋值运算符列表:

let valueInt = 5;
// +=
value + 5; // 加 5
value = value + 5; // 加 5 并返回结果
value += 5; // 也加 5 并返回结果
// -=
value - 5; // 减 5
value = value - 5; // 减 5 并返回结果
value -= 5; // 也减 5 并返回结果
// *=
value * 5// 乘以 5
value = value * 5; // 乘以 5 并返回结果
value *= 5; // 也乘以 5 并返回结果
// /=
value / 5; // 除以 5
value = value / 5; // 除以 5 并返回结果
value /= 5; // 也除以 5 并返回结果
// %=
value % 5// 得到 5 的模数
value = value % 5; // 得到 5 的模数并返回结果
value %= 5; // 也得到 5 的模数并返回结果
// &=
value & 5; // 位和 5
value = value & 5; // 位和 5 并返回结果
value &= 5; // 也位和 5 并返回结果
// ^=
value ^ 5// 位似 XOR 5
value = value ^ 5; // 位似 XOR 5 并返回结果
value ^= 5; // 也是位似 XOR 5 并返回结果
// |=
value | 5; // 位似 OR 5
value = value | 5; // 位似 OR 5 并返回结果
value |= 5; // 也是位似 OR 5 并返回结果