这段 go 代码始终理解不到
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,始终理解不到
定义 Greeting 类型:goCopy codetype Greeting func(name string) string这里定义了一个新的类型 Greeting ,它是一个函数签名。这意味着任何具有相同签名(即接受一个 string 类型的参数并返回一个 string 类型)的函数都可以被看作是一个 Greeting 类型。为 Greeting 类型添加 say 方法:goCopy codefunc (g Greeting) say(n string) { fmt.Println(g(n))}这个方法接受一个 Greeting 类型的接收器和一个 string 类型的参数。在这个方法内部,它调用了 Greeting 类型的函数(这里的 g ),传入了 n 作为参数,并打印了该函数的返回值。因为 Greeting 是一个函数类型,所以这里 g(n) 实际上是在调用这个函数。定义 english 函数:goCopy codefunc english(name string) string { return "Hello, " + name}这是一个简单的函数,接受一个 string 类型的参数,并返回一个新的 string ,其中包含了问候语。这个函数符合 Greeting 类型的定义。在 main 函数中使用:goCopy codefunc main() { greet := Greeting(english) greet.say("World")}这里首先将 english 函数转换成 Greeting 类型,并赋值给 greet 变量。这是可能的,因为 english 符合 Greeting 类型的定义。然后,调用了 greet 的 say 方法,并传入了 "World" 作为参数。这将会打印 "Hello, World",因为 english 函数被调用,并以 "World" 作为参数。这个程序的核心思想是通过函数类型和方法,实现了对函数的封装和扩展。在这个例子中,Greeting 函数类型通过添加 say 方法,能够以一种更结构化的方式使用函数。这种模式在 Go 中是一种强大的设计方式,允许开发者以灵活且富有表达力的方式编写代码。
chatgpt?
很难理解么?say 里面是啥?不就一个 g(n) 么g 是啥,g 不就是 func english n 是啥 n 不就是 "World"那 g(n) 不就是 english("World") fmt.Println(g(n)) 不就应该是 Hello, World
pass func like pass value
type Greeting func(name string) string 因为这个 typedef ,所以 greet := Greeting(english) 这一行的效果是,greet := english那么 say 里面: fmt.Println(g(n)) 等价于 fmt.Println(english(n))
做等价代换就清晰了,首先可以看成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") }
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" 作为参数,然后输出结果。
嗯
在 Go 中,函数是一等公民, 函数式编程, 非常巧妙。Greeting(english) 是函数转换, 函数签名必须一致, 也就是参数,返回值都必须一致。适用的场景太多了, 比如回调函数,web 框架的中间件等同时也体现了 go 的类型模式的优点, 把静态语言玩出这种魔法确实强。
首先定义了 Greeting 为 func 类型, 并且他有一个 say 方法.greet := Greeting(english) 这段是把 english 转换成 Greeting 类型,所以 greet 可以调用 say()方法, 又由于 say 方法有接收者类型 g, g 是一个 func 类型,所以这块可以调用 g 的方法.
这段 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 语言中函数类型和方法的灵活性和方便性。
相当于定义了一个接口, 下面就是实现的方法,这么理解好理解点。
我已经尽力去写个好理解的变量名字了,有点长·package mainimport "fmt"type strFunc func(name string) stringfunc (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")}·
很典型的 callback 用法,适用于 func 作为参数的场景
Greeting 和 english 的签名相同
你以前是写啥的,没有变量存函数的概念么
好奇,是初学者吗……
像高数的换元法。。
类似 C 的类型转换 greet = (Greeting) english
这个你如果会一点 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"))
防御性编程 + 1
对 go 了解不深
因吹斯汀,高数跟编程语言建立了连接
结合另一个例子 应该可以看懂
没学过 GO ,浅浅懵一下。首先创建了一个 lambda 表达式 Greeting ,定义了一种行为,该行为接收一个 string,并返回一个 string 。在 main 函数的第一行,将复合上文中的定义的 english 函数包装成指定的 Greeting lambda 。然后 say 函数,似乎是被定义在 Greeting 名下的,作为其方法。
给到一个回调函数作为类型,进行后续的类型下的方法处理JS 、PHP 蛮多这种的,没啥特别。看这个帖才知道,Go 连 func 都能当类型,有点野哈哈。
想知道 Rust 里面是不是也有类似的写法?
正在学 go,还没见过 type 为 func 的,以前都是 struct...
按照我这种新手的理解(应该和楼主一样吧),
核心就是理解 fmt.Println(g(n)),
其实在 greet := Greeting(english)之后 greet 就等于 english 了,
所以 g(n)就等于 english(n),n 又是由 say 的"world"传入...
最后就相当于执行了 english,参数是 say("world")的参数
卧槽 还想着带个格式来着
他们都说的复杂了,其实很好理解,因为 english 的函数签名和 Greeting 一致,所以可以转换,这边是转换就像 int64 ->int , 是不是就是 int(int64) 这种写法, 那么 greet 就是 english ,然后调用了 say ('world')=>greet(n) 方法, 实际上就是 english('world)
函数式编程还是 LISP 那种看着舒服
Go 语言中的方法( Method )是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者( Receiver )- say 称作方法,(g Greeting)称之为接收者。- 方法也是一种函数,接收者也是一个参数gotype 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))}
把方法强转为了函数对象。写惯 pojo 容易以为是构造函数了
go 只是语法简单,上手程度我觉得不如 java
就是有点绕而已
#1 2677672 #11 zrlhk gpt 回答
重点:函数可以是变量。。
go 语言里的 duck typing (鸭子类型)
谢谢,看到“强转”这两个字就懂了
我不懂 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. go.dev/ref/spec#Function_types我查了一下 golang 的函数类型是不不包括函数名的,所以 Greeting 和 english 就是同一个函数类型。至于 golang 函数签名和函数类型是不是一回事,也就是说是否包含函数名称这点我看两种说法都有,spec 里面我没看到明确说法。
Greeting 是一个新的类型,它的 underlying 类型是 func(name string) stringenglish 是一个签名是 func(name string) string 的函数
多态,委托 ,先好好补习一下面向对象
OP 有用过 PHP 吗?类似:call_user_func_array
简单说,其实你的例子,如果去掉 type 定义,直接等价于下面代码:package mainimport "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")}
他们都说的复杂了,其实很好理解,因为 english 的函数签名和 Greeting 一致,所以可以转换,这边是转换就像 int64 ->int , 是不是就是 int(int64) 这种写法, 那么 greet 就是 english ,然后调用了 say ('world')=>greet(n) 方法, 实际上就是 english('world)
就是存了个回调函数吧,等效于 JS 的。function Foo (func) { this._func = func; this.say = function (str) { console.log(this._func(str)); }; if (!(this instanceof Foo)) { return new Foo(func); }}function english (str) { return 'Hello, ' + str;}// 1Foo(english).say('World');// 2(new Foo(english)).say('World');
类似接口型函数,用处多多看下这篇文章 geektutu.com/post/7days-golang-q1.html
js 好理解function english(name) { return "Hello, " + name}function Greeting (english) { return { say: (name) => { english(name) } }}function main() { const greet = Greeting(english) greet.say("World")}
首先 Greeting 的类型是一个函数然后 english 这个函数跟 Greeting 的函数签名一样所以可以把 english 这个函数,强转成 Greeting 类型,即:greet := Greeting(english)然后调用 Greeting 的“实例”方法 say由于 Greeting 本身是个函数,可以直接调用,say 里面就是调用 g(n)实际就是调用的 english("World")
写成这样同事不打人的吗
学过 C/C++的,一看就明白
方法本质就是普通的函数,方法的接收者就是隐含的第一个参数。gogreet.say("World");say(greet, "World");
上面两者是等效的,从这个角度理解就简单了不少。具体介绍可以看:深入 Go 语言之旅: 方法
鸭子类型,english 入餐像鸭子,出餐也像鸭子,那我们就可以认为 English 就是鸭子。上面的鸭子就是 Greeting
OP 可以断言下:english.(Greeting) 看看会不会报错
回复咋不支持 markdown 格式,另外怎么发链接,我发的链接都显示成纯文本(没使用 markdown ,直接发个链接也这样),看别人发的可以直接点击?有 V 友可以告知怎么操作链接吗。
你在return "Hello, " + name
处打个断点, 观察调用栈能就明白
type 类型名称 类型定义type Greeting func(name string)type Name stringtype Data struct { Name string}type 类型名称 类型名称type Data2 DataGo 里面自己使用 type 定义的新类型名称都可以给它加方法, 你这个例子里面是把 english 相当于是匿名的 func(string)类型, 然后把它转换成了 Greeting 类型(实际都是 func(string)类型,Go 里面能随便转换), 就可以调用 Greeting 的 say 方法了,和接口,鸭子类型没关系
gopackage mainimport "fmt"// Greeting 定义为一个函数type Greeting func(name string) stringfunc (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!")}
gopackage mainimport "fmt"// Greeting 定义为一个函数type Greeting func(name string) stringfunc (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!")}
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 语言中,函数作为一等公民,也可以为其定义方法。这样做的好处是,可以为某个函数类型扩展一些通用的辅助方法,而不需要为每个具体的函数值都实现这些方法。这提高了代码的可重用性和可维护性。
类型别名
把 greet := Greeting(english) greet.say("World")换成 var greet2 Greeting = english greet2.say("World")是不是更容易理解点了?
我一开始也蒙,仔细观察发现这一行代码实际上是把 english 的类型转换成了 Greetinggreet := Greeting(english)
貌似就是转换了一下类型,然后把函数作为参数传进去了,前端不经常这么干么??
更正一下,没传!看错了!
这样写不是闲的吗?同事应该会打人吧(逃
我是 ddd 这方面的新手,入门快两年了,还是不明白里面的缘由。 所以在 v 站上面,寻找这方面知友,相互学习一下,有兴趣的留个 v 。 人多的话,可以创个群 包含战术设计,…
Go语言出了一个Issue,这个Issue的链接在这里:http://code.google.com/p/go/issues/detail?id=9 ,这个Issue的编号是9…
TCP共有11个网路状态,其中涉及到关闭的状态有5个。 在我们编写网络相关程序的时候,这5个状态经常出现。因为这5个状态相互关联,相互纠缠,而且状态变化触发都是由应用触发,但是…