第三章 函数编程 示例代码

浙江传媒学院 · 仓颉面向对象程序设计 · 基于 CangStream 框架构建

示例程序
34 个
综合练习
6 个
代码合计
40 个

一、函数定义(3.1.1)

例3-1 两数相加

基本函数定义:func 关键字,位置参数,显式返回类型与 return 语句。

package demo
func add(a: Int64, b: Int64): Int64 {
return a + b
}
main() {
  let a=20
  let b=30
  println("${a}+${b}的结果是:${add(a,b)}") 
}

例3-2 两点距离(位置参数)

四个 Int64 位置参数,import std.math.* 使用 sqrt,返回 Float64 距离值。

package demo
import std.math.*
func distance(x1: Int64, y1: Int64, x2: Int64, y2: Int64): Float64 {
let dx = x2 - x1
 let dy = y2 - y1
 return sqrt(Float64(dx * dx + dy * dy))
}
main() {
 let x1 = 20
 let y1 = 30
 let x2 = 200
 let y2 = 300
 println("点A(${x1}, ${y1})到点B(${x2}, ${y2})的距离是:${distance(x1, y1, x2, y2)}")
}

例3-3 两点距离(命名参数+默认值)

将参数改为命名参数并设置默认值,调用时可全部省略,直接写 distance()。

package demo
import std.math.*
func distance(x1!: Int64=20, y1!: Int64=30, x2!: Int64=200, y2!: Int64=300): Float64 {
 let dx = x2 - x1
 let dy = y2 - y1
 return sqrt(Float64(dx * dx + dy * dy))
}
main() {
println("ab的距离是:${distance()}")
}

例3-4 省略返回类型 & Unit 函数

函数返回类型可由最后一个表达式推断;myfun() 显式声明返回 Unit 类型。

package demo
import std.math.*
func distance(x1: Float64, y1: Float64, x2: Float64, y2: Float64){
 let dx = x2 - x1
 let dy = y2 - y1
 sqrt(dx*dx+dy*dy)
}
func myfun(): Unit {
 let s = "两点距离计算结果:"
 print(s)
}
main() {
 let x1 = 20.4
 let y1 = 30.5
 let x2 = 200.5
 let y2 = 300.8
 myfun()
 println("点A(${x1}, ${y1})到点B(${x2}, ${y2})的距离是:${distance(x1, y1, x2, y2)}")
}

二、函数调用(3.1.2)

例3-5 位置参数调用

concat(str1, str2) 两个位置参数,按声明顺序传入。

package demo
func concat(str1: String, str2: String):String {
    return str1 + str2
}
main() {
    let s1: String = "Hello, "
    let s2: String = "Cangjie!"
    let result = concat(s1, s2)
    println("${result}")
}

例3-6 混合调用:位置 + 命名参数

str1 为位置参数,str2! 为命名参数,调用时写 concat(s1, str2: s2)。

package demo
func concat(str1: String, str2!: String):String {
    return str1 + str2
}
main() {
    let s1: String = "Hello, "
    let s2: String = "Cangjie!"
    let result = concat(s1,str2:s2)
    println("${result}")
}

例3-7 命名参数:调用顺序任意

全部声明为命名参数(str1!, str2!),调用时可写 concat(str2: s2, str1: s1)。

package demo
func concat(str1!: String, str2!: String):String {
 return str1 + str2
}
main() {
 let s1: String = "Hello,"
 let s2: String = "Cangjie!"
 let result = concat(str2:s2,str1:s1)// 参数的顺序不同
 println("${result}")
}

例3-8 命名参数默认值(可省略)

str2! 设置默认值 "Cangjie!",调用 concat(s1) 时可省略 str2。

package demo
func concat(str1: String, str2!: String= "Cangjie!"):String {
 return str1 + str2
}
main() {
 let s1= "Hello,"
 let result = concat(s1) // 参数个数可以不同
 println("${result}")
}

例3-9 命名参数默认值(显式传入)

同样的函数,通过 concat(s1, str2: s2) 显式覆盖默认值。

package demo
func concat(str1: String, str2!: String= "Cangjie!"):String {
    return str1 + str2
}
main() {
    let s1= "Hello,"
    let s2= "仓颉!"
    let result = concat(s1,str2:s2)// 参数个数可以不同
    println("${result}")
}

三、函数类型(3.2.1 返回类型 / 3.2.2 嵌套函数)

例3-10 函数类型作为参数(高阶函数)

printAdd 接收一个 (Int64, Int64) -> Int64 类型的函数参数,体现高阶函数思想。

package demo
func printAdd(add: (Int64, Int64) -> Int64, a: Int64, b: Int64): Unit {
    println(add(a, b))
}
main() {
    // 定义 addFunction 函数,接受两个 Int64 类型的参数并返回它们的和
    let addFunction: (Int64, Int64) -> Int64 = { x, y => x + y }
    // 调用 printAdd 函数,传入 addFunction 和两个数值作为参数
    printAdd(addFunction, 5, 3)
} 

例3-11 函数作为返回值

returnAdd() 返回类型为 (Int64, Int64) -> Int64,将函数本身作为返回值传递。

package demo
func add(a: Int64, b: Int64): Int64 {
    return a + b
}
func returnAdd(): (Int64, Int64) -> Int64 {
    return add
}
main() {
    // 获取返回的函数
    var addFunc = returnAdd()    
    // 调用返回的函数并传入参数
    println(addFunc(1, 2))
}

例3-12 函数赋值给变量

通过类型标注 let f: (Int64, Int64) -> Int64 = add 将函数赋值给变量。

package demo
func add(p1: Int64, p2: Int64): Int64 {
    return p1 + p2
}
main() {
    let f: (Int64, Int64) -> Int64 = add
    let result = f(1, 2)  // 使用变量 f 调用函数 add
    println(result)  // 输出 3
}

例3-13 重载函数赋值需类型标注(含错误示例)

var f = add 在重载情况下有二义性(编译错误);需显式声明类型消除歧义。

package demo
func add(i: Int64, j: Int64) {
    i + j
}
func add(i: Float64, j: Float64) {
    i + j
}
main() {
    var f = add  // 错误
    var plus: (Int64, Int64) -> Int64 = add  // 正确
}

例3-14 嵌套函数定义与返回

在 foo() 内部定义 nestAdd,并将其作为函数类型返回值传出作用域。

package demo
func foo() {
    func nestAdd(a: Int64, b: Int64): Int64 {
        return a + b + 3
    }
    println(nestAdd(1, 2))  // 输出 6
    return nestAdd
}
main() {
    let f = foo()  // f 是 foo 返回的嵌套函数 nestAdd
    let x = f(1, 2)  // 调用返回的函数
    println("result: ${x}")  // 输出 result: 6
}

四、Lambda 表达式(3.3)

例3-15 无参数 Lambda

{ => 表达式 } 形式的无参 Lambda,赋值给变量后像普通函数一样调用。

package demo
var helloWorld = { => println("Hello World") }
main(): Int64 {
    helloWorld()  // 调用并输出 "Hello World!"
    return 0
}

例3-16 带参数 Lambda 赋值给变量

var add = { a: Int64, b: Int64 => a + b },参数类型在 Lambda 内标注。

package demo
var add = { a: Int64, b: Int64 => a + b }
main(): Int64 {
let result = add(5, 7) 
println("结果是:${result}") 
return 0;
} 

例3-17 Lambda 作为参数传入高阶函数

f({ a2 => a2 + 10 }) 将内联 Lambda 直接作为实参,省去单独定义函数的步骤。

package demo
func f(a1: (Int64) -> Int64): Int64 {
    return a1(5)
}
main(): Int64 {
let result = f({ a2 => a2 + 10 }) 
println("结果是:${result}") 
return 0;
}

五、闭包(3.4)

例3-18 嵌套函数捕获外部变量(闭包)

内部函数 add 捕获外层局部变量 num,通过 returnAddNum() 返回后仍可访问 num。

package demo

func returnAddNum(): (Int64) -> Int64 {
    let num: Int64 = 10

    // 显式声明 add 的返回类型
    func add(a: Int64): Int64 {
        return a + num
    }

    return add
}

main() {
    let f = returnAddNum()
    println(f(10))  // 输出应为 20
}

例3-19 闭包变量捕获示例

嵌套函数 f1 直接访问外层函数 f 中定义的变量 x,展示作用域捕获规则。

package demo
func f() {
   let x = 9
   func f1() {
     println(x)
 }
 f1()
}
main() {
  f()
}

六、函数调用语法(3.5)

例3-20 尾随 Lambda — 条件分支

myIf(true) { 100 } 将 Lambda 写在括号外,适合最后一个参数是函数类型的场景。

package demo
// 定义一个接收布尔值和函数类型参数的函数
func myIf(a: Bool, fn: () -> Int64): Int64 {
    if (a) {
        return fn()   // 如果 a 为真,则执行传入的函数并返回结果
    } else {
        return 0      // 如果 a 为假,返回 0
    }
}
// 使用尾随 Lambda 调用 myIf 函数
func test(): Unit {
    let result = myIf(true) {        // 尾随 Lambda 调用
        100                           // 当条件为真时,返回 100
    }
    println("结果: ${result}")         // 打印结果
}

main(): Int64 {
    test()  // 调用 test 函数执行
    return 0
}

例3-21 尾随 Lambda — 计算平方

f { i => i * i } 省略小括号,Lambda 紧跟函数名,代码更简洁。

package demo
// 定义函数 f,接受一个类型为 (Int64) -> Int64 的函数 fn
func f(fn: (Int64) -> Int64): Unit {
  let result = fn(2)  // 调用传入的函数,并传入 2
  println("结果: ${result}") // 输出结果
}
// 测试函数,使用尾随 Lambda 传入一个计算平方的 Lambda 表达式
func test(): Unit {
   f { i => i * i }  // 尾随 Lambda,计算 i 的平方    
}
// 主函数,调用 test 函数
main(): Int64 {
   test()  // 调用 test 函数,执行计算平方操作
  return 0
}

例3-22 管道操作符 |>

arr |> inc |> sum 将数组依次送入 inc(每元素+1)再送入 sum(求和),链式调用。

package demo
// 定义inc 函数:将数组中的每个元素加 1
func inc(x: Array<Int64>): Array<Int64> {
   let s = x.size  // 获取数组大小
   var i = 0
   // 遍历数组,逐个元素加 1
   for (e in x where i < s) {
     x[i] = e + 1
     i++
   }
   return x
}
// sum 函数:返回数组元素的总和
func sum(x: Array<Int64>): Int64 {
   var total = 0  
   for (item in x) {  // 累加数组中的每个元素
     total += item
   }
   return total
}
// 主程序
main(): Int64 {
   let arr: Array<Int64> =[1, 3, 5] // 创建一个数组
   let result = arr |> inc |> sum  // 使用管道操作符依次调用 inc 和 sum
  println("结果: ${result}") // 输出结果
   return 0
}

例3-23 函数组合 ~>(基本)

f ~> g 等价于 { x => g(f(x)) },先执行 f 再执行 g,组合为新函数。

package demo
// 定义函数 f 和 g
func f(x: Int64): Float64 {
   return Float64(x)
}
func g(x: Float64): Float64 {
   return x
}
// 主程序
main(): Int64 {
  // 使用组合操作符 ~> 将 f 和 g 组合成 fg
  var fg = f ~> g // 等价于 { x: Int64 => g(f(x)) }
  // 调用组合后的函数 fg
  let result = fg(10) // f(10) = 10, g(10.0) = 10.0
println("结果:${result}") // 输出 10.0000
  return 0
}









例3-24 Lambda 与函数组合

({ x: Int64 => x }) ~> f 将匿名 Lambda 与具名函数 f 组合为新函数。

package demo
// 定义函数 f 和一个 lambda 表达式
func f(x: Int64): Float64 {
    return Float64(x)
}
// 主程序
main(): Int64 {  
    let lambdaComp = ({ x: Int64 => x }) ~> f  // 等价于 { x: Int64 => f({x: Int64 => x}(x)) }
    // 调用组合后的函数 lambdaComp
    let result = lambdaComp(10)  // {x => x}(10) -> 10, f(10) = 10.0
    println("结果:${result}")  // 输出 10.0
    return 0
}

例3-25 泛型函数组合

h1 ~> h2 组合两个泛型函数,需在组合时指定类型参数。

package demo
// 定义两个泛型函数 h1 和 h2
func h1<T>(x: T): T {
  return x
}
func h2<T>(x: T): T {
   return x
}
main(): Int64 { 
  // 使用组合操作符 ~> 将 h1 和 h2 组合成 hh
  var hh = h1<Int64> ~> h2<Int64> // 等价于 { x: Int64 => h2<Int64>(h1<Int64>(x)) }
  // 调用组合后的函数 hh
  let result = hh(10) // h1(10) = 10, h2(10) = 10
  println("结果:${result}") // 输出 10
   return 0
}








例3-26 变长参数基本用法

func sum(arr: Array) 可用 sum()、sum(1,2,3) 等不同参数个数调用。

package demo
func sum(arr: Array<Int64>) {
   var total = 0
   for (x in arr) {
     total += x
  }
  return total
}

main() {
   println(sum()) // 输出 1
   println(sum(1, 2, 3))  // 输出 7
}








例3-27 命名数组参数与变长调用(错误示例)

arr! 为命名参数时不能变长调用;须用 length(arr: [...]) 形式,否则编译报错。

// 运行错误的例子
package demo
func length(arr!: Array<Int64>) {
   return arr.size
}
main() {
   println(length()) // 错误,期望 1 个参数,但找到 0 个
   println(length(1, 2, 3)) // 错误,期望 1 个参数,但找到 3 个
}

例3-28 变长参数展示数组内容

f() 传入 0 个或多个参数时,arr 自动组装为 Array 并可按下标访问。

package demo
func f(arr: Array<Int64>) {
   println("array: ${arr}")
}
main() {
   f() // 输出 "array: []"
   f(1) // 输出 "array: [1]"
   f(1, 2)  // 输出 "array: [1, 2]"
}

例3-29 sum 正确实现(初值 0)

标准求和函数:初始值为 0,演示 sum()=0、sum(1,2,3)=6、sum(10,20)=30。

package demo
func sum(arr: Array<Int64>) {
    var total = 0
    for (x in arr) {
        total += x
    }
    return total
}
main() {
    println(sum())           // 输出 0
    println(sum(1, 2, 3))    // 输出 6
    println(sum(10, 20))     // 输出 30
}

例3-30 混合位置参数与变长数组参数

f(a: Int64, arry: Array) 中 a 固定接收第一个参数,其余打包进 arry。

package demo
  func f(a: Int64, arry: Array<Int64>) {
   println("a: ${a}, array: ${arry}")
}
main() {
   f(1,2,3)  // 输出 "a: 1, array: [2, 3]"
}








例3-31 命名参数配合默认值

b!: Int64 = 2 使 b 既是命名参数又有默认值,调用时可只传 a。

package demo
func f(a: Int64, b!: Int64 = 2) {
    println("a: ${a}, b: ${b}")
}
main() {
    f(1)   // 输出 "a: 1, b: 2"
    f(2)   // 输出 "a: 2, b: 2"
    f(3)  // 输出 "a: 3, b: 2"
}

七、函数重载(3.6)

例3-32 按参数个数重载

f(Int64) 与 f(Int64, Float64) 同名但参数个数不同,编译器根据调用自动选择。

package demo
func f(a: Int64): Unit { 
  println("Function with one Int64 argument: ${a}")
}
func f(a: Int64, b: Float64): Unit { 
   println("Function with one Int64 and one Float64 argument: ${a}, ${b}")
}
main() {
   f(10) 
   f(10, 3.14) 
}








例3-33 按参数类型重载

f(Int64) 与 f(Float64) 参数个数相同但类型不同,字面量类型决定调用哪个版本。

package demo
func f(a: Int64): Unit { 
   println("Function with Int64 argument: ${a}")
}
func f(a: Float64): Unit { 
   println("Function with Float64 argument: ${a}")
}
main() {
   f(10)   
   f(3.14) 
}

例3-34 按第二参数类型重载

f(Int64, Int64) 与 f(Int64, Float64) 第一个参数相同,第二个参数类型不同。

package demo
func f(a: Int64, b: Int64): Unit { 
   println("Function with two Int64 arguments: ${a}, ${b}")
}
func f(a: Int64, b: Float64): Unit { 
   println("Function with one Int64 and one Float64 argument: ${a}, ${b}")
}
main() {
   f(10, 20) 
   f(10, 3.14)  
}

八、综合练习(demo3-S1 – demo3-S6)

练习3-S1 求两数最大值

定义 max(a, b) 函数,用 if-else 返回较大值,体现基本函数定义与调用。

package demo
// 定义一个高阶函数 apply,它接受一个整数数组和一个转换函数作为参数
func apply(arr: Array<Int>, f: (Int) -> Int): Array<Int> {
    var result = Array<Int>(arr.size,repeat:0)  // 创建与输入数组相同大小的结果数组
    for (x in 0..arr.size) {
        result[x] = f(arr[x])  // 对arr[x]应用函数f,结果存储到result[x]
    }
    return result
}
// 示例函数1:求2倍
func double(x: Int): Int {
    return x * 2
}
// 示例函数2:求平方
func square(x: Int): Int {
    return x * x
}
// 示例函数3:加10
func addTen(x: Int): Int {
    return x + 10
}
main() {
    // 创建测试数组
    var numbers=Array<Int>(5, repeat: 0)
    numbers[0] = 1
    numbers[1] = 2
    numbers[2] = 3
    numbers[3] = 4
    numbers[4] = 5    
    println("原始数组: [1, 2, 3, 4, 5]")
    
    // 示例1:使用 double 函数求每个元素的2倍
    var doubled = apply(numbers, double)
    print("求2倍: [")
    for (i in 0..doubled.size) {
        print("${doubled[i]}")
        if (i < doubled.size - 1) {
            print(", ")
        }
    }
    println("]")
    
    // 示例2:使用 square 函数求每个元素的平方
    var squared = apply(numbers, square)
    print("求平方: [")
    for (i in 0..squared.size) {
        print("${squared[i]}")
        if (i < squared.size - 1) {
            print(", ")
        }
    }
    println("]")
    
    // 示例3:使用 addTen 函数给每个元素加10
    var addedTen = apply(numbers, addTen)
    print("加10: [")
    for (i in 0..addedTen.size) {
        print("${addedTen[i]}")
        if (i < addedTen.size - 1) {
            print(", ")
        }
    }
    println("]")
    
    // 示例4:使用匿名函数(lambda表达式)求3倍
    var tripled = apply(numbers, {x => x * 3})
    print("求3倍(lambda): [")
    for (i in 0..tripled.size) {
        print("${tripled[i]}")
        if (i < tripled.size - 1) {
            print(", ")
        }
    }
    println("]")
    
    // 示例5:使用匿名函数求负数
    var negated = apply(numbers, {x => -x})
    print("求负数(lambda): [")
    for (i in 0..negated.size) {
        print("${negated[i]}")
        if (i < negated.size - 1) {
            print(", ")
        }
    }
    println("]")
    
    // 示例6:复合操作 - 先乘2再加5
    var complex = apply(numbers, {x => x * 2 + 5})
    print("乘2加5(lambda): [")
    for (i in 0..complex.size) {
        print("${complex[i]}")
        if (i < complex.size - 1) {
            print(", ")
        }
    }
    println("]")
}

练习3-S2 多文件结构程序中的函数调用

跨包调用示例:hoch/mathops.cj 提供高阶函数,hoch/characters.cj 提供汉字组合,main.cj 统一调用。

package demo.hoch

public func double(x: Int64): Int64 {  // 求倍函数
    return x * 2
}

public func applyTwice(f: (Int64) -> Int64, value: Int64): Int64 {
    return f(f(value))                  // 高阶函数:对 value 应用 f 两次
}
package demo.hoch

// 不依赖索引,遍历每个元素,只在不是第一个元素时加分隔符
public func compose(char: String, parts: Array<String>) {
    var partsStr = ""
    var first = true
    for (p in parts) {
        if (!first) {
            partsStr += "+"    // 在后续元素前加分隔符
        }
        partsStr += p
        first = false
    }
    println("${char} = ${partsStr}")
}
package demo
import demo.hoch.*

main() {
    let result = applyTwice(double, 3)    // 高阶函数 applyTwice
    println("高阶函数调用结果是:${result}")

    println("汉字组合是:")
    compose("林", ["木", "木"])
    compose("森", ["木", "木", "木"])
}

练习3-S3 makeAdder — 返回 Lambda 闭包

makeAdder() 返回一个捕获常量 5 的 Lambda,体现闭包与函数作为返回值的组合。

package demo
func makeAdder(): (Int64) -> Int64 {
    return { x: Int64 => x + 5 }
}

main() {
    let add5 = makeAdder()
    println("10 + 5 = ${add5(10)}")  // 输出:15
}

练习3-S4 闭包捕获偏移量

outer() 内定义 inner(x),inner 捕获 offset=100 并返回 x + offset。

package demo

func outer(): (Int64) -> Int64 {
    let offset = 100
    func inner(x: Int64): Int64 {
        return x + offset
    }
    return inner
}

main() {
    let f = outer()
    println(f(200)) // 输出:300
}





练习3-S5 Counter 类 + 闭包递增

getIncrementer() 返回一个闭包,每次调用递增并打印 Counter 类实例的 count 字段。

package demo
main() {
    let result = { a: Int64, b: Int64 => a * b }(6, 7)
    println("6 * 7 = ${result}")  // 输出:42
}

练习3-S6 综合练习六

package demo
// 定义一个元组数组,每个元组存储汉字和对应的部件
let partsList = [
    ("仓", (r'人', r'㔾')),
    ("颉", (r'吉', r'页'))
]

// 定义分析函数
func analyze(char: String): String {
    // 遍历元组数组,查找匹配的汉字
    for (entry in partsList) {
        if (entry[0] == char) {
            // 使用字符串拼接代替 .join()
            let partsStr = entry[1][0].toString() + "和" + entry[1][1].toString()
            return "${char}由${partsStr}组成"
        }
    }
    return "${char}暂时不支持解析"
}
main(): Int64 {
    // 调用 analyze 函数
    println(analyze("仓"))  // 输出: 仓由人和㔾组成
    println(analyze("颉"))  // 输出: 颉由吉和页组成
    return 0
}