运算符

运算符用于在程序运行时执行数学或逻辑运算

Z1h 语言内置的运算符有:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 其他运算符

接下来让我们来详细看看各个运算符的介绍

算术运算符

下表列出了Z1h语言的所有算术运算符. 假定 A 值为 10, B 值为 20.

运算符 描述 示例
+ 相加 A + B 输出结果 30
- 相减 A - B 输出结果 -10
* 相乘 A * B 输出结果 200
/ 相除 B / A 输出结果 2
% 求余 B % A 输出结果 0
++ 自增 A++ 输出结果 11
自减 A– 输出结果 9
** 幂运算 A ** B 输出10的20次方结果100000000000000000000

关系运算符

下表列出了Z1h语言的所有关系运算符. 假定 A 值为 10, B 值为 20.

运算符 描述 示例
== 检查两个值是否相等, 如果相等返回 True 否则返回 False (A == B) 为 False
!= 检查两个值是否不相等, 如果不相等返回 True 否则返回 False (A != B) 为 True
> 检查左边值是否大于右边值, 如果是返回 True 否则返回 False (A > B) 为 False
< 检查左边值是否小于右边值, 如果是返回 True 否则返回 False (A < B) 为 True
>= 检查左边值是否大于等于右边值, 如果是返回 True 否则返回 False (A >= B) 为 False
<= 检查左边值是否小于等于右边值, 如果是返回 True 否则返回 False (A <= B) 为 True

逻辑运算符

下表列出了Z1h语言的所有逻辑运算符. 假定 A 值为 True, B 值为 False.

运算符 描述 示例
&& 逻辑 AND 运算符. 如果两边的操作数都是 True, 则条件 True, 否则为 False (A && B) 为 False
|| 逻辑 OR 运算符. 如果左值为空值或零值(如null/0/false/"等), 则返回右值; 否则返回左值 (A || B) 为 True
?? 如果左值为空值或抛出异常或包含错误类型, 则返回右值; 否则返回左值 (A ?? B) 为 True
! 逻辑 NOT 运算符. 如果条件为 True, 则逻辑 NOT 条件 False, 否则为 True !(A && B) 为 True

?? 操作符

上述逻辑运算符中除了??符号之外, 大多数运算符和其它语言(尤其是js)表现基本一致, 所以这里着重讲一下??的用法

print(false ?? 123)
// false

print(0 ?? 123)
// 0

print(null ?? 456)
// 456

print(1 / 0 ?? 789)
// 左边抛出异常, 返回右值 789

recover

结合recover函数使用??, 可以很优雅地取代try...catch...语法

调用recover函数时, 传入一个函数对象, 该对象的第一个参数就是左值抛出的异常对象

print(1 / 0 ?? recover(e=>`抛出异常:${e.Error()}`))
// 抛出异常:Runtime exception at line 1, column 7: division by zero

panic(123) ?? recover(err=>print(`抛出的内容是: ${err.Origin()}`))
// 抛出的内容是: 123

位运算符

位运算符对整数在内存中的二进制位进行操作.

下表列出了位运算符 &, |, 和 ^ 的计算:

p q p & q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

假定 A = 60; B = 13; 其二进制数转换为:

A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001

支持的位运算符如下表所示. 假定 A 为60, B 为13:

运算符 描述 示例
& 按位与运算符"&"是双目运算符. 其功能是参与运算的两数各对应的二进位相与 (A & B) 结果为 12, 二进制为 0000 1100
| 按位或运算符"|"是双目运算符. 其功能是参与运算的两数各对应的二进位相或 (A | B) 结果为 61, 二进制为 0011 1101
^ 按位异或运算符"^"是双目运算符. 其功能是参与运算的两数各对应的二进位相异或, 当两对应的二进位相异时, 结果为1 (A ^ B) 结果为 49, 二进制为 0011 0001
<< 左移运算符"<<"是双目运算符. 左移n位就是乘以2的n次方. 其功能把"<<"左边的运算数的各二进位全部左移若干位, 由"<<"右边的数指定移动的位数, 高位丢弃, 低位补0 A << 2 结果为 240 , 二进制为 1111 0000
>> 右移运算符">>"是双目运算符. 右移n位就是除以2的n次方. 其功能是把">>"左边的运算数的各二进位全部右移若干位, ">>"右边的数指定移动的位数 A >> 2 结果为 15 , 二进制为 0000 1111

赋值运算符

下表列出了Z1h语言的所有赋值运算符

运算符 描述 示例
= 简单的赋值运算符, 将一个表达式的值赋给一个左值 C = A + B 将 A + B 表达式结果赋值给 C
+= 相加后再赋值 C += A 等于 C = C + A
-= 相减后再赋值 C -= A 等于 C = C - A
*= 相乘后再赋值 C *= A 等于 C = C * A
/= 相除后再赋值 C /= A 等于 C = C / A
%= 求余后再赋值 C %= A 等于 C = C % A
<<= 左移后赋值 C <<= 2 等于 C = C << 2
>>= 右移后赋值 C >>= 2 等于 C = C >> 2
&= 按位与后赋值 C &= 2 等于 C = C & 2
^= 按位异或后赋值 C ^= 2 等于 C = C ^ 2
|= 按位或后赋值 C |= 2 等于 C = C | 2

赋值语句

Z1h同时支持JavaPython的两种赋值语句风格, 详见变量

三元运算符

Z1h同时支持JavaPython的两种三元运算符风格

运算符 风格 示例
判断语句 ? 正确时的值: 错误时的值 Java/JavaScript print(4 % 2 == 0? "A": "B")
正确时的值 if 判断语句 else 错误时的值 Python print("AA" if 4 % 2 == 0 else "BB")

其他运算符

下表列出了Z1h语言的其他运算符

运算符 描述 示例
& 返回变量存储地址 &a将给出变量的实际地址
* 指针变量 *a是一个指针变量
展开(spread)操作 有展开、复制等功能
?.或?[] 可选链 a?.b, a?[c]

… 展开操作符

... 的用处有9种:

  1. 作为函数形参时, 右结合表示聚合一部分形参为数组
  2. 作为函数形参时, 左结合表示将该参数复制一份作为实参
  3. 作为函数形参时, 左+右结合表示完全展开该参数到本作用于本作用域
  4. 调用函数时, 使用左结合表示先复制再传入函数
  5. 调用函数时, 使用右结合表示将该实参展开
  6. 从列表中提取变量时, 左结合表示从列表中取出之后复制再赋值
  7. 从列表中提取变量时, 右结合表示聚合列表的部分切片
  8. 从列表中提取变量时, 左+右结合表示将对应的值的键值对展开到当前变量环境
  9. 从对象中提取变量时, 左结合表示获取到对象字段后复制再赋值

以下为示例代码

作为函数形参时, 右结合表示聚合一部分形参为数组

f = (name, friends...) => print(`My name is ${name}, I have ${friends.len} friends, they are ${friends.join(' and ')}!`)

f('zwr', 'aaa', 'bbb', 'ccc')

作为函数形参时, 左结合表示将该参数复制一份作为实参

// 你可以运行一遍后, 将...e的...去掉再试试
f = (...e) => {
	e.name = 'zwr'
	print('In function:', e)
}
people = {name: 'zq', age: 25}
f(people)
print('Out function:', people)

作为函数形参时, 左+右结合表示完全展开该参数到本作用于本作用域

f = (...e...) => print(`My name is ${name}, age is ${age}`)
f({
	name: 'zwr',
	age: 26,	
})

调用函数时, 使用左结合表示先复制再传入函数

f = e=>{e.Method='POST'}
req = new(net_http.Request)
req.Method = 'GET'
f(...req)
print(req.Method)

调用函数时, 使用右结合表示将该实参展开

path.Join('aaa', ['bbb', 'ccc']..., 'ddd', ['eee', 'fff']...)

从列表中提取变量时, 左结合表示从列表中取出之后复制再赋值

a = [{name: 'zwr', age: 26}, {name: 'zq', age: 25}];
[zwr, ...zq] = a;
zq.age++
print('Out:', zq)
print('In:', a[1])

从列表中提取变量时, 右结合表示聚合列表的部分切片

a = [1, 2, 3, 4];
[top, other..., bottom] = a;
print('Top:', top)
print('Other:', other)
print('Bottom:', bottom)

从列表中提取变量时, 左+右结合表示将对应的值的键值对展开到当前变量环境

请注意, 如果你不能确认展开的结构, 请慎用该特性, 以免污染当前变量环境

n = {name: 'zwr', age: 26};
[...a..., b...] = [n, n, n];
print('a =', a, '; b =', b)
print(`name = ${name}, age = ${age}`)

从对象中提取变量时, 左结合表示获取到对象字段后复制再赋值

n = {name: 'zwr', wife: {name: 'zq', age: 25}};
{name, ...wife} = n
wife.age++
print('Out:', wife)
print('Origin:', n)

可选链

尝试对map、数组、任意对象进行取值时, 由于字段不存在或索引超出了边界, 会抛出各种异常, 此时可以用可选链来试探取值

可选链是短路的, 和||、&&、??类似, 当左值为nil时, 不会调用右值

me = {name: 'zwr'}
print(
	'School name: %v, alias :%v'.fmt(
		me?.school?['name'],
		me?.school?.alias?[1]
	)
)
me.school = {
	name: 'SCUT',
	alias: ['五山禅寺', '华南鲤工大学']
}
print(
	'School name: %v, alias :%v'.fmt(
		me?.school?['name'],
		me?.school?.alias?[1]
	)
)

运算符优先级

有些运算符拥有较高的优先级, 二元运算符的运算方向均是从左至右. 下表列出了所有运算符以及它们的优先级, 由上至下代表优先级由高到低

优先级 运算类型 关联性 运算符
20 圆括号 不相关 ( … )
19 成员访问 从左到右 … . …
需计算的成员访问 从左到右 … [ … ]
new (带参数列表) n/a new … ( … )
函数调用 从左到右 … ( … )
可选链(Optional chaining) 从左到右 ?.
18 new(无参数列表) 从右到左 new …
17 后置递增(运算符在后) … ++
后置递减(运算符在后) … - -
16 逻辑非 从右到左 ! …
按位非 ~ …
一元加法 + …
一元减法 - …
前置递增 ++ …
前置递减 – …
typeof typeof …
void void …
delete delete …
await await …
15 从右到左 … ** …
14 乘法 从左到右
… * …
除法 … / …
取模 … % …
13 加法 从左到右
… + …
减法 … - …
12 按位左移 从左到右 … << …
按位右移 … >> …
无符号右移 … >>> …
11 小于 从左到右 … < …
小于等于 … <= …
大于 … > …
大于等于 … >= …
in … in …
instanceof … instanceof …
10 等号 从左到右
… == …
非等号 … != …
全等号 … === …
非全等号 … !== …
9 按位与 从左到右 … & …
8 按位异或 从左到右 … ^ …
7 按位或 从左到右
6 逻辑与 从左到右 … && …
5 逻辑或 从左到右
4 条件运算符 从右到左 … ? … : …
3 赋值 从右到左 … = …
… += …
… -= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
= …
2 yield 从右到左 yield …
yield* yield* …
1 展开运算符 n/a … …
0 逗号 从左到右 … , …