Golang函数语法详解:func (receiver) identifier(parameters) (returns) { code }
Golang函数语法详解:func (receiver) identifier(parameters) (returns) { code }
关于函数语法,我有以下信息:func (receiver) identifier(parameters) (returns) { code }
由于以下所有项都用括号标记:(receiver)、(parameters) 和 (returns);我该如何区分它们,或者当我在函数中看到它们时,如何知道哪个是哪个?
父母?
更多关于Golang函数语法详解:func (receiver) identifier(parameters) (returns) { code }的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
是的,她确实有。根据我的理解,她进行了多次巡讲,并且在她的提问中也提到了这一点。
@cherilexvold1974 你完成 Go 语言之旅了吗?这是一个很好的起点。 https://tour.golang.org/
根据它们的位置。
同时请注意,虽然接收者和参数周围的括号是必需的,但返回类型周围的括号仅在有多个返回值时才需要。
NobbZ:
应该是“括号”,手机虚拟键盘的自动更正功能在捣乱。
我也一样。在这个论坛上用手机回复实在太难了。
cherilexvold1974:
Parents
应该是“parens”,手机虚拟键盘自动更正的结果。而“parens”只是“parentheses”(圆括号)的简称。
这是参数/形参的问题。
接收器实际上是可选的(但如果存在,括号是必需的),返回值也是如此。
形参可以为空,但括号必须存在。只需查看你发布的同一个例子中的 main 函数。
func main() {
fmt.Println("hello world")
}
func liner(functionName string, format string, a …interface{}) {
info := fmt.Sprintf(format, a…)
fmt.Printf(“[%s] %v\n”, functionName, info)
}
你能逐段解释一下这个吗?
liner(“main”, “debug line before define x”)
请同样解释一下这个。
我仍然更喜欢简单的版本:https://golang.org/pkg/fmt/#Printf
谢谢。 根据你的建议,我开始研究这个有用的链接。
Printf 根据格式说明符进行格式化并写入标准输出。它返回写入的字节数和遇到的任何写入错误。
什么是格式说明符? 什么是写入错误?
到目前为止,我的老师一直使用 Printf 来显示代码中特定项目的类型。这如何说明上述定义?
cherilexvold1974:
我不知道那是什么。我该如何自己发现它?
这只是我用 fmt 构建的一个简单函数。你可以通过阅读文档(fmt 包 - fmt - Go 包)来查看它们,或者先用谷歌搜索新工具开始。
cherilexvold1974:
我可以在自己的环境中运行这个吗?怎么做?
是的。这只是给你测试假设的一个想法。
我注意到有时函数没有接收器。参数是否总是跟在标识符后面?而返回值(在我看来,我见过没有返回值的函数),当它们存在时,是否总是跟在参数后面?有没有函数没有参数的情况?
在这个函数中:https://play.golang.org/p/AEbN6j-ClwC,三个项中的哪一个是 (y int)?
在解释刚才提到的代码时,老师说“我们将 x 传入这里 (foo),它被赋值给 y”。
我不理解这个概念:它被赋值给 y。
hollowaykeanho:
返回值是可选的。如果存在,
return关键字必须位于遵循输出规范的代码内部。否则,它用作退出函数的指令。
很有帮助
hollowaykeanho:
foo 函数的参数接收一个整数 y。当 main 调用 foo(x) 时,它会跳转到 foo 函数。由于输入是 x,当执行进入 foo 函数时,它作为 y 被传入。这被称为“按值传递”。
需要学习。我的大脑在抗拒,但我会让它安静下来!
hollowaykeanho:
“当 main 调用 foo(x) 时,它会跳转到 foo 函数。”……这些话真的很有帮助!
hollowaykeanho:
由于输入是 x,当执行进入 foo 函数时,它作为 y 被传入。这被称为“按值传递”。
谢谢!!!
由于以下所有项都用括号标记:(接收者)、(参数)和(返回值);当我看到函数中的这些括号时,如何区分它们,或者知道哪个是哪个?
这是由其书写方式决定的规则,并且第一个单词 func 表明该行是一个函数。因此,一个函数必须包含:
func (receiver)* name(parameters) (return)* {
...
}
* = 可选
参数是否总是出现在标识符之后?
是的。这是一个规则,就像拇指总是朝向你的胸部;小指总是远离身体。
返回值,当它们存在时(因为在我看来我见过没有返回值的函数)
返回值是可选的。如果存在,return 关键字必须出现在代码内部以返回输出。否则,它用作退出函数的指令。
这个概念:它被赋值给 y
foo 函数的参数接收一个整数 y。当 main 函数调用 foo(x) 时,它会跳转到 foo 函数。由于输入是 x,当执行进入 foo 函数时,它作为 y 被传入。这被称为“按值传递”。
hollowaykeanho:
mt.Sprintf
显式参数索引:
在 Printf、Sprintf 和 Fprintf 中,默认行为是每个格式化动词依次格式化调用中传入的参数。然而,紧接在动词之前的 [n] 符号表示要格式化的是第 n 个(索引从 1 开始)参数。在表示宽度或精度的 '*' 之前使用相同的符号,可以选择持有该值的参数索引。在处理完带括号的表达式 [n] 之后,除非另有指定,后续的动词将使用参数 n+1、n+2 等。
例如:
fmt.Sprintf("%[2]d %[1]d\n", 11, 22)
紧接在动词之前的符号
[n]之前?
fmt.?因为我看到Sprintf前面是fmt.。
Sprintf是动词吗?或者可能是
%? 还是d?
cherilexvold1974:
Sprintf 是动词吗? 或者是 %?还是 d?
哇,这也是一种解释方式。
我认为它指的是后续的参数。所以对于 Printf、Sprintf、Fprintf、Errorf 或任何以 f 结尾的打印输出函数,你可以指定字符串的格式(格式化后续的参数?)。然后,其余的参数是可选的。为了解释清楚,我仍然更喜欢简单的版本:fmt package - fmt - Go Packages
%??? 实际上被称为动词。使用 *****f 的过程被称为 字符串格式化。有许多类型的动词可供使用,如 Go by Example: String Formatting 所示。
动词的位置对应于后续的参数。这意味着,例如,name 和 money 根据动词的位置进行填充。
name money
🡣 🡣
fmt.Printf("DEBUG: %s, the return value is %d.\n", name, money)
不过要小心,如果你正在处理时间敏感的项目,不加仔细考虑就动态使用字符串格式化(特别是使用 %v 或 Go 特有的 %#v)可能会产生影响。否则,为了调试方便,请随意使用它。
hollowaykeanho:
fmt.Sprintf
我不知道那是什么。我该如何自己发现它?
我有可能自己运行这个吗?怎么做?
hollowaykeanho:
package main import ( "fmt" ) func liner(functionName string, format string, a ...interface{}) { info := fmt.Sprintf(format, a...) fmt.Printf("[%s] %v\n", functionName, info) } func main() { liner("main", "debug line before define x") x := 2 liner("main", "debug line before calling foo, x=%v", x) foo(x) liner("main", "debug line after foo, x=%v", x) fmt.Println(x) liner("main", "debug line after foo, after print, x=%v", x) } func foo(y int) { liner("foo", "debug line enters foo. y=%v", y) fmt.Println(y) liner("foo", "debug line, after print y, y=%v", y) y = 43 liner("foo", "debug line, after set y, y=%v", y) fmt.Println(y) liner("foo", "debug line, end foo, y=%v", y) }
cherilexvold1974:
func liner(functionName string, format string, a …interface{}) { info := fmt.Sprintf(format, a…) fmt.Printf(“[%s] %v\n”, functionName, info) }你能逐段解释一下这个吗?
当然可以。
这个函数的输入部分:functionName 顾名思义,就是一个名字。至于 format 和 a,它们就是我们所说的可变参数函数的参数,你可以向 a 中放入任意多个参数而无需修改代码。这里的 a 是一个切片。
其目的基本上是为了打印一条语句,就像你的 fmt.Printf(...) 一样。唯一的区别在于我添加了函数名前缀。
函数首先使用 fmt.Sprintf(...) 处理消息(format 和 a),它会将结果输出为一个字符串(保存到 info 中),而不是直接打印到控制台。然后,我使用 fmt.Printf(...) 将 functionName 作为前缀添加到消息前面。
有了这个新的 liner(...) 打印函数,我就可以轻松地在任何地方打印语句了。
cherilexvold1974:
liner(“main”, “debug line before define x”)请同样解释一下这个。
那么当我调用 liner(...) 时,我手动提供了函数名(因为显然这可能会使代码复杂化),以及我想要打印的消息。所以,我传入了:
main,它将传递给functionName变量。debug line before define x,它将传递给format。- 由于我没有第3个变量,可变参数
a是空的。
cherilexvold1974:
需要学习。我的大脑在抗拒,但我会让它安静下来!
一种简单的理解方法是在代码行前后注入调试信息。考虑以下修改后的代码:
package main
import (
"fmt"
)
func liner(functionName string, format string, a ...interface{}) {
info := fmt.Sprintf(format, a...)
fmt.Printf("[%s] %v\n", functionName, info)
}
func main() {
liner("main", "debug line before define x")
x := 2
liner("main", "debug line before calling foo, x=%v", x)
foo(x)
liner("main", "debug line after foo, x=%v", x)
fmt.Println(x)
liner("main", "debug line after foo, after print, x=%v", x)
}
func foo(y int) {
liner("foo", "debug line enters foo. y=%v", y)
fmt.Println(y)
liner("foo", "debug line, after print y, y=%v", y)
y = 43
liner("foo", "debug line, after set y, y=%v", y)
fmt.Println(y)
liner("foo", "debug line, end foo, y=%v", y)
}
输出中混入了 liner 函数的调试信息:
[main] debug line before define x
[main] debug line before calling foo, x=2
[foo] debug line enters foo. y=2
2
[foo] debug line, after print y, y=2
[foo] debug line, after set y, y=43
43
[foo] debug line, end foo, y=43
[main] debug line after foo, x=2
2
[main] debug line after foo, after print, x=2
main 函数由 [main] 标签指示,而 foo 函数由 [foo] 标签指示。
请注意,在 CPU 进入函数后,[foo] 标签的第一行中,y 的值与 x 相同,而不是 0。这就是你的讲师所说的意思,值是从 x 传递给了 y。
由于“按值传递”的特性,当 [foo] 退出后 [main] 恢复执行时,x 仍然是 2,而不是 43,就像你在 foo 函数内部修改的那样。这本质上意味着函数将值克隆到了 y 中,而不是直接修改 x。
注意:
你只能在处理非并发代码时进行这个
liner实验。否则,它是没有意义且难以理解的。对于并发场景,你需要使用不同的工具。


