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,始终理解不到
1
LsLsLsLsLs 237 天前 1
定义 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 中是一种强大的设计方式,允许开发者以灵活且富有表达力的方式编写代码。 |
3
deplives 237 天前
很难理解么?
say 里面是啥?不就一个 g(n) 么 g 是啥,g 不就是 func english n 是啥 n 不就是 "World" 那 g(n) 不就是 english("World") fmt.Println(g(n)) 不就应该是 Hello, World |
4
seers 237 天前 via Android 5
pass func like pass value
|
5
hemingway 237 天前 2
type Greeting func(name string) string 因为这个 typedef ,所以
greet := Greeting(english) 这一行的效果是,greet := english 那么 say 里面: fmt.Println(g(n)) 等价于 fmt.Println(english(n)) |
6
Kumo31 237 天前 1
做等价代换就清晰了,首先可以看成
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") } |
7
xuing 237 天前 2
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" 作为参数,然后输出结果。 |
8
LsLsLsLsLs 237 天前
@Grocker 嗯
|
9
prenwang 237 天前
在 Go 中,函数是一等公民, 函数式编程, 非常巧妙。
Greeting(english) 是函数转换, 函数签名必须一致, 也就是参数,返回值都必须一致。 适用的场景太多了, 比如回调函数,web 框架的中间件等 同时也体现了 go 的类型模式的优点, 把静态语言玩出这种魔法确实强。 |
10
tangqiu0205 237 天前 1
首先定义了 Greeting 为 func 类型, 并且他有一个 say 方法.
greet := Greeting(english) 这段是把 english 转换成 Greeting 类型, 所以 greet 可以调用 say()方法, 又由于 say 方法有接收者类型 g, g 是一个 func 类型,所以这块可以调用 g 的方法. |
11
zrlhk 237 天前
这段 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 语言中函数类型和方法的灵活性和方便性。 |
12
totoro52 237 天前
相当于定义了一个接口, 下面就是实现的方法,这么理解好理解点。
|
13
dhb233 237 天前
我已经尽力去写个好理解的变量名字了,有点长
· 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") } · |
14
yyf1234 237 天前 via iPhone
很典型的 callback 用法,适用于 func 作为参数的场景
|
15
tinyfry 237 天前
Greeting 和 english 的签名相同
|
16
deorth 237 天前 via Android
你以前是写啥的,没有变量存函数的概念么
|
17
6IbA2bj5ip3tK49j 237 天前 via iPhone
好奇,是初学者吗……
|
18
root71370 237 天前
像高数的换元法。。
|
19
lcy 237 天前
类似 C 的类型转换 greet = (Greeting) english
|
20
NessajCN 237 天前
这个你如果会一点 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")) |
21
0Z03ry75kWg9m0XS 237 天前 1
防御性编程 + 1
|
24
wweerrgtc 237 天前 2
|
25
mellowmemories 237 天前
没学过 GO ,浅浅懵一下。
首先创建了一个 lambda 表达式 Greeting ,定义了一种行为,该行为接收一个 string,并返回一个 string 。 在 main 函数的第一行,将复合上文中的定义的 english 函数包装成指定的 Greeting lambda 。 然后 say 函数,似乎是被定义在 Greeting 名下的,作为其方法。 |
26
uiosun 237 天前
给到一个回调函数作为类型,进行后续的类型下的方法处理
JS 、PHP 蛮多这种的,没啥特别。 看这个帖才知道,Go 连 func 都能当类型,有点野哈哈。 |
27
gowk 236 天前
想知道 Rust 里面是不是也有类似的写法?
|
28
journalistFromHK 236 天前
<p>正在学 go,还没见过 type 为 func 的,以前都是 struct...</p>
<p>按照我这种新手的理解(应该和楼主一样吧),</p> <p>核心就是理解 fmt.Println(g(n)),</p> <p>其实在 greet := Greeting(english)之后 greet 就等于 english 了,</p> <p>所以 g(n)就等于 english(n),n 又是由 say 的"world"传入...</p> <p>最后就相当于执行了 english,参数是 say("world")的参数</p> |
29
journalistFromHK 236 天前
@journalistFromHK 卧槽 还想着带个格式来着
|
30
CHchenkeyi 236 天前
他们都说的复杂了,其实很好理解,因为 english 的函数签名和 Greeting 一致,所以可以转换,这边是转换就像 int64 ->int , 是不是就是 int(int64) 这种写法, 那么 greet 就是 english ,然后调用了 say ('world')=>greet(n) 方法, 实际上就是 english('world)
|
31
F7TsdQL45E0jmoiG 236 天前
函数式编程还是 LISP 那种看着舒服
|
32
strconv 236 天前
> Go 语言中的方法( Method )是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者( Receiver )
- say 称作方法,(g Greeting)称之为接收者。 - 方法也是一种函数,接收者也是一个参数 ```go type Greeting func(string) string // say 方法接收者 func (g Greeting) say(name string) { fmt.Println(g(name)) } // say2 // 方法接收者等效于把一个参数前置了,翻译过来就是下面这个函数 // 这个函数就好理解了 // 入参:处理字符串的函数、字符串 // 功能:调用 函数 去处理 字符串 func say2(g Greeting, name string) { fmt.Println(g(name)) } ``` |
33
cmsyh29 236 天前
把方法强转为了函数对象。写惯 pojo 容易以为是构造函数了
|
34
cmsyh29 236 天前 1
go 只是语法简单,上手程度我觉得不如 java
|
35
zhoujx 236 天前
就是有点绕而已
|
37
iceiceice9527 236 天前
重点:函数可以是变量。。
|
38
JoeJasper 236 天前
go 语言里的 duck typing (鸭子类型)
|
40
FYFX 236 天前
我不懂 go,不过 我好奇回复里面说强转的,这个转换真的发生了吗(由编译器做的)?
> A function type denotes the set of all functions with the same parameter and result types. The value of an uninitialized variable of function type is nil. https://go.dev/ref/spec#Function_types 我查了一下 golang 的函数类型是不不包括函数名的,所以 Greeting 和 english 就是同一个函数类型。至于 golang 函数签名和函数类型是不是一回事,也就是说是否包含函数名称这点我看两种说法都有,spec 里面我没看到明确说法。 |
41
leonshaw 236 天前 1
@FYFX
Greeting 是一个新的类型,它的 underlying 类型是 func(name string) string english 是一个签名是 func(name string) string 的函数 |
42
aw2350 236 天前
多态,委托 ,先好好补习一下面向对象
|
43
yiqiao 236 天前
OP 有用过 PHP 吗?类似:call_user_func_array
|
44
flyv2x 236 天前
简单说,其实你的例子,如果去掉 type 定义,直接等价于下面代码:
``` package main import "fmt" func say(g func(name string) string, n string) { fmt.Println(g(n)) } func english(name string) string { return "Hello, " + name } func main() { say(english, "World") } ``` |
45
CHchenkeyi 236 天前
他们都说的复杂了,其实很好理解,因为 english 的函数签名和 Greeting 一致,所以可以转换,这边是转换就像 int64 ->int , 是不是就是 int(int64) 这种写法, 那么 greet 就是 english ,然后调用了 say ('world')=>greet(n) 方法, 实际上就是 english('world)
|
46
Rache1 236 天前
|
47
zhangyq008 236 天前
类似接口型函数,用处多多
看下这篇文章 https://geektutu.com/post/7days-golang-q1.html |
48
hb751968840 236 天前
js 好理解
function english(name) { return "Hello, " + name } function Greeting (english) { return { say: (name) => { english(name) } } } function main() { const greet = Greeting(english) greet.say("World") } |
49
largezhou 236 天前
首先 Greeting 的类型是一个函数
然后 english 这个函数跟 Greeting 的函数签名一样 所以可以把 english 这个函数,强转成 Greeting 类型,即:greet := Greeting(english) 然后调用 Greeting 的“实例”方法 say 由于 Greeting 本身是个函数,可以直接调用,say 里面就是调用 g(n) 实际就是调用的 english("World") |
50
Vegetable 236 天前
写成这样同事不打人的吗
|
51
svnware 236 天前
学过 C/C++的,一看就明白
|
52
sztink 236 天前
**方法本质就是普通的函数,方法的接收者就是隐含的第一个参数。**
```go greet.say("World"); say(greet, "World"); ``` 上面两者是等效的,从这个角度理解就简单了不少。具体介绍可以看:[深入 Go 语言之旅: 方法]( https://go.cyub.vip/function/method/) |
53
via 236 天前 1
鸭子类型,english 入餐像鸭子,出餐也像鸭子,那我们就可以认为 English 就是鸭子。
上面的鸭子就是 Greeting |
54
via 236 天前
OP 可以断言下:english.(Greeting) 看看会不会报错
|
55
sztink 236 天前
@sztink 回复咋不支持 markdown 格式,另外怎么发链接,我发的链接都显示成纯文本(没使用 markdown ,直接发个链接也这样),看别人发的可以直接点击?有 V 友可以告知怎么操作链接吗。
|
56
FengMubai 236 天前
你在`return "Hello, " + name`处打个断点, 观察调用栈能就明白
|
57
cosiner 236 天前
type 类型名称 类型定义
type Greeting func(name string) type Name string type Data struct { Name string } type 类型名称 类型名称 type Data2 Data Go 里面自己使用 type 定义的新类型名称都可以给它加方法, 你这个例子里面是把 english 相当于是匿名的 func(string)类型, 然后把它转换成了 Greeting 类型(实际都是 func(string)类型,Go 里面能随便转换), 就可以调用 Greeting 的 say 方法了,和接口,鸭子类型没关系 |
58
wqtacc 236 天前
```go
package main import "fmt" // Greeting 定义为一个函数 type Greeting func(name string) string func (g Greeting) say(n string) { // g 调用函数自身 s := g(n) fmt.Println(s) } func english(name string) string { return "Hello, " + name } func main() { // english 函数与 Greeting 签名相同,进行强转 greet := Greeting(english) greet.say("World") var greet2 Greeting = func(s string) string { return "Hello, " + s } fmt.Println(greet2("World!")) greet2.say("World!") } ``` |
59
wqtacc 236 天前
```go
package main import "fmt" // Greeting 定义为一个函数 type Greeting func(name string) string func (g Greeting) say(n string) { // g 调用函数自身 s := g(n) fmt.Println(s) } func english(name string) string { return "Hello, " + name } func main() { // english 函数与 Greeting 签名相同,进行强转 greet := Greeting(english) greet.say("World") // 完整的长生命方法, 同时去除 english 的干扰项 var greet2 Greeting = func(s string) string { return "Hello, " + s } fmt.Println(greet2("World!")) greet2.say("World!") } ``` |
60
yuzo555 236 天前
Claude 3 的解释:
这段代码的输出 "Hello, World" 是因为以下几个原因: 1. 在代码中定义了一个名为 Greeting 的函数类型,它是一个接受字符串参数,并返回字符串的函数。 2. 定义了一个名为 english 的函数,它满足 Greeting 类型的要求,即接受一个字符串参数,并返回一个字符串 "Hello, " 加上传入的参数。 3. 在 main 函数中,创建了一个名为 greet 的变量,它的类型是 Greeting,并将 english 函数转换为 Greeting 类型赋值给了 greet 。 4. 对于 Greeting 类型的变量,Go 语言会自动为其提供一个 say 方法,这个方法接受一个字符串参数,并调用 Greeting 类型的函数,传入该字符串参数。 5. 在 main 函数中,调用了 greet.say("World")。这相当于先调用了 greet("World"),也就是调用了 english("World")函数,该函数返回了 "Hello, World"。然后将这个返回值作为参数打印出来。 所以,最终输出的就是 "Hello, World"。这段代码展示了 Go 语言中将函数作为值进行传递和赋值的能力,以及通过为某个类型定义方法来扩展其功能的特性。 在这段代码中,比较难以理解的一个点是: func (g Greeting) say(n string) { fmt.Println(g(n)) } 这里为 Greeting 类型(它是一个函数类型)定义了一个 say 方法。 具体来说,有以下几点需要注意: 1. Greeting 是一个函数类型,它本身并不是一个具体的函数值,而是一种函数签名的描述。 2. Go 语言允许为任何命名类型(包括内置类型和自定义类型)定义方法,这里是为 Greeting 这个函数类型定义了一个 say 方法。 3. say 方法的接收者是 g Greeting,表示这个方法会为所有 Greeting 类型的值(函数值)提供 say 方法。 4. 在 say 方法的实现中,它直接以调用函数的方式 g(n) 来执行该 Greeting 类型的函数值,并将结果打印出来。 这种为函数类型定义方法的做法,看起来有点违反直觉,因为我们一般会认为方法只能为结构体或对象这样的数据类型定义。但在 Go 语言中,函数作为一等公民,也可以为其定义方法。 这样做的好处是,可以为某个函数类型扩展一些通用的辅助方法,而不需要为每个具体的函数值都实现这些方法。这提高了代码的可重用性和可维护性。 |
61
2kCS5c0b0ITXE5k2 236 天前
类型别名
|
62
Subfire 235 天前
把
greet := Greeting(english) greet.say("World") 换成 var greet2 Greeting = english greet2.say("World") 是不是更容易理解点了? |
63
Jessec 235 天前
我一开始也蒙,仔细观察发现这一行代码实际上是把 english 的类型转换成了 Greeting
greet := Greeting(english) |
64
lilei2023 235 天前
貌似就是转换了一下类型,然后把函数作为参数传进去了,前端不经常这么干么??
|
66
sunzhenyucn 235 天前
这样写不是闲的吗?同事应该会打人吧(逃
|
67
sztink 232 天前
@sunzhenyucn 很常见的一个技巧,经常用来将一个普通函数包裹成实现特定接口。比如上面就是 english 这个普通函数可以变成实现 say 方法的接口对象。Go 源码 net/http 里面有使用到 https://github.com/golang/go/blob/master/src/net/http/server.go#L2158-L2167 。包 robfig/cron 里面的 https://github.com/robfig/cron/blob/master/cron.go#L133-L154
|
68
sunzhenyucn 229 天前
@sztink 学到了 谢谢
|
69
kenilalexandra 215 天前
感觉并没有多难理解,Greeting 类型接收函数作为参数,say 函数由于是继承了 Greeting 的所有 greet 实际可以直接调用 say 函数不就完事了?难道说是你没理解函数也可以当作变量进行传递还是?
|