Skip to content

Latest commit

 

History

History
220 lines (166 loc) · 5.83 KB

#014_函数.md

File metadata and controls

220 lines (166 loc) · 5.83 KB

4.2 函数

4.2.1 默认的不可变参数

V 的函数默认是不可变的,即使传递的是引用,要传递可变参数必须标记。

由于 V 没有全局变量,这意味着函数的返回值仅是其参数的函数,并且它们的执行没有影响(除非函数使用 I/O)。

注意:V 并不是一种纯粹的函数式语言。

编译器有一项叫 -enable-globals 的 flag 来允许全局变量,但这只是为底层的内核、驱动开发准备的。

4.2.2 可变参数

可以用关键字 mut 修饰函数参数:

struct User {
    name string
mut:
    is_registered bool
}

fn (mut u User) register() {
    u.is_registered = true
}

mut user := User{}
println(user.is_registered) // "false"
user.register()
println(user.is_registered) // "true"

上面的例子里,接收者被明确标记为了可变的,所以 register() 可以改变 user 对象。这样的操作对非接收者参数也起作用:

fn multiply_by_2(mut arr []int) {
    for i in 0 .. arr.len {
        arr[i] *= 2
    }
}

mut nums := [1, 2, 3]
multiply_by_2(mut nums)
println(nums)
// "[2, 4, 6]"

注意:你调用时也必须在 nums 前面加上 mut 标记,这会很清晰地展示该函数会改变值。

与直接修改值相比,更推荐返回值,比如更推荐写 user = register(user)user.register() 而不是 register(mut user)。修改参数只能在程序的关键性能部分进行,从而减少内存的分配与复制。

也是出于这个原因,V 不允许修改默认类型的参数(比如整型)。只有诸如数组或哈希表这样的复杂类型才能被修改。

结构体更新语法

V 可以轻松返回一个修改后的对象:

struct User {
    name          string
    age           int
    is_registered bool
}

fn register(u User) User {
    return User{
        ...u
        is_registered: true
    }
}

mut user := User{
    name: 'abc'
    age: 23
}
user = register(user)
println(user)

4.2.3 可变数目参数

fn sum(a ...int) int {
    mut total := 0
    for x in a {
        total += x
    }
    return total
}

println(sum()) // 0
println(sum(1)) // 1
println(sum(2, 3)) // 5
// using array decomposition
a := [2, 3, 4]
println(sum(...a)) // <-- using prefix ... here. output: 9
b := [5, 6, 7]
println(sum(...b)) // output: 18

4.2.4 匿名函数与高阶函数

fn sqr(n int) int {
    return n * n
}

fn cube(n int) int {
    return n * n * n
}

fn run(value int, op fn (int) int) int {
    return op(value)
}

fn main() {
    // Functions can be passed to other functions
    println(run(5, sqr)) // "25"
    // Anonymous functions can be declared inside other functions:
    double_fn := fn (n int) int {
        return n + n
    }
    println(run(5, double_fn)) // "10"
    // Functions can be passed around without assigning them to variables:
    res := run(5, fn (n int) int {
        return n + n
    })
    println(res) // "10"
    // You can even have an array/map of functions:
    fns := [sqr, cube]
    println(fns[0](10)) // "100"
    fns_map := {
        'sqr':  sqr
        'cube': cube
    }
    println(fns_map['cube'](2)) // "8"
}
  • 函数可以传给其他函数
  • 匿名函数可以在其他函数中声明
  • 函数可以在不分配入参变量的情况下传递
  • 你甚至可以构造函数数组/函数哈希表

4.2.5 闭包

V 也支持闭包。 匿名函数可以从创建它们的域内继承变量。不过它们必须通过明确地列出所有继承的变量来这样做。

my_int := 1
my_closure := fn [my_int] () {
    println(my_int)
}
my_closure() // prints 1

继承的变量会在匿名函数创建的时候复制一份。也就是说,如果原变量在匿名函数创建之后被修改了,这样的修改不会反应到匿名函数中,匿名函数执行时仍然拿着创建时的值执行。

mut i := 1
func := fn [i] () int {
    return i
}
println(func() == 1) // true
i = 123
println(func() == 1) // still true

但是,变量可以在匿名函数里修改。尽管修改不会反映在原变量上,但会在后面继续调用时保存下来。

fn new_counter() fn () int {
    mut i := 0
    return fn [mut i] () int {
        i++
        return i
    }
}

c := new_counter()
println(c()) // 1
println(c()) // 2
println(c()) // 3

如果你需要修改原变量,使用引用。

**警告!**你需要保证引用总是有效,否则会导致未定义的行为。

mut i := 0
mut ref := &i
print_counter := fn [ref] () {
    println(*ref)
}

print_counter() // 0
i = 10
print_counter() // 10

4.2.6 引用

struct Foo {}

fn (foo Foo) bar_method() {
    // ...
}

fn bar_function(foo Foo) {
    // ...
}

如果一个函数的参数时不可变的(比如上面例子里的 foo),传递值或者引用进去都可以。编译器会自己判断,开发者不需要想。

你不再需要去记住你在传递结构体时应该传值还是传引用。

当然你也可以通过加 & 保证总是以引用的方式传入结构体:

struct Foo {
    abc int
}

fn (foo &Foo) bar() {
    println(foo.abc)
}

上面代码中 foo 仍然不可变,无法被修改。想要修改必须使用 (mut foo Foo)

通常来说,V 的引用与 Go 指针还有 C++ 引用类似。比如通用树结构按如下定义:

struct Node<T> {
    val   T
    left  &Node<T>
    right &Node<T>
}

想要取消引用直接用值,则像 C 一样用 * 符号即可。