这段 go 代码始终理解不到

56 天前
 Grocker
package main

import "fmt"

type Greeting func(name string) string

func (g Greeting) say(n string) {
	fmt.Println(g(n))
}

func english(name string) string {
	return "Hello, " + name
}

func main() {
	greet := Greeting(english)
	greet.say("World")
}

这段代码为什么会输出 Hello, World,始终理解不到

6600 次点击
所在节点    Go 编程语言
69 条回复
2677672
56 天前
定义 Greeting 类型:

go
Copy code
type Greeting func(name string) string
这里定义了一个新的类型 Greeting ,它是一个函数签名。这意味着任何具有相同签名(即接受一个 string 类型的参数并返回一个 string 类型)的函数都可以被看作是一个 Greeting 类型。

为 Greeting 类型添加 say 方法:

go
Copy code
func (g Greeting) say(n string) {
fmt.Println(g(n))
}
这个方法接受一个 Greeting 类型的接收器和一个 string 类型的参数。在这个方法内部,它调用了 Greeting 类型的函数(这里的 g ),传入了 n 作为参数,并打印了该函数的返回值。因为 Greeting 是一个函数类型,所以这里 g(n) 实际上是在调用这个函数。

定义 english 函数:

go
Copy code
func english(name string) string {
return "Hello, " + name
}
这是一个简单的函数,接受一个 string 类型的参数,并返回一个新的 string ,其中包含了问候语。这个函数符合 Greeting 类型的定义。

在 main 函数中使用:

go
Copy code
func main() {
greet := Greeting(english)
greet.say("World")
}
这里首先将 english 函数转换成 Greeting 类型,并赋值给 greet 变量。这是可能的,因为 english 符合 Greeting 类型的定义。
然后,调用了 greet 的 say 方法,并传入了 "World" 作为参数。这将会打印 "Hello, World",因为 english 函数被调用,并以 "World" 作为参数。
这个程序的核心思想是通过函数类型和方法,实现了对函数的封装和扩展。在这个例子中,Greeting 函数类型通过添加 say 方法,能够以一种更结构化的方式使用函数。这种模式在 Go 中是一种强大的设计方式,允许开发者以灵活且富有表达力的方式编写代码。
Grocker
56 天前
@2677672 chatgpt?
deplives
56 天前
很难理解么?
say 里面是啥?不就一个 g(n) 么
g 是啥,g 不就是 func english
n 是啥 n 不就是 "World"

那 g(n) 不就是 english("World")
fmt.Println(g(n)) 不就应该是 Hello, World
seers
56 天前
pass func like pass value
hemingway
56 天前
type Greeting func(name string) string 因为这个 typedef ,所以
greet := Greeting(english)
这一行的效果是,greet := english
那么 say 里面:
fmt.Println(g(n)) 等价于 fmt.Println(english(n))
Kumo31
56 天前
做等价代换就清晰了,首先可以看成
func say(g Greeting, name string) {
fmt.Println(g(name))
}

func main() {
say(Greeting(english), "World")
}

然后
func say(g func(name string) string, name string) {
fmt.Println(g(name))
}

func main() {
say(english, "World")
}

接着
func say(name string) {
fmt.Println(english(name))
}

func main() {
say("World")
}

最后

func say(name string) {
fmt.Println("Hello, " + name)
}

func main() {
say("World")
}
xuing
56 天前
Greeting 是接口,english 是实现,通过 Greeting(english)进行了强转。调用 say 方法,say 方法里面的 g(n)就是调用 english 方法本身。

GPT4 say:

这段代码定义了一个函数类型 `Greeting`,一个接受字符串并返回字符串的函数。`english` 函数符合 `Greeting` 类型的定义,因此可以将 `english` 函数转换为 `Greeting` 类型,并赋值给变量 `greet`。

`Greeting` 类型还定义了一个方法 `say`,该方法接受一个字符串参数 `n`,并使用 `Greeting` 类型的函数(本例中是 `english` 函数)处理这个参数,然后输出结果。

这里是具体的步骤:

1. 定义 `Greeting` 类型为一个函数类型,它接受一个 `string` 类型的参数,并返回一个 `string` 类型的结果。
2. 定义 `english` 函数,该函数符合 `Greeting` 类型:接受一个 `string` 类型的参数,返回一个拼接了 "Hello, " 和参数的 `string` 类型的结果。
3. 在 `main` 函数中,将 `english` 函数转换为 `Greeting` 类型,并赋值给 `greet` 变量。
4. 调用 `greet` 变量的 `say` 方法,并传递 "World" 作为参数。
5. `say` 方法内部调用 `greet`(即 `english` 函数),传递 "World" 作为参数,

得到返回值 "Hello, World"。
6. 使用 `fmt.Println` 输出这个返回值。

所以,当运行这段代码时,它会输出 "Hello, World"。这是因为 `greet.say("World")` 实际上是调用 `english` 函数,将 "World" 作为参数,然后输出结果。
2677672
56 天前
@Grocker
prenwang
56 天前
在 Go 中,函数是一等公民, 函数式编程, 非常巧妙。

Greeting(english) 是函数转换, 函数签名必须一致, 也就是参数,返回值都必须一致。

适用的场景太多了, 比如回调函数,web 框架的中间件等

同时也体现了 go 的类型模式的优点, 把静态语言玩出这种魔法确实强。
tangqiu0205
56 天前
首先定义了 Greeting 为 func 类型, 并且他有一个 say 方法.
greet := Greeting(english) 这段是把 english 转换成 Greeting 类型,
所以 greet 可以调用 say()方法, 又由于 say 方法有接收者类型 g,
g 是一个 func 类型,所以这块可以调用 g 的方法.
zrlhk
56 天前
这段 Go 语言程序定义了一个简单的示例,用于展示函数类型和方法的使用。让我逐步解释这段代码:

首先,通过 package main 声明该程序是一个可独立运行的程序,并且在编译后会生成可执行文件。

import "fmt"引入了 Go 语言标准库中的 fmt 包,用于格式化输入输出。

接着定义了一个类型为 func(name string) string 的别名 Greeting ,表示这是一个接收 string 类型参数并返回 string 类型结果的函数类型。

然后定义了一个方法 say ,该方法属于类型 Greeting ,接收一个 string 类型参数 n ,通过调用 g(n)来输出对应的问候语。

接下来定义了一个普通函数 english ,实现了一个简单的英文问候功能,接收一个 name 参数,返回"Hello, " + name 的字符串。

在 main 函数中,创建了一个变量 greet ,将类型为 Greeting 的 english 函数赋值给它,相当于将 english 函数转换为 Greeting 类型的变量 greet 。

最后通过 greet.say("World")调用了 say 方法,传入参数"World",实际上是调用了 english 函数并输出了"Hello, World"这个问候语。

总结:这段代码演示了如何定义函数类型、方法以及函数的转换与调用。通过这个示例,展示了 Go 语言中函数类型和方法的灵活性和方便性。
totoro52
56 天前
相当于定义了一个接口, 下面就是实现的方法,这么理解好理解点。
dhb233
56 天前
我已经尽力去写个好理解的变量名字了,有点长
·
package main

import "fmt"

type strFunc func(name string) string

func (g strFunc) callFuncAndPrintReturnVal(n string) {
fmt.Println(g(n))
}

func addHelloPrefix(name string) string {
return "Hello, " + name
}

func main() {
addHelloPrefixFunc := strFunc(addHelloPrefix)
addHelloPrefixFunc.callFuncAndPrintReturnVal("World")
}
·
yyf1234
56 天前
很典型的 callback 用法,适用于 func 作为参数的场景
tinyfry
56 天前
Greeting 和 english 的签名相同
deorth
56 天前
你以前是写啥的,没有变量存函数的概念么
xgfan
56 天前
好奇,是初学者吗……
root71370
56 天前
像高数的换元法。。
lcy
56 天前
类似 C 的类型转换 greet = (Greeting) english
NessajCN
56 天前
这个你如果会一点 js 就特别清晰了,就是把函数当成个变量
譬如这里的 english ,虽然它是个函数,但你可以把他当成跟 i := 1 这样的 i 一样的东西
你可以理解称 english := func(n string) string { return "Hello, " + n }
那既然是个变量,你就可以把它放进类型里 , 就像这里的 Greeting
然后下面定义的 Greeting 的方法 say, 根据它的实现,g 其实就是 english, 那 g(n) 就是 english(n)
那么 greet.say("World") 就等于 fmt.Println(english("World"))

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1023324

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX