仓颉编程(3)基本操作符
1. 赋值操作符
1.1 基本赋值操作符(=)
赋值操作符(=)是仓颉编程语言中最基础的操作符,用于将右侧表达式的值赋给左侧的变量或常量。在仓颉中,赋值操作符具有以下特性:
语法格式:
左值 = 表达式
其中,左值必须是一个可修改的变量或属性,而表达式可以是任何合法的仓颉表达式。值得注意的是,仓颉是强类型语言,因此右侧表达式的类型必须与左侧变量的类型完全匹配或可以隐式转换为左侧变量的类型。
示例说明:
// 声明并初始化变量
let age: Int64 = 25 // 声明一个Int64类型的常量age并赋值为25
var name: String = "Alice" // 声明一个String类型的变量name并赋值为"Alice"
// 重新赋值(仅适用于变量)
name = "Bob" // 将name重新赋值为"Bob"
// 类型匹配要求
let num: Int32 = 100 // 正确:Int32类型
// let num: Int32 = 3.14 // 错误:不能将Float64类型赋给Int32类型
特殊规则:
- 赋值表达式本身的类型是Unit(空元组()),这意味着不能将赋值表达式作为其他表达式的一部分使用,从而避免了将判等操作符(==)误写成赋值操作符(=)的常见错误
- 例如:if (a = 3) 会编译错误,因为赋值表达式的类型是Unit而不是Bool
1.2 多变量赋值
仓颉支持多变量赋值语法,可以一次性为多个变量赋值,这在需要交换变量值或同时初始化多个变量时非常有用。
语法格式:
(变量1, 变量2, ..., 变量N) = (表达式1, 表达式2, ..., 表达式N)
示例说明:
// 同时初始化多个变量
let (x, y, z) = (1, 2, 3) // x=1, y=2, z=3
// 交换两个变量的值
var a = 5
var b = 10
(a, b) = (b, a) // a=10, b=5
// 使用下划线忽略某些值
let (first, _, third) = (10, 20, 30) // first=10, third=30,忽略第二个值
1.3 解构赋值
解构赋值是仓颉中一种强大的特性,允许从复杂的数据结构(如元组、结构体)中提取值并直接赋给多个变量。
语法格式:
let (变量列表) = 复杂表达式
示例说明:
// 从元组中解构
let person = ("Alice", 25, true)
let (name, age, isStudent) = person // name="Alice", age=25, isStudent=true
// 从结构体中解构
struct Point {var x: Int64var y: Int64
}
let p = Point(x: 10, y: 20)
let (px, py) = p // px=10, py=20
// 从嵌套结构中解构
let nested = ((1, 2), (3, 4))
let ((a, b), (c, d)) = nested // a=1, b=2, c=3, d=4
2. 算术操作符
2.1 一元算术操作符
2.1.1 一元负号(-)
一元负号操作符(-)用于对数值类型的表达式取反,它是一个一元前缀操作符,只能出现在操作数的前面。
语法格式:
-操作数
示例说明:
let num1: Int64 = 8
let num2 = -num1 // num2 = -8
let num3 = -(-num1) // num3 = 8(双重否定)
// 浮点数的一元负号
let floatNum = -3.14 // -3.14
let positiveNum = -floatNum // 3.14
类型要求:
- 操作数必须是数值类型(如Int64、Float64等)
- 结果的类型与操作数的类型相同
2.1.2 幂运算(**)
幂运算操作符(**)用于计算一个数的幂次方,是一个二元操作符,具有右结合性。
语法格式:
底数 ** 指数
示例说明:
let result1 = 2 ** 3 // 8(2的3次方)
let result2 = 3 ** 2 // 9(3的2次方)
let result3 = 10 ** 4 // 10000(10的4次方)
// 浮点数幂运算
let result4 = 2.5 ** 2 // 6.25
let result5 = 4.0 ** 0.5 // 2.0(平方根)
类型规则:
- 当左操作数为Int64类型时,右操作数只能为UInt64类型,结果为Int64类型
- 当左操作数为Float64类型时,右操作数可以是Int64或Float64类型,结果为Float64类型
2.2 二元算术操作符
2.2.1 加法(+)
加法操作符(+)用于计算两个数值的和,也可用于字符串的拼接。
语法格式:
操作数1 + 操作数2
示例说明:
// 整数加法
let sum1 = 5 + 3 // 8
let sum2 = 10 + 20 // 30
// 浮点数加法
let floatSum = 2.5 + 3.5 // 6.0
// 字符串拼接
let greeting = "Hello" + " " + "World" // "Hello World"
let message = "The answer is: " + 42.toString() // "The answer is: 42"
类型要求:
- 两个操作数必须是相同的数值类型或可以转换为相同类型
- 当用于字符串拼接时,至少有一个操作数必须是字符串类型
2.2.2 减法(-)
减法操作符(-)用于计算两个数值的差,是一个二元中缀操作符。
语法格式:
操作数1 - 操作数2
示例说明:
let difference1 = 10 - 4 // 6
let difference2 = 5 - 8 // -3
let difference3 = -5 - 3 // -8
let difference4 = -5 - (-3) // -2
2.2.3 乘法(*)
乘法操作符(*)用于计算两个数值的乘积。
语法格式:
操作数1 * 操作数2
示例说明:
let product1 = 6 * 7 // 42
let product2 = 3 * -2 // -6
let product3 = -4 * -5 // 20
2.2.4 除法(/)
除法操作符(/)用于计算两个数值的商。需要注意的是,当操作数为整数时,结果会向零取整。
语法格式:
操作数1 / 操作数2
示例说明:
let quotient1 = 10 / 3 // 3(向零取整)
let quotient2 = -10 / 3 // -3(向零取整)
let quotient3 = 10.0 / 3.0 // 3.333...(浮点数除法)
整数除法规则:
- 当两个操作数都是整数时,结果也是整数,采用向零取整的方式
- 例如:7 / 3 = 2,-7 / 3 = -2
2.2.5 取模(%)
取模操作符(%)用于计算两个整数相除的余数,操作数只能是整数类型。
语法格式:
被除数 % 除数
示例说明:
let remainder1 = 10 % 3 // 1
let remainder2 = 17 % 5 // 2
let remainder3 = -10 % 3 // -1(余数符号与被除数相同)
let remainder4 = 10 % -3 // 1(余数符号与被除数相同)
取模运算规则:
- 余数的符号与被除数相同
- 数学定义:a % b = a - b * (a / b)
2.3 数值类型支持
仓颉的算术操作符支持多种数值类型,包括整数类型和浮点数类型:
整数类型:
- Int8、Int16、Int32、Int64(有符号整数)
- UInt8、UInt16、UInt32、UInt64(无符号整数)
- IntNative(与平台相关的整数类型)
浮点数类型:
- Float16、Float32、Float64
类型转换规则:
- 不同类型的数值进行运算时,需要显式转换为相同类型
- 例如:
let intNum: Int64 = 10
let floatNum: Float64 = 3.5
let result = Int64(floatNum) + intNum // 错误:需要显式转换
let result = Float64(intNum) + floatNum // 正确:转换为相同类型
2.4 溢出处理机制
仓颉编程语言具有内置的溢出检测机制,当算术运算的结果超出目标类型的表示范围时,会触发运行时错误。这种机制有助于避免因数值溢出导致的意外行为:
溢出检测场景:
- 整数加法、减法、乘法运算
- 整数与浮点数之间的转换
- 数值类型的强制转换
示例:
// 整数溢出示例
let maxInt = Int64.max // Int64的最大值
// let overflow = maxInt + 1 // 会触发溢出错误
// 浮点数溢出示例
let maxFloat = Float64.max
let floatOverflow = maxFloat * 2 // 会得到Infinity(无穷大)
3. 复合赋值操作符
3.1 算术复合赋值操作符
复合赋值操作符是将算术运算与赋值操作结合在一起的简写形式,用于简化代码并提高执行效率。
3.1.1 复合加法赋值(+=)
语法格式:
变量 += 表达式
等价形式:
变量 = 变量 + 表达式
示例说明:
var counter = 0
counter += 1 // counter = counter + 1 → 1
counter += 5 // counter = counter + 5 → 6
counter += -3 // counter = counter + (-3) → 3
3.1.2 复合减法赋值(-=)
语法格式:
变量 -= 表达式
等价形式:
变量 = 变量 - 表达式
示例说明:
var balance = 100
balance -= 20 // balance = balance - 20 → 80
balance -= 30 // balance = balance - 30 → 50
balance -= -10 // balance = balance - (-10) → 60
3.1.3 复合乘法赋值(*=)
语法格式:
变量 *= 表达式
等价形式:
变量 = 变量 * 表达式
示例说明:
var value = 2
value *= 3 // value = value * 3 → 6
value *= 4 // value = value * 4 → 24
value *= 0.5 // value = value * 0.5 → 12
3.1.4 复合除法赋值(/=)
语法格式:
变量 /= 表达式
等价形式:
变量 = 变量 / 表达式
示例说明:
var number = 100
number /= 2 // number = number / 2 → 50
number /= 5 // number = number / 5 → 10
number /= 3 // number = number / 3 → 3(整数除法)
3.1.5 复合取模赋值(%=)
语法格式:
变量 %= 表达式
等价形式:
变量 = 变量 % 表达式
示例说明:
var remainder = 10
remainder %= 3 // remainder = remainder % 3 → 1
remainder %= 2 // remainder = remainder % 2 → 1
remainder %= 5 // remainder = remainder % 5 → 1
3.1.6 复合幂运算赋值(**=)
语法格式:
变量 **= 表达式
等价形式:
变量 = 变量 ** 表达式
示例说明:
var base = 2
base **= 3 // base = base ** 3 → 8
base **= 2 // base = base ** 2 → 64
base **= 0.5 // base = base ** 0.5 → 8(平方根)
3.2 位运算复合赋值操作符
除了算术复合赋值操作符外,仓颉还支持位运算相关的复合赋值操作符:
3.2.1 按位与复合赋值(&=)
语法格式:
变量 &= 表达式
等价形式:
变量 = 变量 & 表达式
示例说明:
var flags = 0b1010 // 10
flags &= 0b0101 // flags = flags & 0b0101 → 0
3.2.2 按位或复合赋值(|=)
语法格式:
变量 |= 表达式
等价形式:
变量 = 变量 | 表达式
示例说明:
var flags = 0b1000 // 8
flags |= 0b0100 // flags = flags | 0b0100 → 12
3.2.3 按位异或复合赋值(^=)
语法格式:
变量 ^= 表达式
等价形式:
变量 = 变量 ^ 表达式
示例说明:
var flags = 0b1100 // 12
flags ^= 0b0101 // flags = flags ^ 0b0101 → 9
3.2.4 左移复合赋值(<<=)
语法格式:
变量 <<= 表达式
等价形式:
变量 = 变量 << 表达式
示例说明:
var number = 3 // 0b11
number <<= 2 // number = number << 2 → 12(0b1100)
3.2.5 右移复合赋值(>>=)
语法格式:
变量 >>= 表达式
等价形式:
变量 = 变量 >> 表达式
示例说明:
var number = 12 // 0b1100
number >>= 2 // number = number >> 2 → 3(0b11)
3.3 逻辑复合赋值操作符
仓颉还支持逻辑运算相关的复合赋值操作符:
3.3.1 逻辑与复合赋值(&&=)
语法格式:
变量 &&= 表达式
等价形式:
变量 = 变量 && 表达式
示例说明:
var condition = true
condition &&= false // condition = condition && false → false
condition &&= true // condition = condition && true → false
3.3.2 逻辑或复合赋值(||=)
语法格式:
变量 ||= 表达式
等价形式:
变量 = 变量 || 表达式
示例说明:
var condition = false
condition ||= true // condition = condition || true → true
condition ||= false // condition = condition || false → true
4. 关系操作符
4.1 基本关系操作符
关系操作符用于比较两个值的大小或相等性,返回一个布尔值(true或false)。
4.1.1 等于(==)
语法格式:
表达式1 == 表达式2
功能说明:判断两个表达式的值是否相等。
示例说明:
let a = 5
let b = 5
let c = 3
print(a == b) // true
print(a == c) // false
print(3.0 == 3) // true(自动类型转换)
4.1.2 不等于(!=)
语法格式:
表达式1 != 表达式2
功能说明:判断两个表达式的值是否不相等。
示例说明:
let a = 5
let b = 3
print(a != b) // true
print(5 != 5) // false
print("hello" != "world") // true
4.1.3 大于(>)
语法格式:
表达式1 > 表达式2
功能说明:判断第一个表达式是否大于第二个表达式。
示例说明:
print(10 > 5) // true
print(3 > 3) // false
print(2 > 4) // false
print(-1 > -2) // true
4.1.4 小于(<)
语法格式:
表达式1 < 表达式2
功能说明:判断第一个表达式是否小于第二个表达式。
示例说明:
print(3 < 5) // true
print(5 < 5) // false
print(4 < 2) // false
print(-2 < -1) // true
4.1.5 大于等于(>=)
语法格式:
表达式1 >= 表达式2
功能说明:判断第一个表达式是否大于或等于第二个表达式。
示例说明:
print(5 >= 5) // true
print(5 >= 3) // true
print(3 >= 5) // false
4.1.6 小于等于(<=)
语法格式:
表达式1 <= 表达式2
功能说明:判断第一个表达式是否小于或等于第二个表达式。
示例说明:
print(5 <= 5) // true
print(3 <= 5) // true
print(5 <= 3) // false
4.2 类型比较规则
仓颉的关系操作符在类型比较方面有严格的规则:
- 类型一致性要求:关系操作符要求两个操作数的类型必须相同,这与其他操作符的要求一致。
- 基本类型比较:
- 数值类型(整数、浮点数):按照数值大小进行比较
- 布尔类型:true大于false
- 字符类型:按照 Unicode 编码值进行比较
- 字符串类型:按照字典序进行比较
- 元组类型比较:
- 只有当元组的所有元素类型都支持==和!=操作时,该元组类型才支持这些操作
- 两个同类型的元组相等,当且仅当相同位置的元素全部相等
- 自定义类型比较:
- 自定义类型需要实现Equatable协议才能使用==和!=操作符
- 可以通过重载操作符来提供自定义的比较逻辑
4.3 比较运算示例
以下是一些关系操作符的综合使用示例:
// 数值比较
let age = 25
let votingAge = 18
print(age >= votingAge) // true
// 字符串比较
let str1 = "apple"
let str2 = "banana"
print(str1 < str2) // true(按字典序比较)
// 复合条件判断
let temperature = 25
let humidity = 60
print(temperature > 20 && humidity < 70) // true
// 元组比较
let point1 = (2, 3)
let point2 = (2, 3)
let point3 = (3, 2)
print(point1 == point2) // true
print(point1 == point3) // false
// 浮点数比较(注意精度问题)
let a = 0.1 + 0.2
let b = 0.3
print(a == b) // false(由于精度问题)
print(fabs(a - b) < 1e-10) // true(使用容差比较)
4.4 链式比较
仓颉支持链式比较语法,可以在一个表达式中进行多个连续的比较:
语法格式:
表达式1 操作符1 表达式2 操作符2 表达式3 ...
示例说明:
let x = 5
print(0 < x < 10) // true
print(10 > x >= 5) // true
print(3 <= x < 5) // false
// 链式比较在循环中的应用
for i in 1..10 {if 2 <= i <= 8 {print(i) // 输出2到8}
}
5. Coalescing 操作符
5.1 基本语法与功能
Coalescing 操作符(??)是仓颉中用于处理可选类型(Option)的特殊操作符,它提供了一种简洁的方式来处理可能缺失的值。
语法格式:
表达式1 ?? 表达式2
功能说明:
- 当表达式1的值为Some(v)时,返回v的值,并且不会对表达式2求值(短路特性)
- 当表达式1的值为None时,返回表达式2的值
5.2 可选类型处理机制
在仓颉中,可选类型(Option)用于表示一个值可能存在或不存在的情况。Option类型有两个变体:
- Some(v):表示存在一个值v
- None:表示值缺失
Coalescing 操作符提供了一种优雅的方式来处理Option类型的值:
示例说明:
// 基本用法
let value1: Option<Int64> = Some(10)
let value2: Option<Int64> = None
let result1 = value1 ?? 0 // 10
let result2 = value2 ?? 0 // 0
// 复杂表达式作为默认值
let defaultValue = calculateDefaultValue()
let finalValue = optionalValue ?? defaultValue
5.3 短路特性
Coalescing 操作符具有重要的短路特性:
- 左侧为 Some 时的行为:当表达式1的值为Some(v)时,表达式2不会被求值,直接返回v。
- 应用场景:
- 避免不必要的计算
- 防止副作用的发生
- 提高程序性能
示例说明:
func expensiveComputation() -> Int64 {print("执行耗时计算...")return 42
}
let optionalValue: Option<Int64> = Some(10)
let result = optionalValue ?? expensiveComputation() // 不会执行expensiveComputation()
print(result) // 10
let anotherValue: Option<Int64> = None
let anotherResult = anotherValue ?? expensiveComputation() // 会执行expensiveComputation()
print(anotherResult) // 42
6. 区间操作符
6.1 左闭右开区间(..)
左闭右开区间操作符(..)用于创建一个从起始值开始到终止值结束(不包含终止值)的区间。
语法格式:
start..end
功能说明:
- 包含起始值start
- 不包含终止值end
- 步长默认为 1,也可以显式指定步长
示例说明:
// 整数区间
let range1 = 0..5 // 包含0, 1, 2, 3, 4(不包含5)
// 浮点区间
let range2 = 0.0..3.0 // 包含0.0, 1.0, 2.0(步长1.0)
// 负数区间
let range3 = -3..0 // 包含-3, -2, -1(不包含0)
// 带步长的区间
let range4 = 0..10 : 2 // 包含0, 2, 4, 6, 8(步长2)
let range5 = 10..0 : -2 // 包含10, 8, 6, 4, 2(负步长)
6.2 闭区间(..=)
闭区间操作符(..=)用于创建一个从起始值开始到终止值结束(包含终止值)的区间。
语法格式:
start..=end
功能说明:
- 包含起始值start
- 包含终止值end
- 步长默认为 1,也可以显式指定步长
示例说明:
// 整数闭区间
let range1 = 1..=5 // 包含1, 2, 3, 4, 5
// 字符区间
let charRange = 'a'..='z' // 包含从'a'到'z'的所有小写字母
// 带步长的闭区间
let range2 = 0..=10 : 2 // 包含0, 2, 4, 6, 8, 10
let range3 = 10..=0 : -2 // 包含10, 8, 6, 4, 2, 0
6.3 区间类型的属性
区间类型(Range)具有以下核心属性:
- start:序列的起始值
- end:序列的终止值
- step:序列中前后两个元素之间的差值(步长),类型为Int64,不能为 0
区间特性:
- 区间可以是递增的,也可以是递减的
- 区间可能为空(当步长为正时start >= end,或步长为负时start <= end)
- 区间的元素类型由start和end的类型决定
6.4 区间的遍历与操作
6.4.1 区间遍历
区间类型支持使用for-in循环进行遍历:
示例说明:
// 遍历整数区间
for i in 1..5 {print(i) // 输出1, 2, 3, 4
}
for i in 0..=10 : 2 {print(i) // 输出0, 2, 4, 6, 8, 10
}
// 反向遍历
for i in 10..0 : -1 {print(i) // 输出10, 9, 8, 7, 6, 5, 4, 3, 2, 1
}
// 浮点区间遍历
for x in 0.0..3.0 {print(x) // 输出0.0, 1.0, 2.0
}
6.4.2 区间的常用操作
- 判断元素是否在区间内:
let range = 1..=10
print(5 in range) // true
print(11 in range) // false
- 获取区间长度:
let range = 0..10 : 2
let length = range.count() // 5
- 区间转换为数组:
let range = 1..=5
let array = Array(range) // [1, 2, 3, 4, 5]
- 区间切片:
let range = 0..10
let subRange = range[2..5] // 包含2, 3, 4
7. 逻辑操作符
7.1 逻辑非(!)
逻辑非操作符(!)是一个一元前缀操作符,用于对布尔值取反。
语法格式:
!表达式
功能说明:
- 当操作数为true时,结果为false
- 当操作数为false时,结果为true
示例说明:
let a = true
let b = false
print(!a) // false
print(!b) // true
print(!!a) // true(双重否定)
print(!(a && b)) // true
7.2 逻辑与(&&)
逻辑与操作符(&&)是一个二元操作符,用于判断两个条件是否同时成立。
语法格式:
表达式1 && 表达式2
功能说明:
- 只有当表达式1和表达式2都为true时,结果才为true
- 只要有一个为false,结果就为false
示例说明:
print(true && true) // true
print(true && false) // false
print(false && true) // false
print(false && false) // false
// 复合条件
let age = 25
let isStudent = true
print(age >= 18 && isStudent) // true
7.3 逻辑或(||)
逻辑或操作符(||)是一个二元操作符,用于判断两个条件中是否至少有一个成立。
语法格式:
表达式1 || 表达式2
功能说明:
- 只要表达式1或表达式2有一个为true,结果就为true
- 只有当两个都为false时,结果才为false
示例说明:
print(true || true) // true
print(true || false) // true
print(false || true) // true
print(false || false) // false
// 检查条件
let temperature = 30
let humidity = 70
print(temperature > 35 || humidity > 80) // false
7.4 短路求值特性
逻辑操作符(&&和||)具有重要的短路求值特性:
1. 逻辑与的短路特性:
- 当表达式1为false时,表达式2不会被求值
- 整个表达式直接返回false
2. 逻辑或的短路特性:
- 当表达式1为true时,表达式2不会被求值
- 整个表达式直接返回true
短路特性的实际应用:
func isEven(_ number: Int64) -> Bool {print("检查是否为偶数")return number % 2 == 0
}
func isPositive(_ number: Int64) -> Bool {print("检查是否为正数")return number > 0
}
// 短路示例
let number = -4
print(isPositive(number) && isEven(number)) // 输出:检查是否为正数 → false
// 由于isPositive返回false,isEven不会被调用
print(isPositive(number) || isEven(number)) // 输出:检查是否为正数 → false → 检查是否为偶数 → true
// 由于isPositive返回false,继续调用isEven
8. 位运算操作符
8.1 按位取反(!)
按位取反操作符(!)是一个一元前缀操作符,用于对整数的每一位进行取反操作。
语法格式:
!表达式
功能说明:
- 将操作数的每一位(包括符号位)取反(0 变 1,1 变 0)
- 操作数必须是整数类型
- 结果的类型与操作数相同
示例说明:
// 8位整数示例
let a: Int8 = 0b00000101 // 5
let b: Int8 = !a // 0b11111010 → -6(补码表示)
// 16位整数示例
let c: Int16 = 0b0000000000000101 // 5
let d: Int16 = !c // 0b1111111111111010 → -6
// 32位整数示例
let e: Int32 = 5
let f: Int32 = !e // -6
8.2 按位与(&)
按位与操作符(&)是一个二元操作符,用于对两个整数的对应位进行与运算。
语法格式:
表达式1 & 表达式2
功能说明:
- 只有当两个操作数的对应位都为 1 时,结果位才为 1
- 否则结果位为 0
- 操作数必须是相同的整数类型
示例说明:
// 二进制示例
let a = 0b1010 // 10
let b = 0b0101 // 5
let c = a & b // 0b0000 → 0
// 十进制示例
let d = 10 & 5 // 0
let e = 12 & 5 // 4 (12=1100, 5=0101 → 0100=4)
let f = 15 & 9 // 9 (15=1111, 9=1001 → 1001=9)
// 应用场景:检查特定位是否为1
let flags = 0b1010
if flags & 0b0010 != 0 {print("第二位为1")
}
8.3 按位或(|)
按位或操作符(|)是一个二元操作符,用于对两个整数的对应位进行或运算。
语法格式:
表达式1 | 表达式2
功能说明:
- 当两个操作数的对应位中有一个为 1 时,结果位就为 1
- 只有当两个操作数的对应位都为 0 时,结果位才为 0
示例说明:
// 二进制示例
let a = 0b1010 // 10
let b = 0b0101 // 5
let c = a | b // 0b1111 → 15
// 十进制示例
let d = 10 | 5 // 15
let e = 12 | 5 // 13 (12=1100, 5=0101 → 1101=13)
let f = 8 | 4 // 12
// 应用场景:设置特定位为1
var flags = 0b0000
flags |= 0b0010 // 设置第二位为1
flags |= 0b0100 // 设置第三位为1
print(flags) // 0b0110 → 6
8.4 按位异或(^)
按位异或操作符(^)是一个二元操作符,用于对两个整数的对应位进行异或运算。
语法格式:
表达式1 ^ 表达式2
功能说明:
- 当两个操作数的对应位不同时,结果位为 1
- 当两个操作数的对应位相同时,结果位为 0
示例说明:
// 二进制示例
let a = 0b1010 // 10
let b = 0b0101 // 5
let c = a ^ b // 0b1111 → 15
// 十进制示例
let d = 10 ^ 5 // 15
let e = 12 ^ 5 // 9 (12=1100, 5=0101 → 1001=9)
let f = 8 ^ 8 // 0
// 应用场景:翻转特定位
var flags = 0b1010
flags ^= 0b0010 // 翻转第二位
print(flags) // 0b1000 → 8
// 异或的特殊性质
print(5 ^ 5) // 0
print(5 ^ 0) // 5
print((5 ^ 3) ^ 3) // 5
8.5 左移(<<)
左移操作符(<<)是一个二元操作符,用于将整数的所有位向左移动指定的位数。
语法格式:
表达式1 << 表达式2
功能说明:
- 将表达式1的所有位向左移动表达式2位
- 右侧用 0 填充
- 左侧溢出的位被丢弃
- 第二个操作数必须是非负整数
示例说明:
// 整数左移
let a = 3 << 2 // 12 (3=11 → 1100=12)
let b = 5 << 3 // 40 (5=101 → 101000=40)
let c = 1 << 5 // 32 (1左移5位)
// 负数左移
let d: Int8 = -3 // 11111101
let e: Int8 = d << 2 // 11110100 → -12
// 应用场景:快速乘法
print(5 << 3) // 40 (相当于5 × 8)
print(10 << 2) // 40 (相当于10 × 4)
8.6 右移(>>)
右移操作符(>>)是一个二元操作符,用于将整数的所有位向右移动指定的位数。
语法格式:
表达式1 >> 表达式2
功能说明:
- 将表达式1的所有位向右移动表达式2位
- 对于无符号数,左侧用 0 填充
- 对于有符号数,左侧用符号位填充(算术右移)
- 第二个操作数必须是非负整数
示例说明:
// 无符号数右移
let a: UInt8 = 12 >> 2 // 3 (12=1100 → 11=3)
let b: UInt8 = 40 >> 3 // 5 (40=101000 → 101=5)
// 有符号数右移
let c: Int8 = -12 // 11110100
let d: Int8 = c >> 2 // 11111101 → -3
// 应用场景:快速除法
print(40 >> 3) // 5 (相当于40 ÷ 8)
print(12 >> 2) // 3 (相当于12 ÷ 4)
// 注意:负数右移
print(-12 >> 2) // -3
9. 自增自减操作符
9.1 自增操作符(++)
自增操作符(++)用于将变量的值增加 1,它只能作为后缀操作符使用。
语法格式:
变量++
功能说明:
- 将变量的值增加 1
- 表达式的类型为Unit(空元组)
- 操作后变量的值会改变,但表达式本身不返回值
示例说明:
var counter = 0
counter++ // counter变为1
counter++ // counter变为2
counter++ // counter变为3
// 自增表达式的类型是Unit
let result = counter++ // 错误:不能将Unit赋给Int64类型
9.2 自减操作符(--)
自减操作符(--)用于将变量的值减少 1,它也只能作为后缀操作符使用。
语法格式:
变量--
功能说明:
- 将变量的值减少 1
- 表达式的类型为Unit
- 操作后变量的值会改变,但表达式本身不返回值
示例说明:
var count = 5
count-- // count变为4
count-- // count变为3
count-- // count变为2
// 结合循环使用
while count > 0 {print(count)count--
}
// 输出:2, 1
9.3 后缀操作符特性
仓颉的自增自减操作符具有以下特性:
- 只能作为后缀操作符:
- 与 C/C++ 不同,仓颉不支持前缀形式(++var或--var)
- 只能使用后缀形式(var++或var--)
- 类型限制:
- 操作数必须是整数类型(如Int64、Int32等)
- 不能用于浮点数或其他类型
- 可赋值要求:
- 操作数必须是可被赋值的(即必须是变量,不能是常量或表达式)
- 副作用:
- 自增自减操作会修改变量的值
- 这是一种副作用,在某些上下文中需要谨慎使用
10. 操作符优先级
10.1 优先级规则总览
操作符优先级决定了在没有括号的情况下,表达式中各操作符的执行顺序。优先级高的操作符先执行,优先级低的后执行。仓颉的操作符优先级规则如下(数字越小,优先级越高):
优先级 | 操作符 | 名称 | 结合性 |
0 | @ | 宏调用 | 右结合 |
1 | . [] () | 成员访问、索引、函数调用 | 左结合 |
2 | ++ -- ? | 自增、自减、问号操作符 | 无 |
3 | ! - | 按位取反、逻辑非、一元负号 | 右结合 |
4 | ** | 幂运算 | 右结合 |
5 | * / % | 乘法、除法、取模 | 左结合 |
6 | + - | 加法、减法 | 左结合 |
7 | << >> | 左移、右移 | 左结合 |
8 | .. ..= | 区间操作符 | 无 |
9 | < <= > >= is as | 比较操作符、类型检查、类型转换 | 无 |
10 | == != | 等于、不等于 | 无 |
11 | & | 按位与 | 左结合 |
12 | ^ | 按位异或 | 左结合 |
13 | | | 按位或 | 左结合 |
14 | && | 逻辑与 | 左结合 |
15 | || | 逻辑或 | 左结合 |
16 | ?? | Coalescing 操作符 | 右结合 |
17 | > ~> | pipeline 操作符、composition 操作符 | |
18 | = *= /= %= += -= **= <<= >>= &= ^= |= &&= ||= | 赋值、复合赋值 | 无 |
10.2 结合性规则详解
结合性决定了当多个相同优先级的操作符出现在同一个表达式中时的执行顺序。
10.2.1 左结合性
左结合性意味着操作符从左到右依次执行。具有左结合性的操作符包括:
- 算术操作符(*, /, %, +, -)
- 位运算操作符(<<, >>, &, ^, |)
- 逻辑操作符(&&, ||)
示例说明:
// 左结合示例
let a = 10 - 3 - 2 // 等价于 (10 - 3) - 2 = 5
let b = 8 / 2 / 2 // 等价于 (8 / 2) / 2 = 2
let c = 2 * 3 * 4 // 等价于 (2 * 3) * 4 = 24
// 混合操作符示例
let d = 10 + 3 * 2 // 3*2先执行,因为*优先级高于+ → 16
let e = 10 * 3 + 2 // 10*3先执行 → 32
10.2.2 右结合性
右结合性意味着操作符从右到左依次执行。具有右结合性的操作符包括:
- 幂运算(**)
- 一元操作符(!, -)
- Coalescing 操作符(??)
示例说明:
// 右结合示例
let a = 2 ** 3 ** 2 // 等价于 2 ** (3 ** 2) = 512
let b = 10 - -3 // 等价于 10 - (-3) = 13
let c = ! ! true // 等价于 !(!true) = true
// 连续幂运算
let d = 2 ** 3 ** 2 ** 1 // 等价于 2 ** (3 ** (2 ** 1)) = 2 ** (3 ** 2) = 512
10.2.3 无结合性
某些操作符没有结合性,它们不能连续使用:
- 自增自减操作符(++, --)
- 区间操作符(.., ..=)
- 关系操作符(<, >, <=, >=, ==, !=)
- 赋值操作符(=, *=, /=, 等)
示例说明:
// 无结合性示例
let a = 10 > 5 > 3 // 错误:关系操作符不能连续使用
let b = 5 = 3 // 错误:赋值操作符不能连续使用
let c = i++ + j++ // 错误:自增操作符不能连续使用
10.3 优先级冲突的解决
当表达式中存在多个不同优先级的操作符时,优先级规则决定了执行顺序。但在某些情况下,可能会出现歧义,这时可以使用括号来明确执行顺序。
示例说明:
// 算术操作符优先级
let a = 10 + 2 * 3 // 2*3先执行 → 16
let b = (10 + 2) * 3 // 10+2先执行 → 36
// 位运算与算术运算
let c = 3 + 4 << 2 // 4<<2先执行 → 3 + 16 = 19
let d = (3 + 4) << 2 // 3+4先执行 → 7 << 2 = 28
// 逻辑操作符与关系操作符
let e = 5 > 3 && 6 < 10 // 关系操作符先执行 → true
let f = 5 > (3 && 6) // 错误:不能对非布尔值进行比较
// 复合操作符的优先级
let g = 10 += 5 * 2 // 5*2先执行 → 10 += 10 → 20
let h = (10 += 5) * 2 // 错误:复合赋值表达式类型为Unit,不能参与运算
10.4 操作符优先级的实际应用
理解操作符优先级对于编写正确的代码至关重要:
1. 条件表达式:
// 正确理解优先级
let age = 25
let isStudent = true
let canVote = age >= 18 && !isStudent // 先比较age>=18,再取反isStudent
// 使用括号提高可读性
let canDrive = (age >= 16) && (isStudent || hasLicense)
2. 位掩码操作:
// 正确的位掩码操作
let flags = 0b1010
let result = flags & 0b0011 | 0b0100 // 先按位与,再按位或
// 避免歧义
let safeResult = (flags & 0b0011) | 0b0100
3. 数学表达式:
// 复杂数学表达式
let x = 3
let y = 4
let z = 5
let result = x + y * z ** 2 / (x + y) // 正确理解优先级很重要
// 使用括号明确顺序
let clearResult = x + (y * (z ** 2)) / (x + y)