当前位置: 首页 > news >正文

仓颉编程语言青少年基础教程:enum(枚举)类型和Option类型

仓颉编程语言青少年基础教程:enum(枚举)类型和Option类型

enum 和 Option 各自解决一类“语义级”问题:

enum 让“取值只在有限集合内”的约束从注释变成编译器强制;

Option 让“值可能不存在”的语义显式化。

enum类型

enum 类型提供了通过列举一个类型的所有可能取值来定义此类型的方式。

定义 enum 时需要把它所有可能的取值一一列出,称这些值为 enum 的构造器(constructor)。

仓颉的枚举(enum)类型,简单说就是:通过把一个类型所有可能的 “选项” 列出来,来定义这个类型。

枚举只能定义在文件的最顶层(不能嵌套在函数或其他类型里)。

枚举的定义

基本语法:

enum <类型名> {

    | <构造器1> [参数]

    | <构造器2> [参数]

    ......

    | ... // 非穷举(non-exhaustive)构造器,只能有一个且在最后

}

说明:用enum关键字开头,后面跟类型名,大括号里列出所有可能的 “选项”(官方叫 “构造器”),选项之间用|隔开。

  •  构造器分无参(如 Red)和有参(如 Red(UInt8))

  •  支持同名构造器(需参数个数不同)

  •  可包含递归定义(构造器参数使用自身类型)

  •  可添加 ... 作为最后一个构造器(non-exhaustive enum,不可直接匹配)

注意,构造器(constructor)的含义,枚举的 “构造器” = 枚举类型的 “所有可能取值”,和类里的 “构造器(构造函数)” 不是一回事。

基本示例源码:

// 1. 顶层 enum 定义
enum RGBColor {| Red | Green                 // 无参构造器| Blue(UInt8)                 // 有参构造器(亮度值)// 成员函数:打印颜色信息public func describe(): Unit {      // ① 必须显式写出返回类型 Unitmatch (this) {                   // ② match 后面要加括号case Red => println("Red")case Green => println("Green")case Blue(brightness) => println("Blue with brightness: ${brightness}") // ③ 字符串插号语法}}
}// 2. 主函数入口
main(): Int64 {                        let red   = RGBColor.Red            // 全名访问,稳妥let green = RGBColor.Green          //  为避免歧义,统一加类型名let blue  = RGBColor.Blue(150)red.describe()    // 输出:Redgreen.describe()  // 输出:Greenblue.describe()   // 输出:Blue with brightness: 150return 0                        
}

编译运行截图:

使用(创建枚举的实例的)规则

  •  通过 类型名.构造器 或直接用构造器创建实例(无歧义时)

  •  名字冲突时必须加类型名(如与变量 / 函数同名)

例如:

let c1 = Color.Red  // 完整写法

let c2 = Red       // 简写,前提是不跟别的 Red 冲突

如果当前作用域里已经有一个变量叫 Red,那就必须写全名 Color.Red,否则编译器会以为你在用那个变量。

完整示例:

enum Color {| Red| Yellow| Green
}// 模拟变量名冲突
let Red = "字符串变量Red"func message(light: Color): String {match (light) {case Red    => "停!" case Yellow => "注意!"case Green  => "通行!"}
}main() {let light = Yellow     // 可以用 Yellow 或Color.Yellowprintln(message(light))             // 输出:注意!let light2 = Green   // 可以用 Green 或Color.Greenprintln(message(light2))             // 输出:通行!let light3 = Color.Red    // 只能用Color.Red ,不能用Redprintln(message(light3))             // 输出:停!  println(Red) //  输出:字符串变量Red
}

编译运行输出:

注意!
通行!       
停!
字符串变量Red

重要事项说明

1.构造器比较灵活:
(1)可以带参数(带附加信息)
比如想表示 “红色的亮度”,可以给Red加个参数(比如 0-255 的数值):
enum RGBColor {
| Red(UInt8) | Green(UInt8) | Blue(UInt8)
}
// 比如 Red(255) 就表示“亮度为255的红色”

(2)可以有同名选项,但参数个数必须不同
比如既可以有 “单纯的红色”(不带参数),也可以有 “带亮度的红色”(带参数):
plaintext
enum RGBColor {
| Red | Green | Blue  // 不带参数的选项
| Red(UInt8) | Green(UInt8) | Blue(UInt8)  // 带参数的同名选项(参数个数不同)
}

(3)可以有一个 “省略号选项”...
加在最后,表示 “还有其他没列出来的选项”(这种枚举叫 non-exhaustive enum)。用的时候不能直接匹配这个...,得用通配符_(比如 “其他所有情况”):
plaintext
enum T {
| Red | Green | Blue | ...  // 最后一个是省略号,表示还有其他可能
}

2.枚举可以 “自己包含自己”(递归定义)
枚举的选项参数可以是它自己的类型,这在定义复杂结构时很有用。
比如定义一个 “数学表达式” 类型:
•  可以是一个数字(Num)
•  可以是两个表达式相加(Add)
•  可以是两个表达式相减(Sub)
可以这样写:
enum Expr {
| Num(Int64)  // 数字,参数是具体数值(比如Num(5))
| Add(Expr, Expr)  // 加法,参数是两个表达式(比如Add(Num(2), Num(3))表示2+3)
| Sub(Expr, Expr)  // 减法,同理
}
这样你就能表达 1 + (2 - 3) 这样的嵌套结构。
完整示例源码如下:

// 定义递归枚举:表示数学表达式
enum Expr {| Num(Int64)          // 数字| Add(Expr, Expr)     // 加法| Sub(Expr, Expr)     // 减法
}// 定义一个递归函数:计算Expr表达式的值
func eval(expr: Expr): Int64 {match (expr) {case Num(n) => n  // 如果是数字,直接返回数值case Add(a, b) => eval(a) + eval(b) // 加法:递归计算左右两边再相加case Sub(a, b) => eval(a) - eval(b) // 减法:递归计算左右两边再相减}
}// 构造表达式:1 + (2 - 3)
let nestedExpr = Add(Num(1), // 左边:1Sub(Num(2), Num(3)) // 右边:2 - 3
)main(): Unit {let result = eval(nestedExpr)println(result)   // 输出:0,因为1 + (2-3) = 1 + (-1) = 0
}

3.枚举里可以加 “功能”(函数 / 属性)
可以在枚举里定义函数或属性,但名字不能和选项(构造器)重复。完整示例源码如下:

// 带亮度参数的RGB颜色枚举
enum RGBColor {| Red(UInt8)   // 红色(亮度0-255)| Green(UInt8) // 绿色| Blue(UInt8)  // 蓝色// 成员函数:打印颜色和亮度信息func printInfo() {// 使用this指代当前实例match (this) {case Red(b) => println("红色,亮度:${b}")case Green(b) => println("绿色,亮度:${b}")case Blue(b) => println("蓝色,亮度:${b}")}}// 成员函数:判断是否为高亮度(亮度>127)func checkHighBright(): Bool {match (this) {case Red(b) => return b > 127case Green(b) => return b > 127case Blue(b) => return b > 127}}
}// 程序入口(无需func修饰)
main() {let red = RGBColor.Red(200)let green = RGBColor.Green(50)// 调用枚举成员函数red.printInfo()          // 输出:红色,亮度:200println("红色是否高亮度:${red.checkHighBright()}")  // 输出:红色是否高亮度:truegreen.printInfo()        // 输出:绿色,亮度:50println("绿色是否高亮度:${green.checkHighBright()}")// 输出:绿色是否高亮度:false
}

输出:

红色,亮度:200
红色是否高亮度:true 
绿色,亮度:50       
绿色是否高亮度:false

Option类型

Option 是 enum 的泛型应用,专门处理 “可能为空” 的场景,通过 Some 和 None 避免空指针错误,是仓颉中处理不确定性的常用方式。

Option<T> 类型 —— 一种安全处理“可能无值”情况的机制。Option 类型通过Some(有值)和None(无值)两种状态,让 "值是否存在" 变得显式可控,配合模式匹配、??操作符、?操作符等工具,能安全、简洁地处理各种可能无值的场景,是仓颉中避免空引用错误的重要机制。

Option 类型使用 enum 定义,它包含两个构造器(constructor):Some 和 None。其中,Some 会携带一个参数,表示有值;None 不带参数,表示无值。当需要表示某个类型可能有值,也可能没有值时,可以选择使用 Option 类型。

Option 是仓颉标准库里内置的泛型枚举,声明如下:

enum Option<T> {
Some(T)   // 表示“有值”,并携带一个 T 类型的数据
None      // 表示“无值”
}

其中,Some() 是仓颉语言中 Option<T> 类型的一个“构造器”(constructor),用来表示“有一个值”。用法示例:

写法                 含义

Some(100)       表示“有一个整数 100”

Some("Hello") 表示“有一个字符串 "Hello"”

Option 类型简洁的写法:

?T 等价于 Option<T>  (官方写为:?Ty 等价于 Option<Ty>。T和Ty都是Type 的缩写,都代表“某个具体的类型”):

?Int64      等价于 Option<Int64>

?String     等价于 Option<String>

?Bool       等价于 Option<Bool>

例如

// 完整写法
let a: Option<Int64> = Some(100)  // 有值:100
let b: Option<String> = Some("Hello")  // 有值:"Hello"
let c: Option<Int64> = None  // 无值

// 简写形式(?Ty)
let d: ?Int64 = Some(200)  // 等价于Option<Int64>
let e: ?String = None  // 等价于Option<String>

Option 类型的解构使用方式

Option 是一种非常常用的类型,所以仓颉为其提供了多种解构【注】方式,以方便 Option 类型的使用,具体包括:模式匹配、getOrThrow 函数、coalescing 操作符(??),以及问号操作符(?)。

【“解构” 在这里的核心含义是:打破 Option 类型的 “包装”,获取 Some 中包含的具体值,或对 None 进行处理】

1.模式匹配(match 语句)

通过match匹配Some和None,显式处理两种状态。 示例:

//将Option<Int64>转换为字符串(有值则返回值的字符串,无值返回"none")
func getString(p: ?Int64): String {match (p) {case Some(x) => "数字:${x}"  // 匹配有值状态,x绑定具体值case None => "没有提供数字"     // 匹配无值状态}
}main() {let a = Some(10)let b: ?Int64 = Noneprintln(getString(a))  // 输出:数字:10println(getString(b))  // 输出:没有提供数字
}

2. coalescing 操作符(??

用于为None提供默认值,语法:e1 ?? e2

  •  若e1是Some(v),返回v

  •  若e1是None,返回e2(e2需与v同类型)

示例:

main() {let a: ?Int64 = Some(5)let b: ?Int64 = Nonelet r1 = a ?? 0 // a是Some(5),返回5let r2 = b ?? 0  // b是None,返回默认值0println(r1)  // 输出:5println(r2)  // 输出:0
}

3. 问号操作符(?

与.、()、[]、{}结合使用,实现对 Option 内部成员的安全访问:

  •  若 Option 是Some(v),则访问v的成员,结果用Some包装

  •  若 Option 是None,则直接返回None(避免空访问错误)

示例:

a.访问结构体 / 类的成员(. 操作):

// 定义一个结构体
struct R {public var a: Int64public init(a: Int64) {this.a = a}
}main() {let r = R(100)let x: ?R = Some(r)  // 有值的Optionlet y: ?R = None     // 无值的Optionlet r1 = x?.a  // x是Some(r),访问r.a,结果为Some(100)let r2 = y?.a  // y是None,直接返回Noneprintln(r1)  // 输出:Some(100)println(r2)  // 输出:None
}

b.多层访问

问号操作符支持链式调用,任意一层为None则整体返回None:

class A {public var b: B = B()  // A包含B类型的b
}class B {public var c: ?C = C()  // B包含Option<C>类型的c(有值)public var c1: ?C = None // B包含Option<C>类型的c1(无值)
}class C {public var d: Int64 = 100  // C包含Int64类型的d
}main() {let a: ?A = Some(A())  // 有值的Option<A>// 多层访问:a?.b.c?.d// a是Some,b存在;c是Some,d存在 → 结果为Some(100)let r1 = a?.b.c?.d// a?.b.c1?.d:c1是None → 结果为Nonelet r2 = a?.b.c1?.dprintln(r1)  // 输出:Some(100)println(r2)  // 输出:None
}

4. getOrThrow 函数

用于直接获取Some中的值,若为None则抛出异常(需配合try-catch处理):

main() {let a: ?Int64 = Some(8)let b: ?Int64 = None// 获取a的值(成功)let r1 = a.getOrThrow()println(r1)  // 输出:8// 获取b的值(失败,抛出异常)try {let r2 = b.getOrThrow()} catch (e: NoneValueException) {println("捕获异常:b是None")  // 输出:捕获异常:b是None}
}

Option 类型主要用于以下场景:

1.     处理可能为空的返回值:如函数可能返回有效结果或无结果(避免返回 null)

2.     安全的成员访问:通过?操作符避免访问空对象的成员

3.     简化错误处理:用None表示 "无结果" 类错误,替代复杂的错误判断


文章转载自:

http://D83i5pi0.jwgmx.cn
http://z0ujkDsm.jwgmx.cn
http://uSenWRrm.jwgmx.cn
http://WstIzh59.jwgmx.cn
http://B5Pl0Mqh.jwgmx.cn
http://ljMatB7M.jwgmx.cn
http://GR5BEFHK.jwgmx.cn
http://lLAhwQc9.jwgmx.cn
http://mhpT0ocg.jwgmx.cn
http://G5dXE6Oz.jwgmx.cn
http://N6FbdFm7.jwgmx.cn
http://zCP2Hl9p.jwgmx.cn
http://p5TFJwtB.jwgmx.cn
http://8VMxgdUm.jwgmx.cn
http://xXE7l1iC.jwgmx.cn
http://3k5QO2LM.jwgmx.cn
http://JVxdY8SH.jwgmx.cn
http://mSuAyGD4.jwgmx.cn
http://xBblkUa4.jwgmx.cn
http://CkGVVavD.jwgmx.cn
http://uRe1QE8S.jwgmx.cn
http://jc1IsD1X.jwgmx.cn
http://YfUFYbm8.jwgmx.cn
http://X77KhQPr.jwgmx.cn
http://hzlHoa5H.jwgmx.cn
http://79TF6IVI.jwgmx.cn
http://if91JTo7.jwgmx.cn
http://cG5v9JBY.jwgmx.cn
http://qNWEWxWo.jwgmx.cn
http://dlytzbUY.jwgmx.cn
http://www.dtcms.com/a/385356.html

相关文章:

  • 124.stm32故障:程序下载不能运行,stlink调试时可以正常运行
  • 3.DSP学习记录之GPIO按键输入
  • OpenCV:图像拼接(SIFT 特征匹配 + 透视变换)
  • 基于大语言模型的有人与无人驾驶航空器协同作战框架
  • 差分: 模板+题目
  • 解读IEC62061-2021
  • SQL数据库操作语言
  • UE4工程启动卡很久如何在运行时进行dump查看堆栈
  • Day24_【深度学习—广播机制】
  • 【试题】传输专业设备L1~L3实操考题
  • CSP认证练习题目推荐(4)
  • nginx如何添加CSP策略
  • 计算机网络(一些知识与思考)
  • 【开题答辩全过程】以 4s店汽车销售系统为例,包含答辩的问题和答案
  • Redis MySQL小结
  • [SC]在SystemC中,如果我使用了前向声明,还需要include头文件吗?
  • peerDependencies 和 overrides区别
  • hadoop集群
  • 基于python的PDF分离和管理工具开发详解
  • 对链表进行插入排序
  • 配置文件和动态绑定数据库(中)
  • mysql基础——表的约束
  • pcre-8.44-2.ky10.x86_64.rpm怎么安装?CentOS/Kylin系统RPM包安装详细步骤(附安装包)
  • TDengine 聚合函数 COUNT 用户手册
  • STM32F103C8T6开发板入门学习——点亮LED灯
  • K-means 聚类算法:基于鸢尾花数据集的无监督学习全流程解析
  • JVM新生代/老年代垃圾回收器、内存分配与回收策略
  • 介绍一下 RetNet
  • rt-linux下__slab_alloc里的另外一处可能睡眠的逻辑
  • 如何统计DrawMeshInstancedIndirect绘制物体的Triangle数据