【华为鸿蒙开发技术】仓颉编程语言之函数调用语法糖指南
仓颉编程语言
仓颉编程语言是一种面向全场景应用开发的通用编程语言,兼顾开发效率和运行性能,提供了良好的编程体验。主要特点如下:
1. 语法简明高效
提供简明高效的语法,如插值字符串、主构造函数、Flow 表达式、match、if-let、while-let、重导出等语法,减少冗余书写,提升开发效率。
2. 多范式编程
支持函数式、命令式和面向对象等多范式编程,融合了高阶函数、代数数据类型、模式匹配、泛型等函数式语言的特性,以及封装、接口、继承、子类型多态等面向对象语言特性,还有值类型、全局函数等简洁高效的命令式语言特性。
3. 类型安全
静态强类型语言,通过编译时类型检查尽早识别程序错误,降低运行时风险,便于代码维护。强大的类型推断能力减少类型标注工作,提高开发效率。
4. 内存安全
支持自动内存管理,运行时进行数组下标越界检查、溢出检查等,确保运行时内存安全。
5. 高效并发
提供用户态轻量化线程(原生协程),以及简单易用的并发编程机制,保证并发场景的高效开发和运行。
6. 兼容语言生态
支持与 C 等主流编程语言的互操作,并采用便捷的声明式编程范式,实现对其他语言库的高效复用和生态兼容。
7. 领域易扩展
提供基于词法宏的元编程能力,支持在编译时变换代码,并提供尾随 lambda、属性、操作符重载、部分关键字可省略等特性,利于内嵌式领域专用语言(EDSL)的构建。
8. 助力 UI 开发
基于元编程和尾随 lambda 等特性,可以搭建声明式 UI 开发框架,提升 UI 开发效率和体验。
9. 内置库功能丰富
提供数据结构、常用算法、数学计算、正则匹配、系统交互、文件操作、网络通信、数据库访问、日志打印、解压缩、编解码、加解密和序列化等功能的内置库。
函数调用语法糖
尾随 lambda
函数最后一个形参是函数类型且调用时参数为 lambda 时,可以将 lambda 放在函数调用的尾部。仅一个 lambda 实参时,可以省略 ()
。
func myIf(a: Bool, fn: () -> Int64) {
if(a) {
fn()
} else {
0
}
}
func test() {
myIf(true, { => 100 }) // 普通调用
myIf(true) { // 尾随 lambda
100
}
}
Flow 表达式
包括 pipeline (|>
) 和 composition (~>
) 表达式,简化数据流处理和函数组合。
let res = arr |> inc |> sum // pipeline 表达式
var fg = f ~> g // composition 表达式
变长参数
形参最后一个非命名参数是 Array 类型时,实参可以直接传入参数序列代替 Array 字面量。
func sum(arr: Array<Int64>) {
var total = 0
for (x in arr) {
total += x
}
return total
}
main() {
println(sum())
println(sum(1, 2, 3))
}
函数重载
同一作用域中,同名函数的参数不同(参数个数或参数类型不同)构成重载。
func f(a: Int64): Unit {}
func f(a: Float64): Unit {}
func f(a: Int64, b: Float64): Unit {}
泛型函数重命名泛型形参后,非泛型部分不同构成重载,否则重复定义错误。
interface I1{}
interface I2{}
func f1<X, Y>(a: X, b: Y) {}
func f1<Y, X>(a: X, b: Y) {} // 重载
类内构造函数参数不同构成重载,包括主构造函数和 init 构造函数。
class C {
C(var a!: Int64, var b!: Float64) {
this.a = a
this.b = b
}
public init(a: Int64) {
b = 0.0
this.a = a
}
}
不同作用域中定义的函数在共同可见的作用域中构成重载。
func f(a: Int64): Unit {}
func g() {
func f(a: Float64): Unit {}
}
父类和子类中同名函数在共同可见的作用域中构成重载。
open class Base {
public func f(a: Int64): Unit {}
}
class Sub <: Base {
public func f(a: Float64): Unit {}
}
函数重载决议时,优先选择作用域级别高的函数和最匹配的函数。
open class Base {}
class Sub <: Base {}
func outer() {
func g(a: Sub) {
print("1")
}
func g(a: Base) {
print("2")
}
g(Sub()) // 输出: 1
}
操作符重载
通过定义操作符函数为类型重载操作符,操作符函数需要在 func
关键字前添加 operator
修饰符。
一元和二元操作符重载
open class Point {
var x: Int64 = 0
var y: Int64 = 0
public init (a: Int64, b: Int64) {
x = a
y = b
}
public operator func -(): Point {
Point(-x, -y)
}
public operator func +(right: Point): Point {
Point(this.x + right.x, this.y + right.y)
}
}
索引操作符重载
class A {
operator func [](arg1: Int64, arg2: String): Int64 {
return 0
}
operator func [](arg1: Int64, arg2: String, value!: Int64): Unit {
return
}
}
函数调用操作符重载
open class A {
public init() {}
public operator func ()(): Unit {}
}
func test1() {
let a = A()
a()
}
操作符重载的优先级和结合性不变。可以被重载的操作符包括 ()
, []
, !
, -
, **
, *
, /
, %
, +
, -
, <<
, >>
, <
, <=
等。
可重载的运算符
运算符 | 描述 |
---|---|
> | 大于 |
>= | 大于或等于 |
== | 等于 |
!= | 不等于 |
& | 按位与 |
^ | 按位异或 |
| | 按位或 |
&& | 逻辑与 |
|| | 逻辑或 |
… | 范围 |
-> | Lambda 定义 |
= | 赋值 |
+= | 加等于 |
-= | 减等于 |
*= | 乘等于 |
/= | 除等于 |
%= | 取余等于 |
<<= | 左移等于 |
>>= | 右移等于 |
&= | 按位与等于 |
^= | 按位异或等于 |
|= | 按位或等于 |
扩展(Extend)
如果需要为现有类型添加新的方法,可以使用 extend
关键字定义扩展。
扩展的定义和使用
- 可以使用
extend
关键字为现有的类、接口、结构体和枚举类型添加新方法。 - 扩展不能添加存储属性,但可以添加计算属性。
- 扩展可以为现有类型添加新的构造函数、方法和计算属性,但不能重写已有的方法或属性。
示例
以下示例展示了如何使用扩展为现有类型添加新的方法和计算属性:
// 为 Int 类型添加一个计算属性和一个方法
extend Int {
// 添加计算属性 isEven,判断数字是否为偶数
var isEven: Bool {
get {
self % 2 == 0
}
}
// 添加方法 square,返回数字的平方
func square(): Int {
self * self
}
}
func main() {
let x = 4
println(x.isEven) // 输出:true
println(x.square()) // 输出:16
}
使用限制
- 扩展不能重写现有类型的方法或属性。
- 扩展不能添加存储属性。
- 扩展可以为现有类型添加新的构造函数、方法和计算属性。
扩展的应用场景
- 为现有类型添加新的方法和属性,而不需要继承或修改现有类型的源码。
- 扩展标准库类型或第三方库类型,以便在不改变原有代码的情况下增强其功能。
- 在保持代码简洁和模块化的同时,增加类型的功能和行为。
模式匹配(Pattern Matching)
模式匹配是一种强大的语法特性,用于检查数据结构并从中提取值。仓颉编程语言支持多种模式匹配形式,包括值匹配、元组匹配、数组匹配、对象匹配和枚举匹配等。
示例
以下是一些模式匹配的示例:
值匹配
func describe(x: Int) {
match (x) {
case 0 => println("Zero")
case 1 => println("One")
case _ => println("Other")
}
}
func main() {
describe(0) // 输出:Zero
describe(1) // 输出:One
describe(2) // 输出:Other
}
元组匹配
func describe(point: (Int, Int)) {
match (point) {
case (0, 0) => println("Origin")
case (x, 0) => println("On the X-axis")
case (0, y) => println("On the Y-axis")
case _ => println("Somewhere else")
}
}
func main() {
describe((0, 0)) // 输出:Origin
describe((1, 0)) // 输出:On the X-axis
describe((0, 1)) // 输出:On the Y-axis
describe((1, 1)) // 输出:Somewhere else
}
数组匹配
func describe(arr: Array<Int>) {
match (arr) {
case [1, 2, 3] => println("1, 2, 3")
case [1, 2, _] => println("1, 2, something else")
case _ => println("Other")
}
}
func main() {
describe([1, 2, 3]) // 输出:1, 2, 3
describe([1, 2, 4]) // 输出:1, 2, something else
describe([2, 3, 4]) // 输出:Other
}
对象匹配
class Point(var x: Int, var y: Int)
func describe(p: Point) {
match (p) {
case Point(0, 0) => println("Origin")
case Point(x, 0) => println("On the X-axis")
case Point(0, y) => println("On the Y-axis")
case _ => println("Somewhere else")
}
}
func main() {
let p1 = Point(0, 0)
let p2 = Point(1, 0)
let p3 = Point(0, 1)
let p4 = Point(1, 1)
describe(p1) // 输出:Origin
describe(p2) // 输出:On the X-axis
describe(p3) // 输出:On the Y-axis
describe(p4) // 输出:Somewhere else
}
枚举匹配
enum Shape {
case Circle(radius: Float)
case Rectangle(width: Float, height: Float)
}
func describe(s: Shape) {
match (s) {
case Circle(radius) => println("Circle with radius \(radius)")
case Rectangle(width, height) => println("Rectangle with width \(width) and height \(height)")
}
}
func main() {
let circle = Circle(5.0)
let rectangle = Rectangle(10.0, 20.0)
describe(circle) // 输出:Circle with radius 5.0
describe(rectangle) // 输出:Rectangle with width 10.0 and height 20.0
}
注意事项
- 模式匹配中的变量绑定可以在匹配成功后直接使用。
match
语句可以像switch
语句一样,处理多种情况,并提供默认情况。
通过模式匹配,可以以一种简洁和直观的方式检查和解构复杂的数据结构,从而使代码更加清晰和易于维护。
总结
仓颉编程语言以其简洁的语法和强大的功能,适用于各种编程任务。通过面向对象的设计、扩展机制和模式匹配,提供了灵活且易于维护的编程方式。适合初学者和有经验的开发者。
- 点赞
- 收藏
- 关注作者
评论(0)