【华为鸿蒙开发技术】仓颉编程语言详解之多范式编程与内存安全探究

举报
柠檬味拥抱 发表于 2024/07/22 13:56:06 2024/07/22
【摘要】 仓颉编程语言是一种面向全场景应用开发的通用编程语言,旨在兼顾开发效率和运行性能,并提供良好的编程体验。它的主要特点如下: 语法简明高效仓颉编程语言提供了一系列简明高效的语法,减少冗余书写,提升开发效率。例如插值字符串、主构造函数、Flow表达式、match、if-let、while-let和重导出等语法,让开发者可以用较少的编码表达相关逻辑。 多范式编程仓颉编程语言支持函数式、命令式和面向对...

仓颉编程语言是一种面向全场景应用开发的通用编程语言,旨在兼顾开发效率和运行性能,并提供良好的编程体验。它的主要特点如下:

语法简明高效

仓颉编程语言提供了一系列简明高效的语法,减少冗余书写,提升开发效率。例如插值字符串、主构造函数、Flow表达式、match、if-let、while-let和重导出等语法,让开发者可以用较少的编码表达相关逻辑。

多范式编程

仓颉编程语言支持函数式、命令式和面向对象等多范式编程,融合了以下特性:

  • 函数式语言:高阶函数、代数数据类型、模式匹配、泛型等。
  • 面向对象语言:封装、接口、继承、子类型多态等支持模块化开发的特性。
  • 命令式语言:值类型、全局函数等简洁高效的特性。

开发者可以根据开发偏好或应用场景,选择不同的编程范式。

类型安全

仓颉编程语言是静态强类型语言,通过编译时类型检查尽早识别程序错误,降低运行时风险,也便于代码维护。同时,仓颉编译器提供了强大的类型推断能力,减少了类型标注工作,提高开发效率。

内存安全

仓颉编程语言支持自动内存管理,并在运行时进行数组下标越界检查、溢出检查等,确保运行时内存安全。

高效并发

仓颉编程语言提供了用户态轻量化线程(原生协程)以及简单易用的并发编程机制,保证并发场景的高效开发和运行。

兼容语言生态

仓颉编程语言支持与C等主流编程语言的互操作,并采用便捷的声明式编程范式,实现对其他语言库的高效复用和生态兼容。

领域易扩展

仓颉编程语言提供了基于词法宏的元编程能力,支持在编译时变换代码。此外,还提供了尾随lambda、属性、操作符重载、部分关键字可省略等特性,开发者可以深度定制程序的语法和语义,有利于构建内嵌式领域专用语言(EDSL)。

助力UI开发

基于仓颉编程语言的元编程和尾随lambda等特性,可以搭建声明式UI开发框架,提升UI开发效率和体验。

内置库功能丰富

仓颉编程语言提供了功能丰富的内置库,涵盖数据结构、常用算法、数学计算、正则匹配、系统交互、文件操作、网络通信、数据库访问、日志打印、解压缩、编解码、加解密和序列化等功能。

元组类型

元组(Tuple)可以将多个不同的类型组合在一起,成为一个新的类型。元组类型使用 (T1, T2, ..., TN) 表示,其中 T1TN 可以是任意类型。元组的长度是固定的,并且元组类型是不可变的。元组类型的字面量使用 (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 类型的值做加法。每个区间类型的实例包含 startendstep 三个值。

以上是仓颉编程语言的一些特点和常见数据类型的使用示例。这种语言提供了简明高效的语法、多范式编程支持、类型和内存安全、高效并发、语言生态兼容、领域易扩展、UI开发支持以及丰富的内置库功能,旨在提升开发者的开发效率和编程体验。

区间类型的创建

我们可以使用以下几种方法创建一个区间类型的实例:

  1. 使用区间构造函数:

    let range1 = Range<Int64>(start: 0, end: 10, step: 1)
    let range2 = Range<Float64>(start: 0.0, end: 1.0, step: 0.1)
    
  2. 使用区间字面量:
    区间类型支持使用 … 表示从起始值到终止值的区间:

    let range1 = 0..10 // 表示从 0 到 10 的整数区间,步长为 1
    let range2 = 0.0..1.0 // 表示从 0.0 到 1.0 的浮点数区间,步长为 1
    
  3. 使用 … 运算符创建具有指定步长的区间:

    let range1 = 0..10..2 // 表示从 0 到 10 的整数区间,步长为 2
    let range2 = 0.0..1.0..0.1 // 表示从 0.0 到 1.0 的浮点数区间,步长为 0.1
    

访问区间元素

区间类型提供了以下几种方式访问其元素:

  1. 使用 for-in 循环遍历区间中的所有元素:

    main() {
        let range = 0..5
        for (i in range) {
            println(i)
        }
    }
    

    输出结果:

    0
    1
    2
    3
    4
    5
    
  2. 使用下标访问区间中的指定位置的元素:

    main() {
        let range = 0..5
        let element = range[2]
        println(element)
    }
    

    输出结果:

    2
    

区间类型的成员函数

区间类型提供了一些成员函数,可以方便地进行区间操作:

  1. contains 函数:判断一个值是否在区间内:

    main() {
        let range = 0..5
        let isContained = range.contains(3)
        println(isContained) // 输出 true
    }
    
  2. 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} 表示,其中 k1kn 是键的表达式,v1vn 是对应的值的表达式。

let dict: Dictionary<String, Int64> = {"one": 1, "two": 2, "three": 3}

访问字典元素

  1. 使用键访问字典中的值:

    main() {
        let dict = {"one": 1, "two": 2, "three": 3}
        let value = dict["two"]
        println(value) // 输出 2
    }
    
  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
    

修改字典元素

字典类型允许我们对其中的元素进行修改,添加新的键值对,或者删除已有的键值对。

  1. 修改字典中的值:

    main() {
        let dict = {"one": 1, "two": 2, "three": 3}
        dict["two"] = 22
        println(dict["two"]) // 输出 22
    }
    
  2. 添加新的键值对:

    main() {
        let dict = {"one": 1, "two": 2, "three": 3}
        dict["four"] = 4
        println(dict["four"]) // 输出 4
    }
    
  3. 删除已有的键值对:

    main() {
        let dict = {"one": 1, "two": 2, "three": 3}
        dict.remove("two")
        println(dict.contains("two")) // 输出 false
    }
    

字典类型的成员函数

字典类型提供了一些成员函数,可以方便地进行字典操作:

  1. contains 函数:判断一个键是否在字典内:

    main() {
        let dict = {"one": 1, "two": 2, "three": 3}
        let isContained = dict.contains("two")
        println(isContained) // 输出 true
    }
    
  2. 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()
}

总结

通过本文的介绍,我们详细了解了仓颉编程语言的基础语法、数据类型、集合类型、函数与方法、模块与包管理、错误处理以及并发与并行编程等内容。仓颉编程语言的设计旨在提供清晰、高效、灵活的编程体验,适用于多种应用场景和编程范式。希望通过这些内容,能够帮助读者更好地掌握和应用仓颉编程语言。

image.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。