函数

函数是基本的代码块, 用于执行一个任务. 你可以通过函数来划分不同功能, 逻辑上每个函数执行的是指定的任务. Z1h 语言标准库提供了多种可动用的内置的函数. 例如, len() 函数可以接受不同类型参数并返回该类型的长度. 如果我们传入的是字符串则返回字符串的长度, 如果传入的是数组, 则返回数组中包含的元素个数.

函数定义

函数定义格式如下 函数名 = 参数 => { 函数体 } 函数名 = (参数1, 参数2...) => { 函数体 } func 函数名(参数1, 参数2...) { // 这种情况只能声明局部的函数 函数体 } 也可以将形参的内容解出, 如 // 将第一个参数的key1、key2值解出 函数名 = ({key1, key2}, 参数2) => { 函数体 } // 将第一个参数(数组)的key1、key2值解出 函数名 = ([item1, item2], 参数2) => { 函数体 }

示例

z1h add1 = num => { return num + 1 } add = (num1, num2) => num1 + num2 func minus(num1, num2) { return num1 - num2 } hello = {name, age} => { printf('Hello, name = %v, age = %v', name, age) } hi = ([p1, p2], a2) => { printf({p1, p2, a2}) }

定义解析

|名称|描述| |:-:|:-:| |func|函数由 func 开始声明| 函数名称|函数名就是这个函数的变量名 参数列表|参数就像一个占位符, 当函数被调用时, 你可以将值传递给参数, 这个值被称为实际参数. 参数列表指定的是参数类型、顺序、及参数个数. 参数是可选的, 也就是说函数也可以不包含参数. 函数体|函数定义的代码集合.

函数调用

当创建函数时, 你定义了函数需要做什么, 通过调用该函数来执行指定任务. 调用函数, 向函数传递参数, 并返回值, 例如: z1h func getDouble(num) { return num * 2; } getDouble(100); // 输出200

函数返回多个值

Z1h 函数可以返回多个值, 调用得到的值是一个数组, 例如: z1h func nameAndSuffix(str) { var suffix = $file.ext(str, ''); return (suffix ? str[:suffix.length+1]: str), suffix; } print(nameAndSuffix("hello.z1h")); // 输出[ "hello", ".z1h" ] [foo, bar] = nameAndSuffix('index.html'); print(`${foo} and ${bar}`)

示例

z1h add = e => { e + 1 } print(add(3)) plus = (num1, num2) => { num1 + num2 } print(plus(3, 7)) func minus(n1, n2) { n1 - (n2 || 0) } minus(8, 7)

自动关闭的对象

如果一个对象包含Close函数(包括自定义结构体/map/原生对象), 可以使用该函数自动关闭 这个使用方式与Python很相似 // os.Create 返回的是 *os.File 类型, 包含Close函数 with assert(os.Create(`test.txt`)) as f { assert(f.Write("你好啊".bytes)) } 或者 z1h with { tag: '测试', Close: e=>{print(`关闭: 标记=${this.tag}, 名称=${this.name}`)}, } as obj { obj.name = "haha" print('设置了name') }

逃逸对象(闭包)

如果你的函数返回了函数(或函数的某种形式的引用), 包含了上层定义的变量, 此时就需要进行变量的逃逸 (相当于js里的闭包概念) Z1h也是支持逃逸(闭包)的, 在产生函数的时候会保存当前的栈环境, 示例代码如下: // 声明一个函数, 这个函数返回了三个函数 f = e => { var i = 1 // 三个函数共用的变量 return [ e => { i++ }, e => { i += 2 }, e => { i = nil; print('I=' + i) }, ] }; // 获取一套函数 [f1, f2, f3] = f() // 以下函数逐行调用, 并观察结果 f1() f2() f3() f2() f1() print(i) // 报错: 没有i这个变量

函数调用–函数名后置

foo = arg => print({arg}) bar = 123 (bar)foo // 等同于 foo(bar) // 都会输出 {"arg":123} 要求: 被调用的函数必须是 单个函数变量名, 不能是获取对象变量等其它方式(因为会产生歧义, 并且降低可读性), 例如下面这样是被禁止的 (bar)people.foo // 不允许这样写, 会报错: 语法错误 (bar)people[0] // 不允许这样写, 会报错: 语法错误 结合 #符号 可以做到这种用法 x = "aaa" b = @ { print(a + "b") } c = @ { print(a + "c") } d = @ { print(a + "d") } e = @ { print(a + "e") } f = @ { print(a + "f") } res = x# b# c# d# e# f // aaabcdef // 因为上面的变成了 f(e(d(c(b(x)))))

函数调用–管道操作符(Pipeline Operator)

建议与下方"绑定函数"结合使用 funcs = { foo1: arg => arg + 1, foo2: arg => arg * 100, } foo3 = arg => "Result: " + arg bar = 123 bar |> funcs.foo1 |> funcs.foo2 |> foo3 // Result: 12400 // 近似等于 foo3(funcs.foo2(funcs.foo1(bar))) // Result: 12400

区别

之所以说"近似等于", 是因为两种调用函数的方式略有区别: 通过管道操作符的方式, 是先计算左边传入的参数, 再计算右边调用的函数, 最后调用 传统的函数调用时先计算函数, 再计算参数, 再调用 通过以下代码能更为直观的感受到区别 foo = @{panic("foo")} // 快速声明函数的语法糖, @{TODO} 等同于 (a, a1, a2)=>{TODO} bar = @{panic("bar")} // 这种情况bar函数还没有被调用就崩了 foo()(bar()) // Panic at call func: foo ... // 这种情况foo函数还没有被调用就崩了 bar() |> foo() // Panic at call func: bar ...

绑定函数

先看以下柯里化的示例 foo = (a, b) => (a + 1) * (b + 100) foo1 = b => foo(1, b) foo1(3) // 206 可以用语法糖进行简化 foo = (a, b) => (a + 1) * (b + 100) foo(1, ~0)(3) // 206 上文示例中, foo(1, ~0) 本质上等于 arg => foo(1, arg) ~0 意思是绑定了未来传入的第0个参数, 可以以此类推 ~1~2、…、~99999都是可以的 并且~0等绑定占位符可以进行一些简单的操作符运算, 或者放在map/array里面作为元素 示例: x = (a, b) => print({ a, b }) x(~1 / 3, ~0 / 2 + ~1 / 2 + "Heihei")(222, 333) // {"a":111,"b":"277Heihei"} x([333, 222, { [~1 / 3]: "xx" + ~0, }], ~0 / 2 + ~1 / 2 + "Heihei")(222, 333) 当然了, ~0等绑定占位符如果放在嵌套函数里, 意思就变了 bar = foo(~1 + 2, ~0) // 等价于 bar = (arg0, arg1) => { return foo(arg1 + 2, arg0) } // 但是 bar = foo(~1 + 2, foo1(~0)) // 这个foo1(~0)就会马上被执行, 返回了一个被绑定了的函数, 这个~0不是foo的, 而是foo1的了. 相当于 bar = (_, arg1) => { return foo(arg1 + 2, arg0 => { return foo1(arg0) }) }

绑定函数与管道操作符结合

其实绑定函数主要就是为了解决管道操作符(见上方)的易用性问题 "aaa,bbb" |> strings.Split(~0, ",") // 相当于 foo = arg => strings.Split(arg, ",") foo("aaa,bbb")

与Go原生交互

如果一个函数需要转成Go的原生函数, 只需要使用以下方式即可创建: z1h f = (e=>`传入参数的平方为${e*e}`).native_int_return_string // 后面的部分也可以简写成 .native_i_return_s print('Go类型:', type(f)) // func(int) string print('返回结果:', f(20)) // "传入参数的平方为400" 如果需要创建有复杂类型的函数, 可以通过native函数: f = new('func', ['', z1h.ElemType(os.FileInfo.Ptr), z1h.ElemType(errors.Interface)], z1h.ElemType(errors.Interface), (name, info, err)=>{ print(`Name: ${name}, Size: ${info.Size()?? 'No info'}`) return z1h.NilValue(errors.Interface) } ) print(type(f)) // func(string, os.FileInfo, error) error path_filepath.Walk(".", f)