【华为鸿蒙开发技术】仓颉编程语言详解之多范式编程与内存安全探究
仓颉编程语言是一种面向全场景应用开发的通用编程语言,旨在兼顾开发效率和运行性能,并提供良好的编程体验。它的主要特点如下:
语法简明高效
仓颉编程语言提供了一系列简明高效的语法,减少冗余书写,提升开发效率。例如插值字符串、主构造函数、Flow表达式、match、if-let、while-let和重导出等语法,让开发者可以用较少的编码表达相关逻辑。
多范式编程
仓颉编程语言支持函数式、命令式和面向对象等多范式编程,融合了以下特性:
- 函数式语言:高阶函数、代数数据类型、模式匹配、泛型等。
- 面向对象语言:封装、接口、继承、子类型多态等支持模块化开发的特性。
- 命令式语言:值类型、全局函数等简洁高效的特性。
开发者可以根据开发偏好或应用场景,选择不同的编程范式。
类型安全
仓颉编程语言是静态强类型语言,通过编译时类型检查尽早识别程序错误,降低运行时风险,也便于代码维护。同时,仓颉编译器提供了强大的类型推断能力,减少了类型标注工作,提高开发效率。
内存安全
仓颉编程语言支持自动内存管理,并在运行时进行数组下标越界检查、溢出检查等,确保运行时内存安全。
高效并发
仓颉编程语言提供了用户态轻量化线程(原生协程)以及简单易用的并发编程机制,保证并发场景的高效开发和运行。
兼容语言生态
仓颉编程语言支持与C等主流编程语言的互操作,并采用便捷的声明式编程范式,实现对其他语言库的高效复用和生态兼容。
领域易扩展
仓颉编程语言提供了基于词法宏的元编程能力,支持在编译时变换代码。此外,还提供了尾随lambda、属性、操作符重载、部分关键字可省略等特性,开发者可以深度定制程序的语法和语义,有利于构建内嵌式领域专用语言(EDSL)。
助力UI开发
基于仓颉编程语言的元编程和尾随lambda等特性,可以搭建声明式UI开发框架,提升UI开发效率和体验。
内置库功能丰富
仓颉编程语言提供了功能丰富的内置库,涵盖数据结构、常用算法、数学计算、正则匹配、系统交互、文件操作、网络通信、数据库访问、日志打印、解压缩、编解码、加解密和序列化等功能。
元组类型
元组(Tuple)可以将多个不同的类型组合在一起,成为一个新的类型。元组类型使用 (T1, T2, ..., TN)
表示,其中 T1
到 TN
可以是任意类型。元组的长度是固定的,并且元组类型是不可变的。元组类型的字面量使用 (e1, e2, ..., eN)
表示。
访问元组元素
元组支持通过 t[index]
的方式访问某个具体位置的元素。例如:
main() {
var pi = (3.14, "PI")
println(pi[0])
println(pi[1])
}
输出结果为:
3.140000
PI
解构赋值
元组支持解构赋值:
var a: Int64
var b: String
var c: Unit
var f = { => ((1, "abc"), ())}
((a, b), c) = f() // a is 1, b is "abc", c is '()'
类型参数
元组类型可以标记显式的类型参数名:
func getFruitPrice (): (name: String, price: Int64) {
return ("banana", 10)
}
数组类型
仓颉编程语言使用 Array<T>
来表示数组类型,其中 T
表示数组的元素类型。数组类型是单一元素类型、有序序列的数据。
初始化数组
使用字面量初始化数组:
let a: Array<String> = []
let b = [1, 2, 3, 3, 2, 1]
使用构造函数初始化数组:
let a = Array<Int64>()
let b = Array<Int64>(a)
let c = Array<Int64>(3, item: 0)
let d = Array<Int64>(3, {i => i + 1})
访问数组成员
通过for-in循环遍历数组:
main() {
let arr = [0, 1, 2]
for (i in arr) {
println("The element is ${i}")
}
}
修改数组元素
数组允许修改其元素:
main() {
let arr = [0, 1, 2, 3, 4, 5]
arr[0] = 3
println("The first element is ${arr[0]}")
}
值类型数组
仓颉编程语言引入了值类型数组 VArray<T, $N>
,其中 T
表示元素类型,$N
表示长度。初始化例子:
var a: VArray<Int64, $3> = [1, 2, 3]
构造函数例子:
let b = VArray<Int64, $5>({ i => i})
let c = VArray<Int64, $5>(item: 0)
区间类型
区间类型用于表示拥有固定步长的序列,使用 Range<T>
表示。其中,T
必须支持关系操作符,并且可以与 Int64
类型的值做加法。每个区间类型的实例包含 start
、end
和 step
三个值。
以上是仓颉编程语言的一些特点和常见数据类型的使用示例。这种语言提供了简明高效的语法、多范式编程支持、类型和内存安全、高效并发、语言生态兼容、领域易扩展、UI开发支持以及丰富的内置库功能,旨在提升开发者的开发效率和编程体验。
区间类型的创建
我们可以使用以下几种方法创建一个区间类型的实例:
-
使用区间构造函数:
let range1 = Range<Int64>(start: 0, end: 10, step: 1) let range2 = Range<Float64>(start: 0.0, end: 1.0, step: 0.1)
-
使用区间字面量:
区间类型支持使用 … 表示从起始值到终止值的区间:let range1 = 0..10 // 表示从 0 到 10 的整数区间,步长为 1 let range2 = 0.0..1.0 // 表示从 0.0 到 1.0 的浮点数区间,步长为 1
-
使用 … 运算符创建具有指定步长的区间:
let range1 = 0..10..2 // 表示从 0 到 10 的整数区间,步长为 2 let range2 = 0.0..1.0..0.1 // 表示从 0.0 到 1.0 的浮点数区间,步长为 0.1
访问区间元素
区间类型提供了以下几种方式访问其元素:
-
使用 for-in 循环遍历区间中的所有元素:
main() { let range = 0..5 for (i in range) { println(i) } }
输出结果:
0 1 2 3 4 5
-
使用下标访问区间中的指定位置的元素:
main() { let range = 0..5 let element = range[2] println(element) }
输出结果:
2
区间类型的成员函数
区间类型提供了一些成员函数,可以方便地进行区间操作:
-
contains
函数:判断一个值是否在区间内:main() { let range = 0..5 let isContained = range.contains(3) println(isContained) // 输出 true }
-
size
属性:获取区间的元素个数:main() { let range = 0..5 let size = range.size println(size) // 输出 6 }
字典类型
字典类型的定义
字典类型(Dictionary)是键值对的集合,用于存储具有唯一键的元素。字典类型使用 Dictionary<K, V>
表示,其中 K
表示键的类型,V
表示值的类型。
let dict: Dictionary<String, Int64> = ...
字典类型的字面量
字典类型的字面量使用 {k1: v1, k2: v2, ..., kn: vn}
表示,其中 k1
到 kn
是键的表达式,v1
到 vn
是对应的值的表达式。
let dict: Dictionary<String, Int64> = {"one": 1, "two": 2, "three": 3}
访问字典元素
-
使用键访问字典中的值:
main() { let dict = {"one": 1, "two": 2, "three": 3} let value = dict["two"] println(value) // 输出 2 }
-
使用 for-in 循环遍历字典中的所有键值对:
main() { let dict = {"one": 1, "two": 2, "three": 3} for (key, value in dict) { println("${key}: ${value}") } }
输出结果:
one: 1 two: 2 three: 3
修改字典元素
字典类型允许我们对其中的元素进行修改,添加新的键值对,或者删除已有的键值对。
-
修改字典中的值:
main() { let dict = {"one": 1, "two": 2, "three": 3} dict["two"] = 22 println(dict["two"]) // 输出 22 }
-
添加新的键值对:
main() { let dict = {"one": 1, "two": 2, "three": 3} dict["four"] = 4 println(dict["four"]) // 输出 4 }
-
删除已有的键值对:
main() { let dict = {"one": 1, "two": 2, "three": 3} dict.remove("two") println(dict.contains("two")) // 输出 false }
字典类型的成员函数
字典类型提供了一些成员函数,可以方便地进行字典操作:
-
contains
函数:判断一个键是否在字典内:main() { let dict = {"one": 1, "two": 2, "three": 3} let isContained = dict.contains("two") println(isContained) // 输出 true }
-
size
属性:获取字典的元素个数:main() { let dict = {"one": 1, "two": 2, "three": 3} let size = dict.size println(size) // 输出 3 }
以上是关于仓颉编程语言的详细介绍和一些关键特性示例。通过这些特性,仓颉编程语言能够提供高效、灵活、安全的开发体验,适用于多种编程范式和应用场景。
函数与方法
在仓颉编程语言中,函数和方法是重要的组成部分,用于封装代码逻辑和实现模块化编程。函数是独立的代码块,而方法是绑定到某个对象或类型上的函数。
函数定义与调用
定义函数
使用 fn
关键字定义一个函数,函数可以有参数和返回值。
fn add(a: Int64, b: Int64): Int64 {
return a + b
}
调用函数
直接使用函数名加上参数列表来调用函数。
main() {
let result = add(3, 5)
println(result) // 输出 8
}
可选参数与默认值
函数参数可以有默认值,调用时可以省略这些参数。
fn greet(name: String, message: String = "Hello") {
println("${message}, ${name}")
}
main() {
greet("Alice") // 输出 Hello, Alice
greet("Bob", "Hi") // 输出 Hi, Bob
}
可变参数
使用 ...
表示可变参数,允许传递多个参数。
fn sum(numbers: Int64...): Int64 {
let total = 0
for (num in numbers) {
total += num
}
return total
}
main() {
let result = sum(1, 2, 3, 4, 5)
println(result) // 输出 15
}
方法定义与调用
定义方法
方法定义在类或结构体内部,第一参数是 self
,表示方法所属的实例。
class Calculator {
fn add(self, a: Int64, b: Int64): Int64 {
return a + b
}
}
调用方法
使用对象名加上方法名来调用方法。
main() {
let calc = Calculator()
let result = calc.add(3, 5)
println(result) // 输出 8
}
闭包与匿名函数
定义闭包
使用 fn
关键字可以定义闭包,闭包可以捕获其上下文中的变量。
main() {
let a = 10
let add = fn (b: Int64): Int64 {
return a + b
}
let result = add(5)
println(result) // 输出 15
}
高阶函数
高阶函数是可以接受其他函数作为参数,或者返回一个函数的函数。
fn apply_twice(f: fn (Int64): Int64, x: Int64): Int64 {
return f(f(x))
}
main() {
let double = fn (n: Int64): Int64 {
return n * 2
}
let result = apply_twice(double, 3)
println(result) // 输出 12
}
泛型函数
使用泛型函数可以定义适用于多种类型的函数。
fn swap<T>(a: T, b: T): (T, T) {
return (b, a)
}
main() {
let (x, y) = swap(1, 2)
println("${x}, ${y}") // 输出 2, 1
let (s1, s2) = swap("hello", "world")
println("${s1}, ${s2}") // 输出 world, hello
}
模块与包管理
仓颉编程语言支持模块和包管理,以实现代码的组织和复用。
模块
模块是包含在文件中的一组相关功能,可以通过 import
关键字导入。
定义模块
在文件中定义模块内容。
// math.chinese
fn add(a: Int64, b: Int64): Int64 {
return a + b
}
fn subtract(a: Int64, b: Int64): Int64 {
return a - b
}
导入模块
使用 import
关键字导入模块。
import math
main() {
let result1 = math.add(3, 5)
println(result1) // 输出 8
let result2 = math.subtract(10, 4)
println(result2) // 输出 6
}
包管理
仓颉编程语言的包管理工具用于下载和管理第三方库。可以通过 cangjie
命令行工具来管理包。
安装包
使用 cangjie install
命令安装包。
cangjie install <package-name>
卸载包
使用 cangjie uninstall
命令卸载包。
cangjie uninstall <package-name>
列出已安装包
使用 cangjie list
命令列出所有已安装的包。
cangjie list
错误处理
仓颉编程语言提供了异常处理机制来处理运行时错误。
异常定义与抛出
使用 raise
关键字抛出异常。
fn divide(a: Int64, b: Int64): Int64 {
if (b == 0) {
raise Exception("Division by zero")
}
return a / b
}
异常捕获
使用 try-catch
块捕获异常。
main() {
try {
let result = divide(10, 0)
println(result)
} catch (e: Exception) {
println("Error: ${e.message}")
}
}
自定义异常
可以定义自己的异常类型。
class CustomException: Exception {
init(self, message: String) {
super.init(message)
}
}
fn risky_function() {
raise CustomException("Something went wrong")
}
main() {
try {
risky_function()
} catch (e: CustomException) {
println("Caught custom exception: ${e.message}")
}
}
并发与并行
仓颉编程语言提供了多种方式来处理并发和并行任务。
线程
使用 thread
关键字创建新线程。
fn worker(id: Int64) {
println("Worker ${id} is running")
}
main() {
let threads = []
for (i in 1..5) {
let t = thread worker(i)
threads.append(t)
}
for (t in threads) {
t.join()
}
}
协程
使用 coroutine
关键字创建协程。
fn worker(id: Int64) {
println("Worker ${id} is running")
}
main() {
let coroutines = []
for (i in 1..5) {
let c = coroutine worker(i)
coroutines.append(c)
}
for (c in coroutines) {
c.resume()
}
}
通道
使用通道在线程或协程之间传递数据。
fn producer(ch: Channel<Int64>) {
for (i in 1..5) {
ch.send(i)
}
ch.close()
}
fn consumer(ch: Channel<Int64>) {
for (value in ch) {
println("Received: ${value}")
}
}
main() {
let ch = Channel<Int64>()
let p = thread producer(ch)
let c = thread consumer(ch)
p.join()
c.join()
}
总结
通过本文的介绍,我们详细了解了仓颉编程语言的基础语法、数据类型、集合类型、函数与方法、模块与包管理、错误处理以及并发与并行编程等内容。仓颉编程语言的设计旨在提供清晰、高效、灵活的编程体验,适用于多种应用场景和编程范式。希望通过这些内容,能够帮助读者更好地掌握和应用仓颉编程语言。
- 点赞
- 收藏
- 关注作者
评论(0)