仓颉语言入门 —— 基本数据类型(二)

Posted by     "xtcel" on Thursday, July 11, 2024

仓颉语言入门——基本数据类型(二)

在仓颉语言的编程之旅中,掌握基本数据类型是构建任何程序的基础。继前一篇文章介绍了整型、浮点型等数值类型后,本篇将深入探讨布尔类型、字符类型、字符串类型以及数组类型,帮助读者进一步了解仓颉语言的数据结构。

布尔类型(Bool)

布尔类型是仓颉语言中用于表示逻辑真假的数据类型。它只有两个可能的值:true 和 false。布尔类型在条件判断、循环控制以及逻辑运算中扮演着至关重要的角色。

示例代码

// 声明布尔变量  
let isOpen: Bool = true  
let isClosed = false  // 类型可以自动推断  

// 使用布尔变量进行条件判断  
if (isOpen) {  
    println("门是开着的")  
} else {  
    println("门是关着的")  
}  

// 逻辑运算  
let canEnter = isOpen && !isClosed  // 逻辑与和逻辑非运算  
println("可以进入吗?", canEnter)

字符类型 (Rune)

字符类型用于表示单个字符,如字母、数字或符号。在仓颉语言中,字符类型使用 Rune 表示,可以表示 Unicode 字符集中的所有字符。 单个字符的字符字面量举例:

// 声明字符变量  
let firstChar: Rune = r'A'  
let secondChar = r"b"  // 类型可以自动推断 

转义字符是指在一个字符序列中对后面的字符进行另一种解释的字符。转义字符使用转义符号 \ 开头,后面加需要转义的字符。 举例如下:

let slash: Rune = '\\'
let newLine: Rune = '\n'
let tab: Rune = '\t'

通用字符以 \u 开头,后面加上定义在一对花括号中的 1~8 个十六进制数,即可表示对应的 Unicode 值代表的字符。举例如下:

main() {
    let he: Rune = '\u{4f60}'
    let llo: Rune = '\u{597d}'
    print(he)
    print(llo)
}

编译并执行上述代码,输出结果为:

你好

字符串类型(String)

字符串类型用于表示一系列字符的集合,是处理文本数据的基础。在仓颉语言中,字符串类型使用 String 表示,由一串 Unicode 字符组合而成。

字符串字面量

字符串字面量分为三类:单行字符串字面量,多行字符串字面量,多行原始字符串字面量。 单行字符串字面量的内容定义在一对单引号或一对双引号之内,引号中的内容可以是任意数量的任意字符。单行字符串字面量只能写在同一行,不能跨越多行。举例如下:

let s1: String = ""
let s2 = 'Hello Cangjie Lang'
let s3 = "\"Hello Cangjie Lang\""
let s4 = 'Hello Cangjie Lang\n'

多行字符串字面量开头结尾需各存在三个双引号(""")或三个单引号(’’’)。 举例如下:

let s1: String = """
"""
let s2 = '''
Hello,
Cangjie Lang'''

多行原始字符串字面量以一个或多个井号(#)和一个单引号(’)或双引号(")开头,后跟任意数量的合法字符,直到出现与字符串开头相同的引号和与字符串开头相同数量的井号为止。 在当前文件结束之前,如果还没遇到匹配的双引号和相同个数的井号,则编译报错。与多行字符串字面量一样,原始多行字符串字面量可以跨越多行。不同支持在于,转义规则不适用于多行原始字符串字面量,字面量中的内容会维持原样。举例如下:

let s1: String = #""#
let s2 = ##'\n'##
let s3 = ###"
Hello,
Cangjie
Lang"###

插值字符串

插值字符串是一种包含一个或多个插值表达式的字符串字面量(不适用于多行原始字符串字面量),通过将表达式插入到字符串中,可以有效避免字符串拼接的问题。 插值表达式必须用花括号 {} 包起来,并在 {} 之前加上 $ 前缀。{} 中可以包含一个或者多个声明或表达式。 下面是插值字符串的简单示例:

main() {
    let fruit = "apples"
    let count = 10
    let s = "There are ${count * count} ${fruit}"
    println(s)


    let r = 2.4
    let area = "The area of a circle with radius ${r} is ${let PI = 3.141592; PI * r * r}"
    println(area)
}

字符串类型支持的操作 字符串类型支持使用关系操作符进行比较,支持使用 + 进行拼接。下面的例子展示了字符串类型的判等和拼接:

main() {
    // 字符串操作  
    let firstName = "John"  
    var lastName = "Doe"  
    let fullName = firstName + " " + lastName  // 字符串拼接  
    println(fullName)  // 输出 "John Doe"  

    // 字符串长度  
    println(greeting.length)  // 输出 13(包括空格和标点符号)
}

字符串还支持其他常见操作,例如拆分、替换等。

数组类型(Array)

数组是一种基本的数据结构,用于存储一系列相同类型的元素。在仓颉语言中,数组是固定大小的。数组中的每个元素都可以通过索引来访问,索引从0开始。仓颉使用 Array 来表示 Array 类型。T 表示 Array 的元素类型,T 可以是任意类型。

var a: Array<Int64> = ... // Array whose element type is Int64
var b: Array<String> = ... // Array whose element type is String

构造数组

我们可以轻松使用字面量来初始化一个 Array,只需要使用方括号将逗号分隔的值列表括起来即可。 编译器会根据上下文自动推断 Array 字面量的类型。

let a: Array<String> = [] // Created an empty Array whose element type is String
let b = [1, 2, 3, 3, 2, 1] // Created a Array whose element type is Int64, containing elements 1, 2, 3, 3, 2, 1

也可以使用构造函数的方式构造一个指定元素类型的 Array。 需要注意的是,当通过 item 指定的初始值初始化 Array 时,该构造函数不会拷贝 item,如果 item 是一个引用类型,构造后数组的每一个元素都将指向相同的引用。

let a = Array<Int64>() // Created an empty Array whose element type is Int64
let b = Array<Int64>(a) // Use another Array to initialize b
let c = Array<Int64>(3, item: 0) // Created an Array whose element type is Int64, length is 3 and all elements are initialized as 0
let d = Array<Int64>(3, {i => i + 1}) // Created an Array whose element type is Int64, length is 3 and all elements are initialized by the initialization function

操作数组

当我们需要对 Array 的所有元素进行访问时,可以使用 for-in 循环遍历 Array 的所有元素。当我们想访问单个指定位置的元素时,可以使用下标语法访问。非空 Array 的第一个元素总是从位置 0 开始的。我们可以从 0 开始访问 Array 的任意一个元素,直到最后一个位置(Array 的 size - 1)。通过 size 属性可以获取 Array 元素的个数。Array 是按元素插入顺序排列的,因此对 Array 遍历的顺序总是恒定的。

main() {
    let arr = [0, 1, 2]
    for (i in arr) {
        println("The element is ${i}")
    }
    let a = arr[0] // a == 0
    let c = arr[-1] // 错误 array size is '3', but access index is '-1', which would overflow
    println("The size of array is ${arr.size}")
}

索引值不能使用负数或者大于等于 size,当编译器能检查出索引值非法时,会在编译时报错,否则会在运行时抛异常。

Array 是一种长度不变的 Collection 类型,因此 Array 没有提供添加和删除元素的成员函数(后续章节将会介绍支持增删的 ArrayList 类型)。 但是 Array 允许我们对其中的元素进行修改,同样使用下标语法。

main() {\
    let arr = [0, 1, 2, 3, 4, 5]
    arr[0] = 3
    println("The first element is ${arr[0]}")

    let arr1 = [0, 1, 2]
    let arr2 = arr1
    arr2[0] = 3
    // arr1 contains elements 3, 1, 2
    // arr2 contains elements 3, 1, 2
}

Array 是引用类型,因此 Array 在作为表达式使用时不会拷贝副本,同一个 Array 实例的所有引用都会共享同样的数据。 因此对 Array 元素的修改会影响到该实例的所有引用。

VArray

仓颉编程语言引入了值类型数组 VArray<T, $N> ,其中 T 表示该值类型数组的元素类型,$N 是一个固定的语法,通过 $ 加上一个数值字面量表示这个值类型数组的长度。需要注意的是,VArray<T, $N> 不能省略 <T, $N>,且使用类型别名时,不允许拆分 VArray 关键字与其泛型参数。

type varr1 = VArray<Int64, $3> // ok
type varr2 = VArray // Error

由于运行时后端限制,当前 VArray<T, $N> 的元素类型 T 或 T 的成员不能包含引用类型、枚举类型、Lambda 表达式(CFunc 除外)以及未实例化的泛型类型。

VArray 可以由一个数组的字面量来进行初始化,左值 a 必须标识出 VArray 的实例化类型:

var a: VArray<Int64, $3> = [1, 2, 3]

同时,它拥有两个构造函数:

// VArray<T, $N>(initElement: (Int64) -> T)
let b = VArray<Int64, $5>({ i => i}) // [0, 1, 2, 3, 4]
// VArray<T, $N>(item!: T)
let c = VArray<Int64, $5>(item: 0) // [0, 0, 0, 0, 0]

除此之外,VArray<T, $N> 类型提供了两个成员方法:

  • 用于下标访问和修改的 [] 操作符方法:
var a: VArray<Int64, $3> = [1, 2, 3]
let i = a[1] // i is 2
a[2] = 4 // a is [1, 2, 4]
  • 用于获取 VArray 长度的 size 成员:
var a: VArray<Int64, $3> = [1, 2, 3]
let s = a.size // s is 3

总结

通过本篇文章,我们深入了解了仓颉语言中的布尔类型、字符类型、字符串类型以及数组类型。这些基本数据类型是构建更复杂数据结构和算法的基础,掌握它们对于深入学习仓颉语言至关重要。希望读者能够通过实践加深对这些类型的理解,并在未来的编程实践中灵活运用。