目录
一、引言:仓颉编程的崛起
2024年6月21日下午,华为终端BG软件部总裁龚体先生在华为开发者大会主题演讲《鸿蒙原生应用,全新出发!》中向全球开发者介绍了华为自研仓颉编程语言,并发布了HarmonyOS NEXT仓颉语言开发者预览版。这是华为首次公开发布仓颉编程语言。
2019年,仓颉编程语言项目在华为诞生,历经5年研发沉淀,大量研发投入,今日终于和全球开发者见面。仓颉编程语言通过现代语言特性的集成、全方位的编译优化和运行时实现、以及开箱即用的IDE工具链支持,为开发者打造友好开发体验和卓越程序性能。
仓颉编程语言是一款面向全场景智能的新一代编程语言,主打原生智能化、天生全场景、高性能、强安全。
二、编程环境搭建全攻略
工欲善其事,必先利其器。搭建仓颉编程环境,每个环节都至关重要。接下来,我将为大家详细剖析在不同操作系统下搭建环境的具体步骤。
官方下载链接 点击👉 下载中心-仓颉编程语言官网 (cangjie-lang.cn)
对于 Windows 用户
- 首先要在 GitCode Cangjie 社区大显身手。
- 在这里,找到并下载仓颉 VScode 插件以及对应的 SDK,这两个可是编程的得力助手。
- 下载完成后,解压安装仓颉插件
- 配置仓颉运行环境,配置路径务必精准到 “~\Cangjie-0.50.3-windows_x64\cangjie” 路径层级,只有这样才能让环境生效。
- 万事俱备,按下 Ctrl+Shift+P 组合键,打开 VScode 功能搜索框,输入 “Create Cangjie Project”
- 创建好项目后,选择 main.cj 文件,点击右上角 Run Code 按钮,
- 你的第一个仓颉编程语言程序就能成功编译运行,仿佛看到一颗新星在代码宇宙中闪耀。
Linux 用户也有自己的专属攻略
- 先安装 Visual Studio Code
- 接着,同样要去 GitCode 社区下载仓颉语言插件以及 SDK
- 不过要注意,得根据你的 Linux 系统架构,下载相应的.tar.gz 文件,
- 下载好插件后解压,会得到一个.vsix 文件,在 VScode 的扩展页面,通过 “从 VSIX 安装” 选项将其安装妥当。
- 对于 SDK,解压.tar.gz 文件后,把里面的 cangjie 文件夹放在你心仪的位置,然后在 VScode 的扩展设置里,将 cangjie 目录在系统中的位置填写在 “Cangjie SDK Path: CJNative Backend” 下面一栏
- 值得一提的是,Cangjie Linux SDK 很贴心,不需要自己设置环境变量,它自带的 envsetup.sh 脚本会自动搞定一切。
- 最后,按 Ctrl+Shift+P,输入 “Create Cangjie Project” 创建项目,选择合适的项目类型,输入名称,点开 “src” 找到 “main.cj” 源文件,点击右上方三角运行项目,看着终端输出 “hello world”,成就感涌上心头
Mac 用户攻略
- 打开 VSCode,使用快捷键 Shift + Command + P 召唤出命令面板
- 输入 cangjie,选择 “Create Cangjie Project” 选项,接着选择 “Create CJNative Cangjie project” 以及 “Create Executable Output Cangjie project”
- 选好项目路径并输入名称,确认后就能自动生成 main.cj 文件
- 然后用 VSCode 打开项目文件夹,在 Explorer 侧边栏找到 src/main.cj 文件,看看默认代码,熟悉一下它的 “模样”。
- 运行项目前,要在 VSCode 中打开终端,输入 “source /Users/ 用户名 /cangjie/envsetup.sh”(记得把用户名替换为实际用户名)配置环境
- 接着输入 “cjpm run” 运行项目,检查终端输出,要是看到 “hello world”,就说明成功
- 要是想修改代码,打开 src/main.cj 文件,改动后保存,再次运行 “cjpm run”
三、基础语法精析
(一)变量与数据类型
在仓颉编程语言中,变量定义有着独特的规则。
变量由变量名、数据(值)和若干属性构成,定义时需要指定修饰符、变量名和变量类型(若初始值具有明确类型,可省略变量类型标注)。
修饰符犹如变量的 “标签”,常见的有可变性修饰符 let 与 var,其中 let 修饰的变量如同被封印的宝箱,一旦初始化赋值,就不能再更改,而 var 修饰的变量则像个万能口袋,允许多次赋值;可见性修饰符 private 与 public 等,掌控着变量在不同作用域的可见范围,就如同给变量划分了 “私密空间” 和 “公共广场”。
例如:
let a: Int64 = 20
var b = 12
b = 23
println("${a}${b}")
这里,a被定义为不可变的Int64类型变量,初始值为 20,而b是可变变量,先赋值为 12,后又被重新赋值为 23。
数据类型方面,仓颉编程语言内置了丰富多样的类型,以满足不同场景需求。
整数类型分为有符号(signed)和无符号(unsigned),有符号整数类型涵盖 Int8、Int16、Int32、Int64 和 IntNative,能精准表示不同范围的有符号整数值;无符号整数类型包含 UInt8、UInt16、UInt32、UInt64 和 UIntNative,为处理无符号整数提供了多样选择。整数类型字面量支持二进制(0b 或 0B 前缀)、八进制(0o 或 0O 前缀)、十进制(无前缀)、十六进制(0x 或 0X 前缀)4 种进制表示,还可用下划线_充当分隔符,方便识别数值位数,如0b0001_1000。使用时,若字面量值超出对应类型范围,编译器会立刻发出 “警报” 报错。同时,通过添加后缀能明确整数字面量类型,如100i8表示Int8类型的 100。
浮点类型包括 Float16、Float32 和 Float64,分别对应 IEEE 754 中的半精度、单精度和双精度格式,精度依次提升,能处理不同精度要求的带小数数字。布尔类型用 Bool 表示,是逻辑判断的基石,只有真(true)和假(false)两种取值。字符类型(如r'甲')用于表示单个字符,而字符串类型(如"你好,仓颉")则能容纳一串字符,在文本处理中发挥关键作用。
此外,还有数组、元组、区间等复合数据类型。
数组就像一个个整齐排列的收纳盒,能存放同类型元素,通过索引访问其中元素;元组则如同定制的礼品套装,可将不同类型元素有序组合,按位置提取元素;区间类型用于表示一个有固定步长的数值序列,格式为start..end[: step](左闭右开区间)或start...end[: step](左闭右闭区间),例如0..4表示序列0, 1, 2, 3,常用于循环遍历场景,能让代码简洁高效地处理特定范围数据。
(二)表达式与流程控制
if 表达式:这是程序决策的 “指挥官”,用于根据条件的值来决定是否执行相关代码逻辑,有单分支、双分支和嵌套三种形式。单分支 if 表达式形如if (条件) {代码块},条件必须是布尔类型表达式,若条件为 true,就执行括号内代码块,否则直接跳过。
例如:
let score: UInt16 = 85
if (score >= 60) {
println("及格")
}
println("程序继续执行")
这里当score >= 60条件满足时,输出 “及格”,无论如何都会输出 “程序继续执行”。
双分支 if 表达式语法为if (条件) {代码块1} else {代码块2},条件为 true 执行代码块 1,为 false 则执行代码块 2。像判断成绩是否及格并输出相应结果就很适用:
let score: UInt16 = 45
if (score >= 60) {
println("及格")
} else {
println("不及格")
}
println("程序结束")
嵌套 if 表达式在需要精细判断多个条件时大显身手,格式为if (条件1) {代码块1} else if (条件2) {代码块2}... else {代码块n},程序会依次测试条件,找到第一个为 true 的条件并执行对应代码块,若都为 false 则执行 else 分支代码块。比如根据不同成绩范围给出等级:
let score: UInt16 = 75
if (score < 60) {
println("不及格")
} else if (score < 80) {
println("良好")
} else {
println("优秀")
}
for 表达式:主要用于遍历序列,语法为for (循环变量 in 序列) {循环体},循环变量在每次循环中接收序列的下一个元素并执行循环体,直到遍历完序列。若序列为空,循环体则不会启动。例如遍历数组:
let numbers = [1, 2, 3, 4, 5]
for (num in numbers) {
println(num)
}
这段代码会依次输出数组中的每个数字。而且在 for 循环中,还能对循环变量进行解构,如for ((index, value) in enumerate(numbers)),可同时获取索引和值,让代码更灵活。
while 表达式:while 表达式是循环结构中的 “耐力选手”
基本形式为while (条件) {循环体},条件为布尔类型,每次循环前先判断条件,若为 true 则执行循环体,执行完自动返回重新判断条件,直到条件为 false 才跳出循环。
若条件一开始就为 false,循环体一次都不会执行。
例如用 while 计算 1 到 10 的阶乘:
main() {
var number = 0
var factorial = 1
while (number < 10) {
number++
factorial *= number
println("$(number)! = $(factorial)")
}
}
在循环中,还可配合break表达式提前终止循环,或用continue表达式跳过当前循环剩余部分直接进入下一次迭代,让循环控制更加得心应手,以应对复杂的逻辑需求。
四、函数:代码复用的利器
(一)普通函数
在仓颉编程语言里,函数是组织代码、实现复用的得力工具。
定义函数时,使用关键字func开启函数定义之旅,其后依次排列着函数名、参数列表、可选的返回值类型以及函数体。
就像func add(a: Int64, b: Int64): Int64 { return a + b }这般,定义了一个名为add的函数,它有两个Int64类型的参数a和b,返回值类型为Int64,函数体内执行两数相加并返回结果。
参数列表可是函数的 “入口”,参数分为非命名参数(如p: T形式,p为参数名,T为类型)和命名参数(p!: T形式,还可为其设默认值,如p!: T = e,e为表达式)。
要注意:
非命名参数得在命名参数前定义。
函数体呢,包含着函数被调用时执行的 “任务”,由变量定义、表达式等组成,甚至还能嵌套新函数。
函数的返回值类型可选,若显式定义,函数体和return表达式的类型得是返回值类型的子类型;
若省略,编译器会依据函数体和return表达式推导,不过推导也有失败的时候,编译器就会报错啦。
调用函数时,形式为函数名(参数列表),非命名参数直接传值,命名参数用参数名: 值形式传值,若命名参数有默认值,不传实参就用默认值,传了新值就用新值替代默认值。
例如:
func add(a: Int64, b!: Int64 = 2) {
return a + b
}
main() {
let x = 1
let r = add(x)
println("The sum of x and b is ${r}")
}
这里add函数的b参数有默认值 2,调用add(x)时,b使用默认值,结果为x + 2。若改成let r = add(x, b: 20),b就取值 20,结果为x + 20。
(二)Lambda 表达式
Lambda 表达式是匿名函数,能在需要函数的地方 “闪现”,无需提前定义函数名。
语法为{ p1: T1,..., pn: Tn => expressions | declarations },=>前是参数列表(可为空),后是表达式或声明序列。比如let f1 = { a: Int64, b: Int64 => a + b },定义了一个求两数和的 Lambda 表达式。
它的参数类型标注可省略,编译器会根据上下文推断,像赋值给变量或作为函数实参时,会依据变量类型或函数形参类型来推断。
而且它不支持声明返回类型,返回类型由上下文 “定夺”,赋值给变量就按变量类型,作为函数参数就依形参类型,作为返回值就跟所在函数返回类型走。
调用 Lambda 表达式很灵活,既可以立即调用,如let r1 = { a: Int64, b: Int64 => a + b }(1, 2),直接传入参数求值;
也能赋值给变量,之后用变量名调用,像func f() { var g = { x: Int64 => println("x = ${x}") }; g(2) },先把 Lambda 表达式赋给g,再用g(2)触发执行,输出x = 2。
这在一些临时需要简单函数逻辑的场景,如数据处理、回调函数场景中,能让代码简洁又高效,无需繁琐的函数定义步骤,随时定义、随时使用,大大提升编程的灵活性与效率。
更多精彩内容,请看下篇讲解:👇