// 源文件以包声明开始。 package learnxinyminutes // 导入语句跟在包声明之后。 import std.convert.* import std.math.* import std.random.* // 安装仓颉 SDK 后,单文件可这样编译: // cjc learncangjie.cj // 较大的工程通常使用 `cjpm` 和 `cjpm.toml`。 // 单行注释以两个斜杠开始。 /* 多行注释是这个样子。 */ //////////////////////////////////////////////////////////// // 1. 程序入口、输出、输入与变量创建 //////////////////////////////////////////////////////////// // `main` 是可执行程序的入口。 main(): Int64 { // `println` 向标准输出写一行。 println("Hello, Cangjie!") // `print` 输出后不自动换行。 print("这个提示保持在同一行: ") println("done") // `readln()` 从标准输入读取一整行,结果是 String。 // 这里保持注释,避免导览程序运行时等待输入。 // let rawInput = readln() // let parsedNumber = Int64.parse(rawInput) // 用 `let` 创建不可变绑定。 let language: String = "Cangjie" // 用 `var` 创建可变变量。 var releaseYear: Int64 = 2024 releaseYear += 1 // 局部变量通常可以省略类型标注。 let inferredInt = 42 let inferredFloat = 3.14159 println("${language} ${releaseYear}") println("${inferredInt}, ${inferredFloat}") learnBindings() learnStringsAndNumbers() learnControlFlow() learnCollections() learnFunctions() learnStructsClassesAndInterfaces() learnEnumsAndOptions() learnStandardLibrary() return 0 } //////////////////////////////////////////////////////////// // 2. 后文会用到的声明 //////////////////////////////////////////////////////////// // `struct` 常用于值语义记录。 struct Vec2 { // 主构造函数可以直接声明字段。 public Vec2(var x: Float64, var y: Float64) {} public func length(): Float64 { sqrt(x * x + y * y) } // `mut` 表示这个 struct 方法会修改接收者。 public mut func scale(k: Float64): Unit { x *= k y *= k } // 操作符也是函数。 public operator func +(rhs: Vec2): Vec2 { Vec2(x + rhs.x, y + rhs.y) } public func toString(): String { "(${x.format(".1")}, ${y.format(".1")})" } } // 接口描述行为。实现接口使用 `<:`。 interface Named { prop label: String } // class 是引用类型,可以包含字段、构造函数、属性、实例方法、 // 静态方法和操作符函数。 class Person <: Named { let id: Int64 private let rawName: String public init(id: Int64, name: String) { this.id = id this.rawName = name } // 属性使用 `get`,可变属性还可以提供 `set`。 public prop label: String { get() { "${id}:${rawName}" } } public func greet(): Unit { println("Hello, I am ${rawName}.") } } // 类也可以使用紧凑构造函数,在参数列表中声明字段。 class Box { public Box(var value: T) {} public func map(f: (T) -> R): Box { Box(f(value)) } } class Directory { private let people: Array public init(people: Array) { this.people = people } // 重载 `[]`,让这个小查询类型可以用下标访问。 public operator func [](id: Int64): ?Person { for (person in people) { if (person.id == id) { return Some(person) } } return None } } // enum 是代数数据类型。构造器可以携带数据。 enum Command { Quit | Echo(String) | Move(Vec2) | Repeat(Int64, String) } // `Option` 是包含 `Some(T)` 和 `None` 的 enum。 // `?T` 是 `Option` 的简写。 func first(items: Array): ?T { if (items.size == 0) { return None } return Some(items[0]) } func run(command: Command): Unit { match (command) { case Quit => println("quit") case Echo(text) => println(text) case Move(delta) => println("move by ${delta.toString()}") case Repeat(times, text) => for (_ in 0..times) { println(text) } } } func printNamed(item: T): Unit where T <: Named { println(item.label) } // 函数可以按参数类型重载。 func twice(x: Int64): Int64 { x * 2 } func twice(x: Float64): Float64 { x * 2.0 } //////////////////////////////////////////////////////////// // 3. 绑定与基本类型 //////////////////////////////////////////////////////////// func learnBindings(): Unit { // 常见基本类型。 let ok: Bool = true let signed: Int64 = -42 let unsigned: UInt64 = 42 let f32: Float32 = 1.25 let f64: Float64 = 2.5 println("${ok}, ${signed}, ${unsigned}, ${f32}, ${f64}") // 整数字面量支持常见进制前缀。 let decimal: Int64 = 255 let binary: Int64 = 0b11111111 let octal: Int64 = 0o377 let hex: Int64 = 0xFF println("${decimal}, ${binary}, ${octal}, ${hex}") // 数值转换是显式的。 let count: Int64 = 3 let price: Float64 = 19.95 let total = Float64(count) * price println("total: ${total.format(".2")}") } //////////////////////////////////////////////////////////// // 4. 字符串、字符、元组与插值 //////////////////////////////////////////////////////////// func learnStringsAndNumbers(): Unit { let name = "Ada" let score = 99.5 // 字符串插值使用 `${...}`。 println("${name} scored ${score.format(".1")}") // `readln()` 读取字符串。做数值计算前要先解析。 // let line = readln() // let n = Int64.parse(line) let csv = "red,green,blue" let parts = csv.split(",") println(parts[0]) // `Rune` 表示 Unicode 字符。 let letter: Rune = Rune(UInt32(65)) let lineBreak: Rune = Rune(UInt32(10)) let generated: Rune = Rune(UInt32(65)) println("${letter}${lineBreak}${generated}") // 元组把固定数量的值组合在一起。 let pair: (String, Int64) = ("answer", 42) let (word, value) = pair println("${word}: ${value}") } //////////////////////////////////////////////////////////// // 5. 控制流是表达式化的 //////////////////////////////////////////////////////////// func learnControlFlow(): Unit { let temperature = 22.5 // `if` 可用于分支。 if (temperature > 30.0) { println("hot") } else if (temperature < 10.0) { println("cold") } else { println("comfortable") } // `if` 也可以产生值。 let status = if (temperature > 18.0) { "open" } else { "closed" } println(status) // `a..b` 是左闭右开区间。 for (i in 0..3) { print("${i} ") } println() // `a..=b` 两端都包含。 var sum: Int64 = 0 for (i in 1..=100) { sum += i } println(sum) var x: Int64 = 4 while (x > 0) { x -= 1 } // `do-while` 至少执行一次循环体。 do { x += 1 } while (x < 2) while (true) { if (x < 2) { continue } break } } //////////////////////////////////////////////////////////// // 6. 数组与区间 //////////////////////////////////////////////////////////// func learnCollections(): Unit { // 数组字面量会推断元素类型。 let primes = [2, 3, 5, 7, 11] println("first prime: ${primes[0]}") // 用重复初值创建固定长度 Array。 var squares: Array = Array(5, repeat: 0) for (i in 0..squares.size) { squares[i] = i * i } for (value in squares) { print("${value} ") } println() let maybeFirst = first(squares) if (maybeFirst.isSome()) { println("first square: ${maybeFirst.getOrThrow()}") } } //////////////////////////////////////////////////////////// // 7. 函数、闭包与重载 //////////////////////////////////////////////////////////// func learnFunctions(): Unit { // 函数类型使用箭头。 var op: (Int64, Int64) -> Int64 = { a: Int64, b: Int64 => a + b } println(op(2, 3)) op = { a: Int64, b: Int64 => a * b } println(op(2, 3)) // 闭包可以捕获局部变量。 let offset = 10 let addOffset: (Int64) -> Int64 = { n: Int64 => n + offset } println(addOffset(5)) // 函数本身可以作为值。 let f: (Int64) -> Int64 = twice println(f(21)) println(twice(21)) println(twice(21.0).format(".1")) let boxed = Box(21) let mapped = boxed.map({ n: Int64 => "value=${n}" }) println(mapped.value) } //////////////////////////////////////////////////////////// // 8. struct、class、interface、属性与操作符 //////////////////////////////////////////////////////////// func learnStructsClassesAndInterfaces(): Unit { var a = Vec2(3.0, 4.0) let b = Vec2(1.0, 2.0) let c = a + b println(a.length().format(".1")) println(c.toString()) a.scale(2.0) println(a.toString()) let ada = Person(1, "Ada") ada.greet() // `Person` 实现了 `Named`,所以可以传给带 `where T <: Named` // 约束的泛型函数。 printNamed(ada) let directory = Directory([ Person(1, "Ada"), Person(2, "Grace"), Person(3, "Edsger") ]) // 这里会调用 Directory.operator[]。 let maybePerson = directory[2] if (maybePerson.isSome()) { let person = maybePerson.getOrThrow() println("found ${person.label}") } } //////////////////////////////////////////////////////////// // 9. enum、match 与 Option //////////////////////////////////////////////////////////// func learnEnumsAndOptions(): Unit { let commands: Array = [ Echo("ready"), Move(Vec2(5.0, -2.0)), Repeat(2, "again"), Quit ] for (command in commands) { run(command) } let directory = Directory([Person(1, "Ada")]) let missing = directory[99] match (missing) { case Some(person) => println(person.label) case None => println("no person with that id") } // `if let` 风格条件适合只关心某个匹配成功分支的场景。 if (directory[1].isSome()) { let person = directory[1].getOrThrow() println("hello ${person.label}") } } //////////////////////////////////////////////////////////// // 10. 标准库一瞥 //////////////////////////////////////////////////////////// func learnStandardLibrary(): Unit { // `std.math.*` println(sqrt(81.0)) println(abs(-12)) // `std.random.*` let rng = Random() let dice = rng.nextInt64(6) + 1 println("dice: ${dice}") // `std.convert.*` let n = Int64.parse("123") let y = Float64.parse("3.5") println("${n}, ${y.format(".2")}") // 仓颉标准库还包含集合、I/O、时间、正则、并发、测试、 // 反射、宏和 C 互操作等模块。 }