golang: 隐式 vs 显式函数定义

huangapple go评论70阅读模式
英文:

golang: implicit vs explicit func definition

问题

考虑以下代码包:

package A
var X = "change me"
var Y = func(i int) int { return i * i }
func Z(i int) int { return -i }

这两个显式变量(X,Y)可以在另一个包中更改,比如main包...

package main
import "A"
func main() {
    A.X = "done"
    A.Y = func(i int) int { return i * i * i }
    print(A.X, A.Y(7))
    //... 但是似乎无法更改A.Z。
    //A.Z = func(int i) int { return i * i * i } //main.go:8: cannot assign to A.Z
}

显然,在定义一个函数变量(如Y)和一个显式函数(如Z)之间存在差异。我已经搜索过了,但没有找到太多有关这方面的启示。似乎var SomeFunc = func(...)确实定义了一个变量,而func SomeFunc(...)定义了一个常量。

PS:在研究这个问题时,我发现了一个小技巧,我在迄今为止阅读的Go书籍中没有看到过。在导入包时,在包名之前加上一个点可以导入名称而无需限定:

package main
import . "A"
func main() {
    X = "done"
    Y = func(i int) int { return i * i * i }
    print(X, Y(7))
}
英文:

Consider this package:

package A
var X="change me"
var Y=func(i int) int { return i*i) }
func Z(i int) int { return -i) }

The two explicit variables (X,Y) can be changed in another package, say main...

package main
import "A"
func main () {
	A.X="done"
	A.Y=func (i int) int { return i*i*i }
	print(A.X,A.Y(7))
	//... but A.Z apparently can't be changed.
	//A.Z=func (int i) int { return i*i*i } //main.go:8: cannot assign to A.Z
}

Obviously there's a difference between defining a func variable (like Y) and an explicit func (like Z). I have googled this but not found much in the way of enlightenment. It almost seems as if var SomeFunc=func (...) defines indeed a variable, but func SomeFunc(...) defines a constant.

PS: A small goodie I found while researching this which I have not seen mentioned in the Go books I've read so far. A dot before a package import imports names without them having to be qualified:

package main
import . "A"
func main () {
    X="done"
    Y=func (i int) int { return i*i*i }
    print(X,Y(7))
}

答案1

得分: 3

func SomeFunc()本质上创建了一个将标识符SomeFunc与你定义的函数进行强大/常量/不可变绑定的方法。当你这样创建一个变量时:

var (
    SomeFunc = func(i int) int {
        return i * 2
    }
)

你创建了一个类型为func(int) int的全局变量。你可以在以后重新分配这个变量。这是你不能用func SomeFunc标识符做的事情。简单来说,这是因为func SomeFunc()将函数直接绑定到标识符上。var SomeFunc的方法创建了一个变量(在这种情况下是类型为func(int) int的变量),并且该变量使用你分配的函数进行初始化。和变量一样,重新分配是可能的。

示例

你可以使用作用域变量来隐藏函数。这可能会被大多数代码检查工具标记,但这是一种有时在测试中有用的技巧。

示例

至于点导入:除非有非常非常非常好的理由,否则请不要这样做。一个好的理由是你编写一个“添加”到现有包的包,所以你不再导入现有包,而是导入你自己的包。可以将其视为“扩展”一个包。99%的情况下,请不要使用它来解决导入encoding/json并向结构体添加JSON序列化注释的错误。在这种情况下,使用下划线:

package foo
import (
    "encoding/json"
)

type Bar struct {
    Foobar string `json:"foobar"`
}

func New() *Bar {
    return &Bar{"Default foobar"}
}

不了解golang 1.8,但是像这样的包可能会导致编译器错误(导入了包encoding/json但未使用)。为了消除这个错误,你只需将导入更改为:

import(
    _ "encoding/json"
)

点包、下划线和包别名都遵循同样的规则:尽量少使用它们。


示例中使用的代码:

package main

import (
    "fmt"
)

var (
    SomeFunc = func(i int) int {
        return i * 2
    }
)

func main() {
    fmt.Println(SomeFunc(2)) // 输出 4
    reassign()
    fmt.Println(SomeFunc(2)) // 输出 8
    shadowReassign()
    fmt.Println(SomeFunc(2)) // 输出 2
}

// 全局函数
func reassign() {
    // 将新函数分配给全局变量。函数类型必须匹配
    SomeFunc = func(i int) int {
        return i * 4
    }
}

// 将函数分配给局部变量reassign
func shadowReassign() {
    reassign := func() {
        // 与全局reassign相同
        SomeFunc = func(i int) int {
            return i
        }
    }
    reassign()
}
英文:

func SomeFunc(), in essence creates a strong/constant/immutable binding of the identifier SomeFunc to the function you define. When you create a variable like so:

var (
    SomeFunc = func(i int) int {
        return i * 2
    }
)

You create a global variable of the type func(int) int. You can reassign this variable later on. This is something you can't really do with a func SomeFunc identifier. Simply put, this is because func SomeFunc() binds the function Directly to the identifier. The var SomeFunc approach creates a variable (type func(int) int in this case), and that variable is initialised using the function you're assigning. As is the case with variables: reassignment is possible.

Example

What you can do with functions, is shadow them using a scoped variable. This will probably get flagged by most linters, but it's a technique/trick that sometimes can be useful in testing

Example

As for the dot-imports: Please don't do that unless there's a very, very, very good reason for it. A good reason would be you writing a package that adds to an existing one, so you no longer import an existing one, but import your own. Think of it as extending a package. 99% of the time. Don't, whatever you do, use it to quench errors when you import encoding/json to add json serialization annotations to a struct. In those cases, use an underscore:

package foo
import (
    "encoding/json"
)

type Bar struct {
    Foobar string `json:"foobar"`
}

func New() *Bar {
    &Bar{"Default foobar"}
}

Don't know about golang 1.8, but packages like that could result in compiler errors (package encoding/json imported but not used). To silence that error, you simply changed the import to:

import(
    _ "encoding/json"
)

The dot-packages, underscores, and package aliases all follow the same rule: use them as little as possible.


Code used in examples:

package main

import (
    "fmt"
)

var (
    SomeFunc = func(i int) int {
	    return i * 2
	}
)

func main() {
    fmt.Println(SomeFunc(2)) // output 4
	reassign()
    fmt.Println(SomeFunc(2)) // output 8
	shadowReassign()
	fmt.Println(SomeFunc(2)) // output 2
}

// global function
func reassign() {
    // assign new function to the global var. Function types MUST match
    SomeFunc = func(i int) int {
	    return i * 4
	}
}

// assign function to local reassign variable
func shadowReassign() {
    reassign := func() {
        // same as global reassign
		SomeFunc = func(i int) int {
    		return i
		}
    }
	reassign()
}

答案2

得分: 0

在声明一个初始化为函数值的变量和声明一个函数之间有一个区别:

var Y = func(i int) int { return i * i }

和声明一个函数:

func Z(i int) int { return -i }

规范对于声明的说明如下:

声明将非空白标识符绑定到常量、类型、变量、函数、标签或包。

规范还指出

函数声明将标识符(函数名)绑定到函数。

Y 的声明将一个变量绑定到该名称。该变量被初始化为一个函数值。Z 的声明将一个函数绑定到该名称。

如果在名称的位置出现一个显式的句点(.),那么该包的所有导出标识符将在导入源文件的文件块中声明,并且必须在没有限定符的情况下访问。

英文:

There's a difference between declaring a variable initialized with a function value:

var Y=func(i int) int { return i*i) }

and declaring a function:

 func Z(i int) int { return -i) }

The specification says this about declarations:

> A declaration binds a non-blank identifier to a constant, type, variable, function, label, or package.

The specification also says:

> A function declaration binds an identifier, the function name, to a function.

The declaration of Y binds a variable to the name. This variable is initialized with a function value. The declaration of Z binds a function to the name.

> If an explicit period (.) appears instead of a name, all the package's exported identifiers declared in that package's package block will be declared in the importing source file's file block and must be accessed without a qualifier.

huangapple
  • 本文由 发表于 2017年3月24日 23:04:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/43002880.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定