英文:
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.
> 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论