【仓颉语言基础语法学与练】第2课:运算符、类型转换与流程控制
本课目标
· 掌握仓颉的算术、关系、逻辑、位运算符及其优先级。
· 理解赋值表达式与复合赋值运算符的特殊性。
· 掌握数值类型之间的显式转换,以及 is 和 as 操作符的用法。
· 理解条件表达式 if 的返回值特性。
· 掌握 while、do-while、for-in 三种循环,以及 break、continue 的使用。
· 掌握函数重载、命名参数、默认值和尾随 lambda。
· 认识元组、数组、字符串三种常见复合类型的创建与访问。
· 能独立完成本课全部习题。
一、运算符详解
- 算术运算符
仓颉的算术运算符包括一元负号 - 和六个二元运算符:+、-、*、/、%、**。
main() { // 程序入口
let a: Int64 = 10 // 定义整数 a
let b: Int64 = 3 // 定义整数 b
println(a + b) // 加法:13
println(a - b) // 减法:7
println(a * b) // 乘法:30
println(a / b) // 整数除法:3(截断小数)
println(a % b) // 取模:1
println(a ** b) // 幂运算:10 的 3 次方 = 1000
println(-a) // 一元负号:-10
} // main 结束
重要规则:仓颉的大部分二元运算符要求参与运算的两个操作数必须是相同类型,否则编译器报错。例如 Int64 + Int32 是不合法的,必须先将其中一个显式转换。
- 关系运算符
关系运算符用于比较两个值,返回 Bool 类型。包括 <、<=、>、>=、==、!=。
main() { // 程序入口
let x: Int64 = 5 // 定义变量 x
let y: Int64 = 8 // 定义变量 y
println(x < y) // true
println(x <= y) // true
println(x > y) // false
println(x >= y) // false
println(x == y) // false
println(x != y) // true
} // main 结束
注意:关系运算符不支持连续使用,例如 a < b < c 是编译错误。需要使用 && 连接:a < b && b < c。
- 逻辑运算符
仓颉提供 &&(逻辑与)、||(逻辑或)和 !(逻辑非)。&& 和 || 都支持短路求值。
main() { // 程序入口
var ic = 10 // 定义可变变量 ic
let r1 = (ic == ic) && (ic = 12 > 0) // 左为 true,执行右,ic 变为 12
println("${r1} ${ic}") // true 12
let r2 = (ic != ic) && (ic = 99 > 0) // 左为 false,短路,右不执行
println("${r2} ${ic}") // false 12
let r3 = true || (ic = 0 > 0) // 左为 true,短路,右不执行
println("${r3} ${ic}") // true 12
} // main 结束
解读:&& 左侧为 false 时,右侧不会执行;|| 左侧为 true 时,右侧不会执行。
- 位运算符
仓颉支持 &(按位与)、|(按位或)、^(按位异或)、<<(左移)、>>(右移)、!(按位求反)。
main() { // 程序入口
let a: Int64 = 0b1100 // 二进制 1100,十进制 12
let b: Int64 = 0b1010 // 二进制 1010,十进制 10
println(a & b) // 按位与:0b1000 = 8
println(a | b) // 按位或:0b1110 = 14
println(a ^ b) // 按位异或:0b0110 = 6
println(a << 1) // 左移一位:24
println(a >> 1) // 右移一位:6
println(!a) // 按位求反:-13
} // main 结束
- 赋值与复合赋值运算符
赋值运算符 = 和复合赋值运算符 +=、-=、*=、/=、%=、**= 等,所有赋值表达式的类型都是 Unit,值为 (),而不是返回左表达式的引用。
main() { // 程序入口
var a: Int64 = 128 // 定义可变变量 a
let b: Int64 = 64 // 定义不可变变量 b
a += b // 等价于 a = a + b,a 变为 192
println(a) // 输出 192
a -= 32 // a 变为 160
a *= 2 // a 变为 320
a /= 4 // a 变为 80
a %= 7 // a 变为 3
println(a) // 输出 3
} // main 结束
注意:由于赋值表达式类型是 Unit,不能写 let x = (a = 5),这会导致类型不匹配错误。
- 运算符优先级
仓颉运算符优先级从高到低:宏调用 @ → 成员访问 .、索引 []、函数调用 () → 自增 ++、自减 – → 逻辑非 !、一元负号 - → 幂 ** → 乘除 * / % → 加减 + - → 移位 << >> → 区间 … …= → 关系 < <= > >= → 判等 == != → 按位与 & → 按位异或 ^ → 按位或 | → 逻辑与 && → 逻辑或 || → 赋值 = 及复合赋值。
main() { // 程序入口
let a: Int64 = 3 + 2 * 6 // 先算 2*6=12,再算 3+12=15
println(a) // 输出 15
let b = (3 + 2) * 6 // 括号优先:先 3+2=5,再 5*6=30
println(b) // 输出 30
let c = a == 15 && b == 30 // 先算两边关系,再算 &&
println(c) // 输出 true
} // main 结束
二、类型转换
- 数值类型之间的转换
仓颉不支持不同类型之间的隐式转换,类型转换必须显式进行。数值类型使用 T(e) 的方式转换,其中 T 是目标类型,e 是源表达式。
main() { // 程序入口
let a: Int8 = 10 // 定义 Int8
let b: Int16 = 20 // 定义 Int16
let r1 = Int16(a) // Int8 转 Int16
println("r1 = ${r1}, 类型 Int16") // 输出 r1 = 10
let r2 = Int8(b) // Int16 转 Int8
println("r2 = ${r2}, 类型 Int8") // 输出 r2 = 20
let c: Float32 = 1.0 // 定义 Float32
let d: Float64 = 1.123456789 // 定义 Float64
let r3 = Float64(c) // Float32 转 Float64
println("r3 = ${r3}") // 输出 1.000000
let r4 = Float32(d) // Float64 转 Float32,精度丢失
println("r4 = ${r4}") // 输出 1.123457
let e: Int64 = 1024 // 定义 Int64
let r5 = Float64(e) // Int64 转 Float64
println("r5 = ${r5}") // 输出 1024.000000
let f: Float64 = 1024.1024 // 定义 Float64
let r6 = Int64(f) // Float64 转 Int64,截断小数
println("r6 = ${r6}") // 输出 1024
} // main 结束
注意:类型转换时可能发生溢出,若溢出可提前在编译器检测出来,编译器会直接报错;否则根据默认的溢出策略抛出异常。
- is 操作符
is 用于判断表达式的类型是否是指定类型或其子类型,返回 Bool 值。
open class Animal { // 定义父类 Animal
public func speak(): String { // 定义成员函数
return "..." // 返回占位符
} // 函数结束
} // 类结束
class Dog <: Animal { // 定义子类 Dog,继承 Animal
public func speak(): String { // 重写成员函数
return "汪汪" // 返回狗叫声
} // 函数结束
} // 类结束
main() { // 程序入口
let animal: Animal = Dog() // 用子类实例初始化父类变量
println(animal is Dog) // true,animal 的实际类型是 Dog
println(animal is Animal) // true,Dog 是 Animal 的子类型
let s: String = "hello" // 定义字符串
println(s is String) // true
} // main 结束
- as 操作符
as 用于将表达式的类型转换为指定类型。由于类型转换可能失败,as 返回的是一个 Option 类型。对于 e as T,如果转换成功,值为 Option.Some(e);如果失败,值为 Option.None。
open class Animal { // 定义父类
public func speak(): String { // 定义成员函数
return "..." // 返回占位符
} // 函数结束
} // 类结束
class Dog <: Animal { // 定义子类
public func speak(): String { // 重写函数
return "汪汪" // 返回狗叫声
} // 函数结束
} // 类结束
class Cat <: Animal { // 定义另一个子类
public func speak(): String { // 重写函数
return "喵喵" // 返回猫叫声
} // 函数结束
} // 类结束
main() { // 程序入口
let animal: Animal = Dog() // 父类引用指向子类实例
let dog = animal as Dog // 转换为 Dog,返回 Option<Dog>
match (dog) { // 对 Option 进行模式匹配
case Some(d) => println(d.speak()) // 转换成功,输出“汪汪”
case None => println("不是 Dog") // 转换失败
} // match 结束
let cat = animal as Cat // 转换为 Cat
match (cat) { // 对 Option 进行模式匹配
case Some(c) => println(c.speak()) // 转换成功
case None => println("不是 Cat") // 转换失败,输出“不是 Cat”
} // match 结束
} // main 结束
关键区别:is 返回 Bool,用于类型判断;as 返回 Option,用于安全类型转换。
三、流程控制
- if 表达式
仓颉的 if 是表达式,可以被求值并作为值使用。if 的值与类型需要根据使用场景来确定。
main() { // 程序入口
let score = 85 // 定义成绩
let result = if (score >= 90) { // if 作为表达式,返回值赋给 result
"优秀" // 分支的值
} else if (score >= 60) { // 继续判断
"及格" // 分支的值
} else { // 兜底分支
"不及格" // 分支的值
} // if 表达式结束
println(result) // 输出“及格”
} // main 结束
代码块的值规则:由 {} 包围的代码块,其值与类型等于其中最后一个表达式的值与类型。空代码块的类型为 Unit,值为 ()。
- while 循环
while 表达式的类型是 Unit,值为 ()。条件为 true 时持续执行循环体。
main() { // 程序入口
var i = 1 // 循环变量初始为 1
while (i <= 5) { // 条件为 true 时循环
println("第 ${i} 次") // 输出当前次数
i = i + 1 // i 自增
} // while 结束
} // main 结束
- do-while 循环
do-while 至少执行一次循环体,然后再判断条件。
main() { // 程序入口
var n = 10 // 定义变量 n
do { // 先执行循环体
println("执行一次:${n}") // 输出
n = n + 1 // n 自增
} while (n < 5) // 条件为 false,循环结束
println("最终 n = ${n}") // 输出 11
} // main 结束
解读:n = 10 时条件 n < 5 为 false,但 do-while 先执行一次循环体,所以至少输出一次。
- for-in 循环
for-in 用于遍历区间或集合。遍历元组构成的序列时,可以使用解构。
main() { // 程序入口
for (i in 1..=5) { // 遍历闭区间 1 到 5
println("区间:${i}") // 输出 1 到 5
} // for 结束
let scores: Array<Int64> = [85, 90, 78] // 定义数组
for (score in scores) { // 遍历数组元素
println("成绩:${score}") // 输出 85、90、78
} // for 结束
} // main 结束
- break 与 continue
break 用于提前终止当前循环,跳出循环体;continue 用于跳过当前循环的剩余部分,直接进入下一轮。
main() { // 程序入口
for (i in 1..=10) { // 遍历 1 到 10
if (i == 6) { // 当 i 等于 6
break // 终止循环
} // if 结束
if (i % 2 == 0) { // 偶数时
continue // 跳过本次循环
} // if 结束
println("奇数:${i}") // 输出 1、3、5
} // for 结束
} // main 结束
重要:break、continue、return、throw 表达式的类型是 Nothing,程序执行到这些表达式时,它们之后的代码不会被执行。
四、函数进阶
- 函数重载
仓颉允许在同一作用域内定义多个同名函数,编译器根据参数的个数和类型来决定调用哪个函数。
func add(a: Int64, b: Int64): Int64 { // 版本 1:两个 Int64
return a + b // 返回两数之和
} // 函数结束
func add(a: Int32, b: Int32): Int32 { // 版本 2:两个 Int32
return a + b // 返回两数之和
} // 函数结束
func add(a: Int64, b: Int64, c: Int64): Int64 { // 版本 3:三个 Int64
return a + b + c // 返回三数之和
} // 函数结束
main() { // 程序入口
println(add(1, 2)) // 调用版本 1
println(add(1, 2, 3)) // 调用版本 3
} // main 结束
- 命名参数
函数定义时在参数名后添加 ! 即为命名参数,调用时必须在实参值前指定参数名。
func dateOf(year!: Int64, month!: Int64, day!: Int64): String { // 定义命名参数
return "${year}年${month}月${day}日" // 返回日期字符串
} // 函数结束
main() { // 程序入口
println(dateOf(year: 2024, month: 6, day: 21)) // 必须写参数名
println(dateOf(month: 12, day: 25, year: 2025)) // 参数顺序可调整
} // main 结束
- 参数默认值
函数定义中可以为特定形参提供默认值,调用时如果使用默认值则可以省略该参数。
func greet(name!: String, greeting!: String = "你好"): String { // greeting 有默认值
return "${greeting},${name}" // 返回拼接结果
} // 函数结束
main() { // 程序入口
println(greet(name: "仓颉")) // 使用默认 greeting
println(greet(name: "开发者", greeting: "欢迎")) // 显式传入 greeting
} // main 结束
- 尾随 lambda
仓颉支持尾随 lambda 语法糖,将函数作为最后一个参数传入时,可以写在括号外面。
func repeat(times: Int64, action: () -> Unit): Unit { // 定义函数,接收 lambda
for (i in 1..=times) { // 重复 times 次
action() // 调用 lambda
} // for 结束
} // 函数结束
main() { // 程序入口
repeat(3) { // 尾随 lambda 写法
println("执行一次") // lambda 体
} // 调用结束
} // main 结束
五、常见复合类型入门
- 元组
元组将多个不同类型的值组合在一起,使用括号表示。元组是固定大小的不可变集合。可以通过索引访问元素(从 0 开始),也可以解构赋值。
main() { // 程序入口
let person: (String, Int64) = ("Alice", 30) // 定义二元组
println(person[0]) // 索引访问:Alice
println(person[1]) // 索引访问:30
let (name, age) = person // 解构赋值
println("${name} 今年 ${age} 岁") // 输出 Alice 今年 30 岁
let (_, onlyAge) = person // 使用 _ 忽略不需要的元素
println("只取年龄:${onlyAge}") // 输出 30
} // main 结束
- 数组
数组用于存储同一类型的多个元素,使用 Array 表示,元素按插入顺序排列。可以通过索引访问,也可以用 for-in 遍历。
main() { // 程序入口
let scores: Array<Int64> = [85, 90, 78] // 定义 Int64 数组
println(scores[0]) // 索引访问:85
println(scores[2]) // 索引访问:78
for (score in scores) { // 遍历数组元素
println("成绩:${score}") // 依次输出 85、90、78
} // for 结束
} // main 结束
- 字符串
字符串使用双引号表示,支持 == 比较、+ 拼接和 ${} 插值。字符串也支持切片操作。
main() { // 程序入口
let s1 = "Hello" // 定义字符串
let s2 = "Cangjie" // 定义字符串
let s3 = s1 + ", " + s2 // 使用 + 拼接字符串
println(s3) // 输出 Hello, Cangjie
println(s1 == "Hello") // true,字符串比较
println(s1.size) // 输出 5,字符串长度
let sub = s3[0..5] // 切片,取前 5 个字符
println(sub) // 输出 Hello
} // main 结束
六、多元化习题与答案
A. 选择题
题目1:下列哪个运算符的优先级最高?
A. +
B. *
C. **
D. &&
答案与解读:C。优先级从高到低:**(4)→ *(5)→ +(6)→ &&(14)。
题目2:仓颉中 Int64 + Int32 的结果是?
A. 自动提升为 Int64
B. 自动截断为 Int32
C. 编译错误,需要显式转换
D. 运行时异常
答案与解读:C。仓颉大部分二元运算符要求参与运算的两个操作数必须是相同类型。
题目3:as 操作符返回的类型是?
A. T
B. Bool
C. Option
D. Unit
答案与解读:C。as 因为可能失败,返回 Option。
题目4:下列哪个表达式的类型是 Unit?
A. 1 + 2
B. a > b
C. a = 5
D. “hello”
答案与解读:C。所有赋值表达式的类型都是 Unit,值为 ()。
题目5:do-while 循环的特点是?
A. 条件为 true 时才执行
B. 至少执行一次循环体
C. 不能使用 break
D. 返回 Bool 值
答案与解读:B。do-while 先执行循环体,再判断条件,因此至少执行一次。
题目6:break 和 continue 表达式的类型是?
A. Unit
B. Bool
C. Nothing
D. Int64
答案与解读:C。break、continue、return、throw 的类型都是 Nothing。
题目7:函数重载的依据是?
A. 函数名不同
B. 返回值类型不同
C. 参数个数或类型不同
D. 函数体不同
答案与解读:C。仓颉根据参数的个数和类型来决定调用哪个重载函数。
题目8:元组元素访问的索引从几开始?
A. 0
B. 1
C. -1
D. 任意
答案与解读:A。元组支持通过 t[index] 访问,索引从 0 开始。
题目9:下列哪个是仓颉合法的区间操作符?
A. …
B. …=
C. …<
D. =>
答案与解读:B。… 是左闭右开,…= 是闭区间。
题目10:if 表达式作为值使用时,其值的类型由什么决定?
A. 固定为 Unit
B. 固定为 Bool
C. 各分支最后一个表达式的类型
D. 由条件类型决定
答案与解读:C。代码块的值与类型等于其中最后一个表达式的值与类型。
B. 判断题
题目1:仓颉支持不同类型之间的隐式转换。
答案与解读:错误。仓颉不支持隐式类型转换,必须显式进行。
题目2:&& 左侧为 false 时,右侧不会执行。
答案与解读:正确。&& 支持短路求值。
题目3:is 操作符返回 Option。
答案与解读:错误。is 返回 Bool,as 才返回 Option。
题目4:while 表达式的类型是 Unit。
答案与解读:正确。while、do-while、for-in 的类型都是 Unit,值为 ()。
题目5:关系运算符可以连续使用,如 a < b < c。
答案与解读:错误。关系运算符不支持连续使用,需要用 && 连接。
题目6:命名参数在定义时参数名后加 !。
答案与解读:正确。定义时加 !,调用时必须写参数名。
题目7:数组是值类型。
答案与解读:错误。Array 是引用类型。
题目8:as 转换失败时会抛出异常。
答案与解读:错误。as 返回 Option,失败时值为 Option.None,不会抛异常。
题目9:复合赋值运算符 += 的表达式类型是 Unit。
答案与解读:正确。所有赋值表达式(包括复合赋值)的类型都是 Unit。
题目10:尾随 lambda 是将函数参数写在括号外面。
答案与解读:正确。尾随 lambda 语法糖允许将最后一个函数参数写在括号外。
C. 填空题
题目1:仓颉中幂运算的运算符是 ______。
答案与解读:**。
题目2:a as T 的返回类型是 ______。
答案与解读:Option。
题目3:do-while 循环至少执行 ______ 次。
答案与解读:1。
题目4:函数重载的依据是参数个数不同或 ______ 不同。
答案与解读:参数类型。
题目5:break、continue 表达式的类型是 ______。
答案与解读:Nothing。
题目6:元组解构时,用 ______ 忽略不需要的元素。
答案与解读:_。
题目7:if 代码块的值等于其中 ______ 表达式的值。
答案与解读:最后一个。
题目8:数组使用 ______ 类型表示,T 是元素类型。
答案与解读:Array。
题目9:字符串拼接使用 ______ 运算符。
答案与解读:+。
题目10:… 是左闭右开区间,…= 是 ______ 区间。
答案与解读:闭。
D. 代码改错题
题目1:
main() { // 程序入口
let a: Int64 = 10 // 定义 Int64
let b: Int32 = 20 // 定义 Int32
let c = a + b // 错误:类型不同
println(c) // 输出
} // main 结束
答案与解读:
main() { // 程序入口
let a: Int64 = 10 // 定义 Int64
let b: Int32 = 20 // 定义 Int32
let c = a + Int64(b) // 显式转换 b 为 Int64
println(c) // 输出 30
} // main 结束
仓颉不支持隐式类型转换,需要显式转换。
题目2:
main() { // 程序入口
let x = if (true) { 1 } // 错误:缺少 else 分支
println(x) // 输出
} // main 结束
答案与解读:
main() { // 程序入口
let x = if (true) { 1 } else { 0 } // 补充 else 分支
println(x) // 输出 1
} // main 结束
if 作为表达式使用时,必须有 else 分支,否则无法确定类型。
题目3:
main() { // 程序入口
let s: String = "hello" // 定义字符串
let r = s as Int64 // 错误:String 不能 as 为 Int64
println(r) // 输出
} // main 结束
答案与解读:
main() { // 程序入口
let s: String = "hello" // 定义字符串
// as 只用于类和接口的类型转换,值类型转换使用 T(e)
println(s.size) // 输出 5
} // main 结束
as 只用于类和接口的引用类型转换,不能用于值类型。数值类型之间用 T(e)。
题目4:
main() { // 程序入口
for (i in 1..=5) { // 遍历 1 到 5
if (i == 3) { // 当 i 等于 3
continue // 跳过本次循环
} else { // 否则
break // 终止循环
} // if 结束
println(i) // 输出
} // for 结束
} // main 结束
答案与解读:这段代码逻辑有问题。当 i == 1 时,continue 不成立,进入 else 的 break,循环直接终止,只输出 1。如果希望输出 1、2、4、5(跳过 3),应改为:
main() { // 程序入口
for (i in 1..=5) { // 遍历 1 到 5
if (i == 3) { // 当 i 等于 3
continue // 跳过本次循环
} // if 结束
println(i) // 输出 1、2、4、5
} // for 结束
} // main 结束
题目5:
func add(a: Int64, b: Int64): Int64 { // 版本 1
return a + b // 返回两数之和
} // 函数结束
func add(a: Int64, b: Int64): Int64 { // 版本 2:与版本 1 完全相同
return a + b // 返回两数之和
} // 函数结束
答案与解读:两个函数名相同、参数类型和个数相同,不构成重载,编译错误。函数重载要求参数个数不同或参数类型不同。
E. 输出预测题
题目1:
main() { // 程序入口
let a: Int64 = 7 // a = 7
let b: Int64 = 2 // b = 2
println(a / b) // 整数除法:3
println(a % b) // 取模:1
println(a ** b) // 幂运算:49
} // main 结束
答案与解读:输出:
3
1
49
整数除法截断小数,7 / 2 = 3;7 % 2 = 1;7 ** 2 = 49。
题目2:
main() { // 程序入口
var x = 5 // x = 5
let y = if (x > 3) { x * 2 } else { x + 1 } // if 表达式
println(y) // 输出 10
} // main 结束
答案与解读:输出 10。x = 5 > 3,取 x * 2 = 10。
题目3:
main() { // 程序入口
var sum = 0 // 累加器
var i = 1 // 循环变量
do { // do-while
sum += i // 累加
i += 1 // 自增
} while (i <= 3) // 条件
println(sum) // 输出 6
} // main 结束
答案与解读:输出 6。1 + 2 + 3 = 6。
题目4:
func add(a: Int64, b: Int64): Int64 { // 两个参数
return a + b // 返回两数之和
} // 函数结束
func add(a: Int64, b: Int64, c: Int64): Int64 { // 三个参数
return a + b + c // 返回三数之和
} // 函数结束
main() { // 程序入口
println(add(1, 2)) // 调用两参数版本
println(add(1, 2, 3)) // 调用三参数版本
} // main 结束
答案与解读:输出:
3
6
函数重载,编译器根据参数个数选择对应版本。
题目5:
main() { // 程序入口
let person: (String, Int64, Bool) = ("Bob", 25, true) // 三元组
let (name, age, _) = person // 解构,忽略第三个元素
println("${name} ${age}") // 输出 Bob 25
} // main 结束
答案与解读:输出 Bob 25。使用 _ 忽略布尔值。
F. 编程题
题目1:编写函数 maxOfThree(a: Int64, b: Int64, c: Int64): Int64,返回三个数中的最大值,使用 if 表达式。
func maxOfThree(a: Int64, b: Int64, c: Int64): Int64 { // 定义函数
return if (a >= b && a >= c) { // a 最大
a // 返回 a
} else if (b >= a && b >= c) { // b 最大
b // 返回 b
} else { // c 最大
c // 返回 c
} // if 表达式结束
} // 函数结束
main() { // 程序入口
println(maxOfThree(3, 7, 5)) // 输出 7
println(maxOfThree(10, 2, 8)) // 输出 10
} // main 结束
if 作为表达式直接 return,各分支返回 Int64。
题目2:用 for-in 遍历数组,统计大于 80 的成绩个数。
main() { // 程序入口
let scores: Array<Int64> = [85, 72, 90, 65, 88] // 定义成绩数组
var count = 0 // 计数器
for (score in scores) { // 遍历数组
if (score > 80) { // 大于 80
count += 1 // 计数器加一
} // if 结束
} // for 结束
println("大于80的成绩个数:${count}") // 输出 3
} // main 结束
遍历数组,条件判断后计数。
题目3:使用命名参数和默认值,定义函数 formatDate(year!: Int64, month!: Int64, day!: Int64, sep!: String = “-”): String。
func formatDate(year!: Int64, month!: Int64, day!: Int64, sep!: String = "-"): String { // 定义
return "${year}${sep}${month}${sep}${day}" // 返回格式化日期
} // 函数结束
main() { // 程序入口
println(formatDate(year: 2024, month: 6, day: 21)) // 默认分隔符
println(formatDate(year: 2024, month: 6, day: 21, sep: "/")) // 自定义分隔符
} // main 结束
输出:
2024-6-21
2024/6/21
命名参数可选择性传参,未传的使用默认值。
题目4:用 while 循环和 continue,计算 1 到 100 中所有不能被 3 整除的数之和。
main() { // 程序入口
var sum = 0 // 累加器
var i = 1 // 循环变量
while (i <= 100) { // 循环条件
if (i % 3 == 0) { // 能被 3 整除
i += 1 // 先自增
continue // 跳过累加
} // if 结束
sum += i // 累加
i += 1 // 自增
} // while 结束
println("不能被3整除的数之和:${sum}") // 输出 3367
} // main 结束
注意:continue 之前必须先自增 i,否则会死循环。
题目5:使用元组返回一个数的整数部分和小数部分。
func splitNumber(n: Float64): (Int64, Float64) { // 返回二元组
let intPart = Int64(n) // 整数部分,截断
let fracPart = n - Float64(intPart) // 小数部分
return (intPart, fracPart) // 返回元组
} // 函数结束
main() { // 程序入口
let (i, f) = splitNumber(3.14) // 解构元组
println("整数:${i},小数:${f}") // 输出 整数:3,小数:0.140000
} // main 结束
元组常用于从函数返回多个值。
G. 开放综合题
题目1:用自己的话解释仓颉为什么要求运算符两侧类型相同。
答案与解读:仓颉是静态强类型语言,要求运算符两侧类型相同可以避免隐式转换带来的精度丢失和逻辑错误,让类型转换在代码中显式可见,提高程序的安全性和可读性。
题目2:简述 is 和 as 的区别。
答案与解读:is 用于判断表达式的类型,返回 Bool;as 用于将表达式转换为指定类型,返回 Option,转换失败时返回 None,不会抛出异常。is 用于判断,as 用于转换。
七、知识点总结
· 算术运算符:+、-、*、/、%、**、一元 -。
· 关系运算符:<、<=、>、>=、==、!=,不支持连续使用。
· 逻辑运算符:&&、|| 支持短路求值,! 逻辑非。
· 位运算符:&、|、^、<<、>>、!。
· 赋值表达式类型为 Unit,值为 ()。
· 运算符优先级:幂 > 乘除 > 加减 > 移位 > 关系 > 判等 > 按位 > 逻辑 > 赋值。
· 数值类型转换使用 T(e),不支持隐式转换。
· is 判断类型,返回 Bool;as 转换类型,返回 Option。
· if 是表达式,代码块的值等于最后一个表达式的值。
· 循环类型均为 Unit:while、do-while、for-in。
· do-while 至少执行一次。
· break、continue、return、throw 的类型是 Nothing。
· 函数重载依据参数个数或类型不同。
· 命名参数定义时加 !,调用时必须写参数名。
· 参数默认值可减少函数重载。
· 尾随 lambda 将函数参数写在括号外。
· 元组是固定大小的不可变集合,支持索引访问和解构。
· 数组 Array 是引用类型,元素类型相同。
· 字符串支持 + 拼接、== 比较、${} 插值和切片。
八、易错点
· 仓颉不支持隐式类型转换,不同类型运算前必须显式转换。
· as 只用于类和接口的引用类型转换,值类型转换用 T(e)。
· as 返回 Option,需要用 match 或 ?? 处理。
· 赋值表达式类型是 Unit,不能用于需要值的场合。
· 关系运算符不能连续使用,如 a < b < c 是编译错误。
· do-while 至少执行一次,条件在循环体之后判断。
· continue 前要确保循环变量已更新,否则可能死循环。
· 函数重载不能仅靠返回值类型区分。
· 命名参数调用时必须写参数名。
· if 作为表达式时必须包含 else 分支。
九、本课通关清单
· 能正确使用算术、关系、逻辑、位运算符。
· 能写出复合表达式并判断求值顺序。
· 能进行数值类型的显式转换。
· 能使用 is 和 as 进行类型判断与转换。
· 能使用 if 表达式作为值。
· 能正确选用 while、do-while、for-in。
· 能在循环中使用 break 和 continue。
· 能定义重载函数、命名参数函数、带默认值的函数。
· 能使用元组、数组、字符串的基本操作。
· 能独立完成本课全部习题。
更多推荐




所有评论(0)