Yak语言核心语法精讲:函数的创建与使用全解析
前言
在掌握了Yak语言的基础数据类型、复合类型和控制流语法后,我们的代码已经具备了基础的逻辑处理能力,但在面对复杂场景时,线性的代码结构会变得臃肿且难以维护。函数作为编程的核心模块化单元,能够将重复的逻辑封装为可复用的代码块,大幅提升开发效率与代码可读性。
本文基于Yak语言官方文档,全面讲解函数的声明、调用、参数传递、可变参数、箭头函数及闭包特性。无论你是编写漏洞扫描脚本、内网渗透工具,还是实现自动化任务,掌握函数的使用都是构建高效、可维护Yak程序的关键。
一、函数声明:Yak的函数是“值”
在Yak语言中,函数本质上是一种值类型,这意味着函数可以被赋值给变量、作为参数传递、作为返回值返回,与字符串、数字等基础类型的使用方式一致。
1.1 推荐声明方式:匿名函数赋值
Yak推荐使用函数字面量声明函数,并将其赋值给变量,这种方式定义的函数本身没有名称,通过变量名来调用。
add = func(a, b) { return a + b } println(add(1, 2)) // 输出:3上述代码中,func(a,b)是函数的类型签名,表示该函数需要两个入参,返回值类型则由函数体的return语句决定,不会显式体现在类型签名中。
1.2 多关键字支持:func、fn、def等价
为了适配不同开发者的编程习惯,Yak允许使用fn或def作为func的替代关键字,三者功能完全一致。同时也支持传统的命名函数声明方式:
// 标准命名函数 func abc() { println("Hello World Named-Function!") } // def关键字定义函数 def (){ println("Function Defined with def keyword") } def namedDefFunction() { println("Hello World Named-Function! With DEF!") } // fn关键字定义函数 fn (){ println("Function Defined with fn keyword") } fn namedFn() { println("Hello World Named-Function! With FN!") }二、函数调用与返回:灵活的返回值机制
Yak语言的函数调用方式直观易懂,同时支持多返回值特性,能满足复杂场景下的数据返回需求。
2.1 函数调用的三种方式
函数定义完成后,可通过变量名+参数列表调用,也支持立即执行,无参函数还可省略空参数列表。
// 1. 标准调用:变量接收函数后调用 hello = func(s){ println(s) } hello("123") // 2. 直接调用:函数定义后立即执行 func(s){ println(s) }("123") // 3. 无参函数简写:省略空括号,定义后立即执行 func{ println("123") }2.2 返回值的处理规则
Yak函数支持无返回值、单返回值和多返回值三种模式,不同返回值场景的处理方式有明确规则。
2.2.1 多返回值:切片接收与解构赋值
多返回值函数的返回结果本质上是一个切片,可通过单一变量接收(得到完整切片),或多变量接收(按顺序解构赋值)。
addsub = func(a, b) { return a + b, a - b } // 单一变量接收:得到切片 result = addsub(1, 2) println(result) // 输出: [3, -1] // 多变量接收:数量必须与返回值一致 sum, diff = addsub(1, 2) println(sum) // 输出: 3 println(diff) // 输出: -1注意:多变量接收时,变量数量必须与返回值数量完全匹配,否则会抛出运行时错误:
sum, diff, another = addsub(1, 2) // 错误:左侧3个变量,右侧2个返回值
2.2.2 无返回值:返回nil
若函数没有return语句,或return后无值,则函数无返回值。此时用变量接收返回结果,会得到Yak的空值标识nil。
func noReturn() { println("This function does not return a value.") } result = noReturn() println(result) // 输出: nil // 可通过nil判断函数是否有返回值 if result != nil { println("Function returned a value.") } else { println("Function did not return a value.") }开发建议:函数的多个出口应保证返回值数量一致,避免因返回值数量不匹配导致的代码健壮性问题。
三、函数参数:支持函数作为参数传递
由于Yak的函数是值类型,因此函数可以作为参数传入另一个函数,这种参数被称为函数参数。通过该特性,我们可以将自定义逻辑传递到函数内部执行,实现灵活的逻辑扩展。
3.1 函数参数的使用案例
以下示例定义了AfterFunc函数,接收一个时间间隔和一个函数参数,等待指定时间后执行传入的函数:
AfterFunc = func(dur,f){ time.Sleep(dur) f() } println(now()) // 传入匿名函数作为参数 AfterFunc(2, func(){ println("2 seconds passed") }) println(now())执行结果
2023-11-09 11:34:34.538482 +0800 CST m=+0.181281751 2 seconds passed 2023-11-09 11:34:36.543558 +0800 CST m=+2.1863747513.2 核心特性总结
函数作为值,其传递方式与字符串、数字等基础类型完全一致,不仅可以作为参数,还能作为返回值、存入数组或字典,是Yak实现高阶编程的基础。
四、函数的可变参数:接收任意数量的参数
在处理不确定数量的输入参数时,Yak支持可变参数特性,通过在参数名后添加...,即可让函数接收任意数量的同类型参数,这些参数在函数内部会被封装为数组。
4.1 定义可变参数函数
以下示例定义了sum函数,接收任意数量的数值参数,计算它们的总和:
sum = func(numbers...) { total = 0 for number in numbers { total += number } return total } // 调用可变参数函数 println(sum(1, 2, 3)) // 输出: 6 println(sum()) // 输出: 04.2 混合固定参数与可变参数
可变参数可以与固定参数混合使用,但可变参数必须是函数的最后一个参数,固定参数需要在调用时优先赋值。
func sum(first, rest...) { total = first for number in rest { total += number } return total } // 调用混合参数函数 println(sum(12)) // 输出: 12(仅传递固定参数) println(sum(12, 3, 5, 6)) // 输出: 26(固定参数+3个可变参数)4.3 可变参数使用注意事项
- 可变参数必须放在函数参数列表的末尾;
- 调用混合参数函数时,固定参数必须传入,可变参数可传0个或多个;
- 函数内部,可变参数以数组形式存在,可通过循环、索引等方式访问。
五、箭头函数:简洁的函数定义语法糖
箭头函数是Yak提供的一种简洁函数定义语法,语法风格与ECMAScript类似,但不需要this上下文,非常适合编写逻辑简单的短函数或回调函数。
5.1 基本语法与定义
箭头函数通过=>连接参数与函数体,支持无参、单参、多参三种形式,函数体可直接写表达式(自动返回结果)或写代码块(需用return返回)。
// 单参数:表达式函数体,自动返回结果 arrowFunction = a => a + 1 // 多参数:需用括号包裹参数 arrowFunction2 = (a, b) => a + b // 无参数:需用括号占位 arrowFunction3 = () => 1 + 1 // 代码块函数体:需显式return arrowFunction4 = (a, b) => { sum = a + b return sum }5.2 箭头函数支持可变参数
箭头函数同样支持可变参数特性,语法与普通函数一致:
// 可变参数箭头函数 arrowFunctionWithRest = (args...) => { for arg in args { println(arg) } } arrowFunctionWithRest(1,2,3) // 输出: 1 2 3 // 混合固定参数与可变参数 arrowFunctionMixed = (fixed1, fixed2, rest...) => { println("Fixed parameters:", fixed1, fixed2) println("Rest parameters:", rest) } arrowFunctionMixed("a", "b", 1, 2, 3) // 输出: Fixed parameters: a b; Rest parameters: [1 2 3]5.3 箭头函数的典型应用:回调函数
箭头函数的简洁性使其非常适合作为回调函数,例如在数组的Filter方法中筛选元素:
result = ["a", 1, 2, 3].Filter(i => typeof(i) == int) println(result) // 输出: [1 2 3]开发建议:逻辑简单的回调函数优先使用箭头函数,可大幅简化代码长度,提升可读性。
六、函数的闭包特性:有状态的函数
闭包是Yak的高级特性之一,它允许内部函数访问并操作外部函数的变量,即使外部函数已经执行完毕,内部函数依然能保留对外部变量的引用,从而实现“有状态”的函数。
6.1 闭包的定义与创建
闭包的创建需要满足两个条件:
- 函数嵌套定义,内部函数引用外部函数的变量;
- 外部函数返回内部函数。
以下示例定义了IntGeneratorFactory函数,返回一个闭包,用于生成递增的整数序列:
IntGeneratorFactory = func(a) { // 内部函数引用外部变量a return func() { a = a + 1 println(a) } }6.2 闭包的使用:独立的状态管理
每次调用外部函数,都会创建一个新的闭包实例,每个实例的状态相互独立,互不影响。
// 创建两个闭包实例,初始状态分别为0和10 IntGenerator1 = IntGeneratorFactory(0) IntGenerator2 = IntGeneratorFactory(10) // 调用闭包,状态独立递增 IntGenerator1() // 输出: 1 IntGenerator1() // 输出: 2 IntGenerator1() // 输出: 3 IntGenerator2() // 输出: 11 IntGenerator2() // 输出: 12 IntGenerator2() // 输出: 136.3 闭包的应用场景
闭包的核心价值在于数据隐藏与状态保留,常见应用场景包括:
- 实现有状态的工具函数(如计数器、生成器);
- 封装私有变量,避免全局变量污染;
- 构建函数工厂,批量生成具有相似逻辑的函数。
总结
函数是Yak语言构建复杂程序的核心单元,其值类型特性是理解所有高级用法的基础。本文从函数声明、调用与返回、参数传递、可变参数、箭头函数到闭包,全面覆盖了Yak函数的核心特性:
- 函数是值,可赋值、传参、返回,支持高阶编程;
- 灵活的返回值机制,支持多返回值与nil判断;
- 可变参数适配不确定输入场景,混合参数需遵循顺序规则;
- 箭头函数简化短逻辑函数定义,适合作为回调;
- 闭包实现状态保留,是封装与模块化的重要工具。
掌握这些特性后,你可以将复杂的业务逻辑拆解为独立的函数模块,无论是开发漏洞扫描工具、内网渗透脚本,还是实现自动化任务,都能编写出更高效、更易维护的Yak代码。
后续学习中,建议结合实际场景(如编写一个带闭包状态的漏洞计数器)进行实战,深化对函数特性的理解。