仓颉实战系列 - Tuple 类型 Tuple 类型
Tuple 类型
仓颉编程语言中元组(Tuple)类型是一个由多种不同类型组合而成的不可变(immutable)类型,使用(Type1, Type2, …, TypeN) 表示,其中 N 代表元组的维度。关于元组类型,说明如下:
- Type1 到 TypeN 可以是任意类型(要求 N 不小于 2,即 Tuple 至少是二元的)。
- 对于元组的每一维度,例如第 K 维,可以存放任何 TypeK 子类型的实例。
- 当 (Type1, Type2, …, TypeN) 中的 Type1 到 TypeN 均支持使用 == 进行值判等(使用 != 进行值判不等)时,此 Tuple 类型才支持使用 == 进行值判等(使用 != 进行值判不等);否则,此 Tuple 类型不支持 == 和 !=(如果使用 == 和 !=,编译报错)。两个同类型的 Tuple 实例相等,当且仅当相同位置(index)的元素全部相等(意味着它们的长度相等)。
Tuple 类型的语法定义为:
tupleType
: '(' type (',' type)+ ')';
元组字面量
元组字面量使用格式 (expr1, expr2, … , exprN) 表示,其中多个表达式之间使用逗号分隔,并且每个表达式可以拥有不同的类型。Tuple literal 的语法定义为:
tupleLiteral
: '(' expression (',' expression)+ ')'
;
元组字面量举例:
(3.14, "PIE")(2.4, 3.5)
(2, 3, 4)
((2, 3), 4)
使用元组做解构
元组也可以用来对另一个元组值进行解构:将一个元组中不同位置处的元素分别绑定到不同的变量。下面的例子展示了如何对多返回值函数的返回值进行解构。
func multiValues(a: Int32, b: Int32): (Int32, Int32) {
return (a + b, a - b) // The type of the return value of the function
↪ multiValues is (Int32, Int32).
}
main() {
var (x, y) = multiValues(8,24) // Define an anonymous tuple who has two↪ elements, i.e., x and y.
print("${x}") // output: 32
print("${y}") // output: -16
return 0
}
元组的下标访问
元组支持通过 tupleName[index] 的方式访问其中某个具体位置的元素(index 表示从 0 开始的下标,并且只能是一个 Int64 类型的字面量)。
main() {
var z = multiValues(8, 24) // the type of z is inferred to be (Int32, Int32)
print("${z[0]}") // output: 32
print("${z[1]}") // output: -16
return 0
}
定义元组类型的变量
在定义元组类型的变量时,可以省略类型标注,由编译器根据程序上下文推断出具体的类型。
let tuplePIE = (3.14, "PIE") // The type of tuplePIE is inferred to be
↪ (Float64, String).
var pointOne = (2.4, 3.5) // The type of pointOne is inferred to be (Float64,↪ Float64).
var pointTwo = (2, 3, 4) // The type of pointTwo is inferred to be (Int64,
↪ Int64, Int64).
var pointThree = ((2, 3), 4) // The type of pointThree is inferred to be
↪ ((Int64, Int64), Int64).
Range 类型
仓颉编程语言使用 Range 表示 Range 类型,并要求 T 只能实例化为实现了 Comparable interrace 和Countable interrace 的类型。Range 类型用于表示一个拥有固定步长的序列,并且是一个不可变(immutable)类型。
每个 Range 类型的实例都会包含 start、end 和 step 值。其中,start 和 end 分别表示序列的起始值和终止值,step 表示序列中前后两个元素之间的差值。
Range 类型支持使用 == 进行判等(使用 != 进行判不等),两个相同类型的 Range 实例相等,当且仅当它们同时为 “左闭右开” 或 “左闭右闭”,并且它们的 start 值、end 值和 step 值均对应相等。
创建 Range 实例
存在两种 Range 操作符:… 和 …=,分别用于创建 “左闭右开” 和 “左闭右闭” 的 Range 实例。它们的使用方式分别为 start…end:step 和 start…=end:step(其中 start、end 和 step 均是表达式)。关于这两种表达式,说明如下。
要求 start 和 end 的类型相同,step 的类型为 Int64。
-
表达式 start…end:step 中,当 step > 0 且 start >= end,或者 step < 0 且 start <= end 时,start…end:step 返回一个空 Range 实例(不包含任何元素的空序列);如果 start…end:step 的
结果是非空 Range 实例,则 Range 实例中元素的个数等于 (end-start)/step 向上取整(即大于等
于 (end-start)/step 的最小整数)。 -
表达式 start…=end:step 中,当 step > 0 且 start > end,或者 step < 0 且 start <
end 时,start…=end:step 返回一个空 Range 实例;如果 start…=end:step 的结果是非空 Range
实例,则 Range 实例中元素的个数等于 ((end-start)/step)+1 向下取整(即小于等于 ((end-start)/step)+1
的最大整数)。
let range1 = 0..10:1 // Define an half-open range [0,10) (with step = 1) which
↪ contains 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9.
let range2 = 0..=10:2 // Define a closed range [0,10] (with step = 2) which
↪ contains 0, 2, 4, 6, 8 and 10.
let range3 = 10..0:-2 // Define an half-open range [10,0) (with step = -2) which↪ contains 10, 8, 6, 4 and 2.
let range4 = 10..=0:-1 // Define a closed range [10,0] (with step = -1) which
↪ contains 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 and 0.
let range5 = 10..0:1 // Define an empty range.
let range6 = 0..0:1 // Define an empty range.
let range7 = 0..=0:1 // Define a closed range [0,0] (with step = 1) which only
↪ contains 0.
let range8 = 0..=10:-1 // Define an empty range.
- 表达式 start…end:step 和 start…=end:step 中,step 的值不能等于 0;
let range9 = 0..10: 0 // error: the value of the step should not be↪ zero.
let range10 = 0..=10: 0 // error: the value of the step should not be↪ zero.
除了下节提到的 Range 类型的实例用在下标操作符 [] 中时可以省略 start 或 end 外,其他场景下,start 和 end 均是必选的,只有 step 是可选的(默认 step=1)。
let range11 = 1..10 // Define an half-open range [1, 10) with step = 1.
let range12 = 1..=10 // Define a closed range [1, 10] with step = 1.
let range13 = ..10 // error: the start value is required.
let range14 = 1.. // error: the end value is required.
let range15 = .. // error: the start and end value are required.
Range 类型的使用
Range 类型的表达式主要用在 for-in 表达式中(参见循环表达式),以及作为下标用于截取片段。需要注意的是:
当 Range 类型的实例用在下标操作符 [] 中时,start 和 end 均是可选的。它们的值由使用的上下文决定:在自定义类型上重载下标操作符 [] 且参数类型是 Range 时,使用过程中省略start 时 start 的值等于 0,省略 end 时 end 的值等于 Int64 的最大值。
定义 Range 类型的变量
在定义 Range 类型的变量时,可以显式添加类型标注,也可以省略类型标注(此时由编译器根据程序上下文推断)。
let range16: Range<Int64> = 0..10 // Define an half-open range [0,10) with
↪ step = 1
let range17: Range<Int64> = -10..10:2 // Define an half-open range [-10,10) with↪ step = 2
let range18 = 0..=10 // Define a closed range [0,10] with step = 1let range19 = -10..=10:2 // Define a closed range [-10,10] with step↪ = 2
Function 类型
函数类型(function type)表示一个具体函数的类型,它同样是 immutable 的。函数类型的语法定义为:
arrowType
: parameterTypes '->' type
;
parameterTypes
: '(' (type (',' type)*)? ')'
;
不可变类型
参数类型列表 parameterTypes 和返回类型 type 使用 -> 连接。其中,参数类型列表的一对 () 是必选的,() 内可以包含 0 个或多个类型(多个类型使用 , 分隔):
() -> Int32 // A function type has an empty parameter type list, and its return↪ value type is 'Int32'.
() -> Unit // A function type has an empty parameter type list, and its return↪ value type is 'Unit'.
(Float32) -> Int32 // A function type has one parameter whose type is 'Float32',
↪ and its return value type is 'Int32'
(Int32, Int32, Float32) -> Int32 // A function type has three parameters, and its
↪ return value type is 'Int32'
(Int32, Int32, Float32) -> (Int32, Int32) // A function type has three parameters,↪ and its return value type is '(Int32, Int32)'
(Int32, Int32, Float32) -> Unit // A function type has three parameters, and its
↪ return value type is 'Unit'.
仓颉编程语言中,函数是一等公民(ffrst-class citizens),意味着函数可以作为其他函数的参数,可以作为其他函数的返回值,也可以直接赋值给变量。
函数类型不支持使用 ==(!=)进行判等(判不等)。更多关于函数的介绍,参看第 5 章函数部分。
enum 类型
enum 类型是一种 immutable 类型,用于将一组相关的值(称为 constructor)组织在一起。声明为enum 类型的值,其在任何时刻只能取 enum 类型定义中的某个 constructor。
定义 enum 类型
定义 enum 类型的语法为:
enumDefinition
: enumModifier? 'enum' identifier typeParameters? ('<:' superInterfaces)?↪ genericConstraints? '{' enumBody '}'
;
enumBody
: '|'? caseBody ('|' caseBody)* (functionDefinition |
↪ operatorFunctionDefinition | propertyDefinition | macroExpression)*
;
caseBody
: identifier ('(' type (',' type)* ')')?;
其中 enumModifier 是 enum 类型的修饰符(即 public),enum 是关键字,identifier 是 enum 类型的名字,typeParameters 和 genericConstraints 分别是类型变元列表和类型变元的约束(参考第 9 章泛型相关内容)。enumBody 中可以定义若干 caseBody(即 constructor),每个 constructor 可以没有参数,也可以有一组不同类型的参数,多个 constructor 之间使用 | 分隔,第一个 constructor 之前可以存在一个可选的 |。caseBody 后可以定义 enum 的其它成员,包含成员函数、操作符重载函数、成员属性。
enum 类型定义举例:
/*
Define an enum type 'TimeUnit1' who has four constructors: 'Year', 'Month', 'Day'↪ and 'Hour'.
*/
enum TimeUnit1 {
Year | Month | Day | Hour
}
/*
'TimeUnit2' has four constructors: 'Year(Int32)', 'Month(Int32, Float32)',
↪ 'Day(Int32, Float32, Float32)' and 'Hour(Int32, Float32, Float32, Float32)'.
*/
enum TimeUnit2 {
| Year(Float32)
| Month(Float32, Float32)
| Day(Float32, Float32, Float32)
| Hour(Float32, Float32, Float32, Float32)
}
/*
Define a generic enum type 'TimeUnit3<T1, T2>' who has four constructors:
↪ 'Year(T1)', 'Month(T1, T2)', 'Day(T1, T2, T2)' and 'Hour(T1, T2, T2, T2)'.*/
enum TimeUnit3<T1, T2> {
| Year(T1)
| Month(T1, T2)
| Day(T1, T2, T2)
| Hour(T1, T2, T2, T2)
}
关于 enum 类型,需要注意的是:
enum 类型只能定义在 top-level。
不支持空括号无参的 constructor 定义,且无参 constructor 的类型为被定义的 enum 类型本身,不视作函数类型。有参的 constructor 具有函数类型,但不能作为一等公民使用。例如:
enum E {
| mkE // OK. The type of mkE is E but not () -> E.
| mkE() // Syntax error.}
enum E1 {
| A
}
let a = A // ok, a: E1
enum E2 {
| B(Bool)
}
let b = B // error
enum E3 {
| C
| C(Bool)
}
let c = C // ok, c: E3
作为一种自定义类型,enum 类型默认不支持使用 (!=)进行判等(判不等)。当然,可以通过重载(!=)操作符(参见操作符重载)使得自定义的 enum 类型支持 ==(!=)。
同一个 enum 中支持 constructor 的重载,但是只有参数个数参与重载,参数类型不参与重载,也就是说,允许同一个 enum 中定义多个同名 constructor,但是要求它们的参数个数不同(没有参数的constructor 虽不为函数类型,也可以与其它 constructors 重载),例如:
enum TimeUnit4 {
| Year
| Year(Int32) // ok
| Year(Float32) // error: redeclaration of 'Year'| Month(Int32, Float32) // ok
| Month(Int32, Int32) // error: redeclaration of 'Month'| Month(Int32) // ok
| Day(Int32, Float32, Float32) // ok
| Day(Float32, Float32, Float32) // error: redeclaration of 'Day'
| Day(Float32, Float32) // ok
| Hour(Int32, Float32, Float32, Float32) // ok
| Hour(Int32, Int32, Int32, Int32) // error: redeclaration of 'Hour'| Hour(Int32, Int32, Int32) // ok
}
enum 类型支持递归和互递归定义,例如:
// recursive enumenum TimeUnit5 {
| Year(Int32)
| Month(Int32, Float32)
| Day(Int32, Float32, Float32)
| Hour(Int32, Float32, Float32, Float32)| Twounit(TimeUnit5, TimeUnit5)
}
// mutually recursive enums
enum E1 {
A | B(E2)
}
enum E2 {
C(E1) | D(E1)
}
enum 不可以被继承。
enum 可以实现接口(接口参看第 6 章)。
Enum 值的访问
第一种是通过 enum 名加上 constructor 名的访问方式,例如:
let time1 = TimeUnit1.Year
let time2 = TimeUnit2.Month(1.0, 2.0)
let time3 = TimeUnit3<Int64, Float64>.Day(1, 2.0, 3.0)
第二种是省略 enum 名的方式,例如:
let time4 = Year // syntax sugar of 'TimeUnit1.Year'
let time5 = Month(1.0, 2.0) // syntax sugar of
↪ 'TimeUnit2.Month(1.0, 2.0)'
let time6 = Day<Int64, Float64>(1, 2.0, 3.0) // syntax sugar of 'TimeUnit3<Int64,↪ Float64>.Day(1, 2.0, 3.0)'
注:由于仓颉语言支持对泛型参数的类型推导,所以在使用上述方式一与方式二访问时,泛型参数 均可省略。
关于第二种使用方式需要满足以下规则。
当不存在其它变量名、函数名、类型名、包名冲突的时候,可以省略类型前缀使用。否则优先选择变量名、函数名、类型名或包名。
package p
}
func g() {
let a = A // ok, find p.Alet b = E.A // ok
let c = p.A // ok
F(1) // ok, find p.F
E.F(1) // ok
p.F(1) // ok
let x: C // ok, find p.C
let y: p.C // ok
}
enum 构造器在省略类型前缀使用时,可以在构造器名称后面声明 enum 类型的泛型参数。
enum E<T> {
| A(T)
| B(T)
}
func f() {
let a = A(1) // ok, a: E<Int64>let b = A<Int32>(2) // ok, b: E<Int32>}
enum 解构
使用 match 表达式和 enum pattern 可以实现对 enum 的解构(详见 4.4.1 模式章节)。对前文中定义的 TimeUnit1 和 TimeUnit2 的解构,举例如下:
let time12 = TimeUnit1.Year
let howManyHours = match (time12) {
case Year => 365 * 24 // matched
case Month => 30 * 24
case Day => 24
case Hour => 1
}
let time13 = TimeUnit2.Month(1.0, 1.5)
let howManyHours = match (time13) {
case Year(y) => y * 365.0 * 24.0
case Month(y, m) => y * 365.0 * 24.0 + m * 30.0 * 24.0 // matched
case Day(y, m, d) => y * 365.0 * 24.0 + m * 30.0 * 24.0 + d * 24.0
case Hour(y, m, d, h) => y * 365.0 * 24.0 + m * 30.0 * 24.0 + d * 24.0 + h}
enum 的其它成员
enum 中也可以定义成员函数、操作符函数和成员属性。
定义普通成员函数参见函数定义。
• 定义操作符函数的语法参见操作符重载。• 定义成员属性的语法参见属性的定义。
以下是一些 enum 定义的简单示例。
enum Foo {
A | B | C
func f1() {
f2(this)
}
static func f2(v: Foo) {match (v) {
case A => 0
case B => 1
case C => 2
}
}
prop item: Int64 {
get() {
f1()
}
main() {
let a = Foo.Aa.f1()
Foo.f2(a)
a.item
}
enum 中的 constructor、静态成员函数、实例成员函数之间不能重载。因此,enum 的 constructor、静态成员函数、实例成员函数、成员属性之间均不能重名。
新一代开源开发者平台 GitCode,通过集成代码托管服务、代码仓库以及可信赖的开源组件库,让开发者可以在云端进行代码托管和开发。旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。
更多推荐


所有评论(0)