Y分钟速成X

其中 X=Cangjie

仓颉是一门静态类型的通用编程语言。它有接近 C 系语言的表层语法, 同时提供类型推断、表达式化控制流、代数枚举、模式匹配、类、接口、 泛型、属性、函数值和操作符重载等现代语言特性。

// 源文件以包声明开始。
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<T> {
    public Box(var value: T) {}

    public func map<R>(f: (T) -> R): Box<R> {
        Box<R>(f(value))
    }
}

class Directory {
    private let people: Array<Person>

    public init(people: Array<Person>) {
        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<T>` 是包含 `Some(T)` 和 `None` 的 enum。
// `?T` 是 `Option<T>` 的简写。
func first<T>(items: Array<T>): ?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<T>(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<Int64> = 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<Int64>(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<Person>(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<Command> = [
        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 互操作等模块。
}

这份导览刻意保持紧凑,但展示了仓颉不同于普通 C 系语法的地方: 表达式有类型,Option 取代许多空值场景,枚举可以携带数据,接口可以 约束泛型代码,操作符和属性也是普通声明。


有建议?或者发现什么错误?在GitHub上开一个issue,或者发起pull request

原著sam5440,并由1个好心人修改。

Creative Commons License

© 2026 sam5440