引言
仓颉语言(Cangjie Language)作为一门新兴的编程语言,具有强类型、函数式编程特性,并且支持现代化的编程范式。本文将深入探讨仓颉语言的基础编程概念,包括程序结构、标识符、函数定义和表达式系统。每个概念都将配有详细的代码示例,确保语法正确且可编译通过。
1. 程序结构(Program Structure)
1.1 基本概念
仓颉程序通常保存在扩展名为 .cj
的文本文件中。在顶层作用域中,可以定义全局变量、全局函数和自定义类型(如 struct
、class
、enum
和 interface
)。程序入口是 main
函数,它可以有 Array<String>
类型的参数(用于接收命令行参数),也可以没有参数。
1.2 程序入口示例
cangjie
package cangjie_blog
// 全局变量,最好有类型标注
let globalVariable: String = "Hello, Cangjie!"
// 全局函数
func globalFunction() {
println("This is a global function")
}
// 自定义类型
struct MyStruct {
MyStruct(let data: Int64){}
}
class MyClass {
// 主构造函数,如果参数前面有修饰符,会自动生成同名的成员变量
MyClass(let name: String){}
}
enum MyEnum {
// 枚举值,用 | 分隔。
// 第一个枚举值前面的|有可无
| Case1
| Case2
// 最后一个枚举值后面还可以有可选的 ...
| ...
}
interface MyInterface {
// 接口方法,不需要实现,只需要声明。必须明确参数和返回值类型
func method(): Unit
}
// 程序入口 - 不需要 func 修饰符
main() {
println(globalVariable)
globalFunction()
let structInstance = MyStruct(42)
let classInstance = MyClass("Cangjie")
println("Struct data: ${structInstance.data}")
println("Class name: ${classInstance.name}")
}
1.3 带命令行参数的程序入口
cangjie
// args.cj
main(args: Array<String>) {
println("程序名称: ${args[0]}")
for (i in 1..args.size) {
println("参数 ${i}: ${args[i]}")
}
}
2. 标识符(Identifier)
2.1 标识符规则
仓颉语言的标识符分为普通标识符和原始标识符两类:
- 普通标识符 :不能和仓颉关键字相同,必须以
XID_Start
字符开头,后接任意长度的XID_Continue
字符,或者以一个_
开头,后接至少一个XID_Continue
字符。 - 原始标识符:在普通标识符或仓颉关键字的首尾加上一对反引号,主要用于将仓颉关键字作为标识符。
2.2 合法标识符示例
cangjie
// valid_identifiers.cj
main() {
// 普通标识符示例
let abc = "普通英文标识符"
let _abc = "下划线开头的标识符"
let abc_ = "下划线结尾的标识符"
let a1b2c3 = "包含数字的标识符"
let a_b_c = "包含下划线的标识符"
let 仓颉 = "中文字符标识符"
let __こんにちは = "日文字符标识符"
// 原始标识符示例
let `abc1` = "原始标识符"
let `if` = "关键字作为标识符"
let `while` = "另一个关键字作为标识符"
println(abc)
println(_abc)
println(abc_)
println(a1b2c3)
println(a_b_c)
println(仓颉)
println(__こんにちは)
// 这里使用反引号引用abc这个变量。和直接使用abc是一样的
println(`abc`)
println(`abc1`)
println(`if`)
println(`while`)
}
2.3 非法标识符示例
cangjie
// invalid_identifiers.cj
main() {
// 以下代码会编译错误,仅用于演示非法标识符
// let ab&c = "包含非法字符" // 错误:& 不是 XID_Continue 字符
// let 3abc = "数字开头" // 错误:数字不能作为起始字符
// let _ = "单独的下划线" // 不会报错,但是也无法引用这个变量,相当于丢弃这个值。这里实际上是模式匹配语法
// let while = "关键字" // 错误:普通标识符不能使用关键字
println("这些标识符都是非法的")
}
3. 函数(Function)
3.1 函数定义语法
仓颉语言使用关键字 func
来定义函数,语法格式为:
text
func 函数名(参数列表): 返回值类型 {
函数体
}
3.2 基本函数示例
cangjie
// functions.cj
// 无参数无返回值的函数
func greet() {
println("Hello, Cangjie!")
}
// 有参数无返回值的函数
func greetPerson(name: String) {
println("Hello, ${name}!")
}
// 有参数有返回值的函数
func add(a: Int64, b: Int64): Int64 {
return a + b
}
// 有多个返回值的函数(使用元组)
func divideAndRemainder(a: Int64, b: Int64): (Int64, Int64) {
return (a / b, a % b)
}
// 递归函数示例
func factorial(n: Int64): Int64 {
if (n <= 1) {
return 1
} else {
return n * factorial(n - 1)
}
}
main() {
greet()
greetPerson("张三")
let sum = add(10, 20)
println("10 + 20 = ${sum}")
let (quotient, remainder) = divideAndRemainder(17, 5)
println("17 ÷ 5 = ${quotient} 余 ${remainder}")
let fact = factorial(5)
println("5! = ${fact}")
}
3.3 函数重载示例
cangjie
// function_overloading.cj
// 函数重载 - 不同参数类型
func add(a: Int64, b: Int64): Int64 {
return a + b
}
func add(a: Float64, b: Float64): Float64 {
return a + b
}
func add(a: String, b: String): String {
return a + b
}
// 函数重载 - 不同参数数量
func multiply(a: Int64, b: Int64): Int64 {
return a * b
}
func multiply(a: Int64, b: Int64, c: Int64): Int64 {
return a * b * c
}
main() {
println("整数相加: ${add(5, 3)}")
println("浮点数相加: ${add(5.5, 3.2)}")
println("字符串连接: ${add("Hello", " Cangjie")}")
println("两个数相乘: ${multiply(4, 5)}")
println("三个数相乘: ${multiply(2, 3, 4)}")
}
4. 变量(Variable)
4.1 变量定义
仓颉语言中的变量由变量名、数据类型、初始值和修饰符构成。主要修饰符包括:
let
:不可变变量var
:可变变量const
:常量private
/public
:可见性修饰符static
:静态性修饰符
4.2 基本变量示例
cangjie
// variables.cj
// 全局变量
let globalConstant: String = "全局常量"
var globalVariable: String = "全局变量"
main() {
// 局部变量
let localConstant: Int64 = 42
var localVariable: String = "局部变量"
// 类型推断
let inferredInt = 100
let inferredString = "类型推断"
let inferredFloat = 3.14
// const 变量
const PI = 3.14159265359
const GRAVITY = 9.8
println("全局常量: ${globalConstant}")
println("全局变量: ${globalVariable}")
println("局部常量: ${localConstant}")
println("局部变量: ${localVariable}")
println("推断整数: ${inferredInt}")
println("推断字符串: ${inferredString}")
println("推断浮点数: ${inferredFloat}")
println("圆周率: ${PI}")
println("重力加速度: ${GRAVITY}")
// 修改变量值
globalVariable = "修改后的全局变量"
localVariable = "修改后的局部变量"
println("修改后 - 全局变量: ${globalVariable}")
println("修改后 - 局部变量: ${localVariable}")
}
4.3 延迟初始化示例
cangjie
// delayed_initialization.cj
main() {
// 延迟初始化 - 先声明后赋值
let message: String
let number: Int64
// 在引用前必须初始化
message = "Hello, Cangjie!"
number = 42
println("消息: ${message}")
println("数字: ${number}")
// 条件初始化
let condition = true
let conditionalValue: String
if (condition) {
conditionalValue = "条件为真"
} else {
conditionalValue = "条件为假"
}
println("条件值: ${conditionalValue}")
}
4.4 值类型和引用类型示例
cangjie
// value_reference_types.cj
// 值类型 - struct
struct Point {
Point(let x: Int64, let y: Int64) {}
}
// 引用类型 - class
class Person {
var name: String
var age: Int64
public init(name: String, age: Int64) {
this.name = name
this.age = age
}
}
main() {
// 值类型示例
let point1 = Point(10, 20)
let point2 = point1 // 创建副本
println("Point1: (${point1.x}, ${point1.y})")
println("Point2: (${point2.x}, ${point2.y})")
// 引用类型示例
let person1 = Person("张三", 25)
let person2 = person1 // 创建引用
println("Person1: ${person1.name}, ${person1.age}")
println("Person2: ${person2.name}, ${person2.age}")
// 修改引用类型会影响所有引用
person2.age = 30
println("修改后 - Person1: ${person1.name}, ${person1.age}")
println("修改后 - Person2: ${person2.name}, ${person2.age}")
}
5. 表达式(Expression)
5.1 代码块
仓颉中没有代码块,但是可以使用闭包代替
cangjie
// code_blocks.cj
main() {
let blockValue = {
=>
let a = 10
let b = 20
a + b
}
println("执行闭包,返回值: ${blockValue()}")
}
5.2 if 表达式
5.2.1 基本 if 表达式
cangjie
// if_expressions.cj
main() {
let number = 15
// 基本 if-else
if (number > 10) {
println("数字大于10")
} else {
println("数字小于等于10")
}
// 多级 if-else
if (number > 20) {
println("数字大于20")
} else if (number > 10) {
println("数字大于10但小于等于20")
} else {
println("数字小于等于10")
}
// if 表达式作为值
let result = if (number % 2 == 0) {
"偶数"
} else {
"奇数"
}
println("${number} 是 ${result}")
// 条件表达式
let score = 85
let grade = if (score >= 90) {
"A"
} else if (score >= 80) {
"B"
} else if (score >= 70) {
"C"
} else if (score >= 60) {
"D"
} else {
"F"
}
println("分数 ${score} 对应等级: ${grade}")
}
5.2.2 涉及 let pattern 的 if 表达式
cangjie
// let_pattern_if.cj
main() {
// Option 类型示例
let someValue = Some(42)
let noneValue: Option<Int64> = None
// 使用 let pattern 进行模式匹配
if (let Some(value) <- someValue) {
println("someValue 包含值: ${value}")
} else {
println("someValue 是 None")
}
if (let Some(value) <- noneValue) {
println("noneValue 包含值: ${value}")
} else {
println("noneValue 是 None")
}
// 组合条件
let anotherSome = Some(100)
if (let Some(x) <- someValue && let Some(y) <- anotherSome) {
println("两个值都存在: ${x} 和 ${y}")
}
// 逻辑或
if (let Some(_) <- someValue || let Some(_) <- noneValue) {
println("至少有一个是 Some")
} else {
println("都是 None")
}
}
5.3 循环表达式
5.3.1 while 表达式
cangjie
// while_expressions.cj
main() {
// 基本 while 循环
var counter = 0
while (counter < 5) {
println("计数器: ${counter}")
counter++
}
// 计算阶乘
var n = 5
var factorial = 1
while (n > 0) {
factorial *= n
n--
}
println("5! = ${factorial}")
// 查找数组中的元素
let numbers = [10, 20, 30, 40, 50]
var index = 0
var found = false
while (index < numbers.size && !found) {
if (numbers[index] == 30) {
println("找到30,索引为: ${index}")
found = true
}
index++
}
if (!found) {
println("未找到30")
}
}
5.3.2 do-while 表达式
cangjie
// do_while_expressions.cj
main() {
// 基本 do-while 循环
var counter = 0
do {
println("do-while 计数器: ${counter}")
counter++
} while (counter < 3)
// 至少执行一次的循环
var number = 0
do {
println("当前数字: ${number}")
number++
} while (number <= 5)
// 密码验证示例
var attempts = 0
let correctPassword = "cangjie123"
var inputPassword = "wrong"
do {
attempts++
println("尝试次数: ${attempts}")
// 模拟密码验证
if (attempts >= 3) {
inputPassword = correctPassword
}
} while (inputPassword != correctPassword && attempts < 5)
if (inputPassword == correctPassword) {
println("密码验证成功!")
} else {
println("密码验证失败!")
}
}
5.3.3 for-in 表达式
cangjie
// for_in_expressions.cj
main() {
// 遍历数组
let fruits = ["苹果", "香蕉", "橙子", "葡萄"]
println("水果列表:")
for (fruit in fruits) {
println("- ${fruit}")
}
// 遍历区间
println("1到5的数字:")
for (i in 1..=5) {
println("数字: ${i}")
}
// 遍历区间(不包含结束值)
println("0到4的数字:")
for (i in 0..5) {
println("数字: ${i}")
}
// 遍历元组数组
let coordinates = [(1, 2), (3, 4), (5, 6)]
println("坐标点:")
for ((x, y) in coordinates) {
println("(${x}, ${y})")
}
// 使用通配符
var sum = 0
for (_ in 0..5) {
sum += 10
}
println("累加结果: ${sum}")
// 使用 where 条件
println("1到10中的偶数:")
for (i in 1..=10 where i % 2 == 0) {
println("偶数: ${i}")
}
// 嵌套循环
println("乘法表 (1-3):")
for (i in 1..=3) {
for (j in 1..=3) {
print("${i * j} ")
}
println()
}
}
5.4 控制转移表达式
5.4.1 break 表达式
cangjie
// break_expressions.cj
main() {
// 在 for 循环中使用 break
let numbers = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
println("查找第一个偶数:")
for (number in numbers) {
if (number % 2 == 0) {
println("找到第一个偶数: ${number}")
break
}
}
// 在 while 循环中使用 break
var counter = 0
while (true) {
counter++
if (counter > 10) {
break
}
if (counter % 2 == 0) {
continue
}
println("奇数: ${counter}")
}
// 嵌套循环中的 break
println("嵌套循环示例:")
for (i in 1..=3) {
for (j in 1..=3) {
if (i == 2 && j == 2) {
println("在 (2,2) 处跳出内层循环")
break
}
println("(${i}, ${j})")
}
}
}
5.4.2 continue 表达式
cangjie
// continue_expressions.cj
main() {
// 在 for 循环中使用 continue
println("打印1到10中的奇数:")
for (i in 1..=10) {
if (i % 2 == 0) {
continue // 跳过偶数
}
println("奇数: ${i}")
}
// 在 while 循环中使用 continue
var number = 0
println("打印小于10的3的倍数:")
while (number < 10) {
number++
if (number % 3 != 0) {
continue // 跳过不是3的倍数的数
}
println("3的倍数: ${number}")
}
// 跳过特定值
let scores = [85, 92, 78, 95, 88, 100, 76, 89]
println("打印90分以上的成绩:")
for (score in scores) {
if (score < 90) {
continue // 跳过90分以下的成绩
}
println("优秀成绩: ${score}")
}
// 处理数组中的有效数据
let data = [Some(1), Option<Int>.None, Some(3), Option<Int>.None, Some(5)]
println("处理有效数据:")
for (item in data) {
if (let None <- item) {
continue // 跳过 None 值
}
if (let Some(value) <- item) {
println("有效数据: ${value}")
}
}
}
6. 作用域(Scope)
6.1 作用域规则
仓颉语言中的作用域由大括号 {}
定义,遵循以下规则:
- 当前作用域中定义的程序元素在当前作用域和其内层作用域中有效
- 内层作用域中定义的程序元素在外层作用域中无效
- 内层作用域可以使用外层作用域中的名字重新定义绑定关系(变量遮蔽)
6.2 作用域示例
cangjie
// scope_examples.cj
// 全局作用域
let globalVar: String = "全局变量"
func outerFunction() {
let outerVar = "外层函数变量"
println("外层函数: ${globalVar}")
println("外层函数: ${outerVar}")
func innerFunction() {
let innerVar = "内层函数变量"
println("内层函数: ${globalVar}")
println("内层函数: ${outerVar}")
println("内层函数: ${innerVar}")
// 变量遮蔽
let outerVar = "遮蔽的外层变量"
println("内层函数(遮蔽后): ${outerVar}")
}
innerFunction()
let globalVar: String = "遮蔽的全局变量"
println("外层函数(遮蔽后): ${globalVar}")
}
main() {
println("主函数: ${globalVar}")
// 局部作用域
do {
let localVar = "局部作用域变量"
println("局部作用域: ${localVar}")
println("局部作用域: ${globalVar}")
} while (false)
// 条件作用域
if (true) {
let conditionalVar = "条件作用域变量"
println("条件作用域: ${conditionalVar}")
}
// 循环作用域
for (i in 1..=3) {
let loopVar = "循环变量 ${i}"
println("循环作用域: ${loopVar}")
}
outerFunction()
}
7. 综合示例
7.1 简单计算器
cangjie
// calculator.cj
func add(a: Float64, b: Float64): Float64 {
return a + b
}
func subtract(a: Float64, b: Float64): Float64 {
return a - b
}
func multiply(a: Float64, b: Float64): Float64 {
return a * b
}
func divide(a: Float64, b: Float64): Option<Float64> {
if (b == 0.0) {
return None
} else {
return Some(a / b)
}
}
func calculate(operation: String, a: Float64, b: Float64): Option<Float64> {
if (operation == "+") {
return Some(add(a, b))
} else if (operation == "-") {
return Some(subtract(a, b))
} else if (operation == "*") {
return Some(multiply(a, b))
} else if (operation == "/") {
return divide(a, b)
} else {
return None
}
}
main() {
let operations = ["+", "-", "*", "/"]
let numbers = [(10.0, 5.0), (15.0, 3.0), (8.0, 0.0)]
println("简单计算器演示:")
println("==================")
for ((a, b) in numbers) {
println("计算 ${a} 和 ${b}:")
for (op in operations) {
if (let Some(result) <- calculate(op, a, b)) {
println(" ${a} ${op} ${b} = ${result}")
} else {
println(" ${a} ${op} ${b} = 错误(除零或无效操作)")
}
}
println()
}
}
7.2 学生成绩管理系统
cangjie
// student_grade_system.cj
import std.collection.ArrayList
struct Student {
let name: String
let id: String
var grades: ArrayList<Float64>
public init(name: String, id: String) {
this.name = name
this.id = id
this.grades = ArrayList<Float64>()
}
// 如果是public修饰的函数,必须显示标注返回值类型
public func addGrade(grade: Float64): Unit {
if (grade >= 0.0 && grade <= 100.0) {
this.grades.add(grade)
} else {
println("无效成绩: ${grade}")
}
}
public func getAverage(): Option<Float64> {
if (this.grades.size == 0) {
return None
}
var sum = 0.0
for (grade in this.grades) {
sum += grade
}
return Some(sum / Float64(this.grades.size))
}
public func getGradeLevel(): String {
if (let Some(average) <- this.getAverage()) {
if (average >= 90.0) {
return "A"
} else if (average >= 80.0) {
return "B"
} else if (average >= 70.0) {
return "C"
} else if (average >= 60.0) {
return "D"
} else {
return "F"
}
} else {
return "无成绩"
}
}
}
main() {
// 创建学生
let student1 = Student("张三", "2024001")
let student2 = Student("李四", "2024002")
let student3 = Student("王五", "2024003")
// 添加成绩
student1.addGrade(85.0)
student1.addGrade(92.0)
student1.addGrade(78.0)
student2.addGrade(95.0)
student2.addGrade(88.0)
student2.addGrade(91.0)
student3.addGrade(72.0)
student3.addGrade(68.0)
student3.addGrade(75.0)
// 显示学生信息
let students = [student1, student2, student3]
println("学生成绩管理系统")
println("==================")
for (student in students) {
println("学生: ${student.name} (ID: ${student.id})")
if (let Some(average) <- student.getAverage()) {
println(" 平均分: ${average}")
println(" 等级: ${student.getGradeLevel()}")
} else {
println(" 无成绩记录")
}
println(" 成绩列表:")
for (i in 0..student.grades.size) {
println(" 第${i + 1}次: ${student.grades[i]}")
}
println()
}
// 统计信息
var totalStudents = 0
var passedStudents = 0
for (student in students) {
totalStudents++
if (let Some(average) <- student.getAverage()) {
if (average >= 60.0) {
passedStudents++
}
}
}
println("统计信息:")
println(" 总学生数: ${totalStudents}")
println(" 及格学生数: ${passedStudents}")
println(" 及格率: ${Float64(passedStudents) / Float64(totalStudents) * 100.0}%")
}