Go语言现代web开发11 函数
函数是执行特定任务的程序的命名部分。我们通常编写函数是为了避免代码重复(通过将项目或包中重复的代码块移动到函数中)或使代码更具可读性(通过将执行特定任务的大量代码块移动到函数中)。在Go编程语言中,函数是用关键字func定义的。
参数是为获得结果而提供给函数的值。函数可以有零个或多个参数。下面是三个函数的例子,第一个没有参数的函数将返回数学常数pi的值,第二个有一个参数的函数将增加整数变量的值,第三个函数将返回作为参数传递的两个整数的和。
func pi() float64(){
return 3.14159
}
func inc(a int) int{
return a + 1
}
func add(a int, b int){
return a + b
}
返回值类型位于声明的末尾,在参数之后,在花括号之间的函数体之前。
如果我们有多个相同类型的参数(比如,在第三个add()函数的例子中),我们可以通过省略除最后一个变量之外的所有变量的类型来缩短参数声明(我们在变量声明中看到类似的事情,在类型之前我们有一个变量名称列表)。这里是简短声明的add()函数。
func add(a, b int) int {
return a + b
}
在前面的所有示例中,函数返回一个结果。但是函数可以返回多个结果。这里是一个函数返回两个结果的例子。
func swap(a, b int) (int int) {
return b, a
}
我们也可以创建不返回任何结果的函数。对于这些函数,我们只需要省略声明末尾的结果类型。main()函数是一个没有返回值的函数的好例子。
我们还可以命名返回值。命名的返回值将被视为在函数顶部声明的变量。对于命名返回值,可以使用不带参数的return,它将返回赋给命名返回值的值。无参数返回通常被称为无参数返回。这里是前面示例中的add()函数,具有命名的返回值。
func add(a, b int) (c int) {
c = a + b
return
}
函数就是值,所以它们可以像所有其他值一样传递。我们可以将函数用作其他函数的参数或返回值。这里有一个例子,我们传递一个函数作为另一个函数的参数。
func calc(fn func(int, int) int) int {
return fn(7, 18)
}
func main() {
add := func(a, b int) int {
return a + b
}
fmt.Println(calc(add))
}
这个例子将在标准输出中显示数字7和18的总和。正如我们所看到的,函数被赋给了一个变量名,但是我们没有定义函数名。这些函数被称为匿名函数。
在下一个示例中,函数multiply()将根据参数参数的值返回一个函数。如果传递的值是偶数,则返回一个与整数值重复的函数,否则返回一个将整数值翻三倍的函数。
func multiply(param int) func(int) int {
if param % 2 == 0 {
return func(a int) int {
return a * 2
}
} else {
return func(a int) int {
return a * 3
}
}
}
func main(){
double := multiply(2)
triple := multiply(3)
fmt.Println(double(5), triple(5))
}
在之前的案例中,标准输出会显示10和15。
参数是按值传递的,因此每次调用函数时,都会创建所传递参数的新副本。该函数将与该副本一起工作,因此如果我们忘记返回updatedcopy,则更改将不起作用。但是如果我们传递一个指针作为参数,就会创建一个内存地址的副本,这样所有的更改都将在原始指针指向的原始变量上生效。为了避免不必要的复制和内存消耗,使用指针参数是一种很好的做法,特别是对于大型结构体。
下面的示例显示了值参数和指针参数之间的区别。带有值参数double()的函数会将传递的参数的副本相乘,因此第一个Println()将在标准输出中显示值5。另一方面,函数doublePointer()将把存储在指针引用的地址上的值乘以,因此第二次调用Println()将显示值10。
func double (a int) {
a = a * 2
}
func doublePointer(a *int){
*a = *a *2
}
func main(){
a := 5
double(a)
fmt.Println(a)
doublePointer(&a)
fmt.Println(a)
}
如果我们稍微修改函数double()和doublePointer(),增加Println()调用作为每个函数的最后一个表达式,标准输出将显示以下值:10 5 10 10 10。
正如我们所看到的,值的副本将被正确更新,但结果将丢失。这是一个修改后的函数。
func double(a int) {
a = a * 2
fmt.Println(a)
}
func doublePointer(a *int){
*a = *a * 2
fmt.Println(*a)
}
带有slice参数的函数很有趣。切片具有指向底层数组的指针,这意味着即使切片作为值传递,切片的内容也可以更改,但容量和长度不能更改。在下面的示例中,在modify()函数中调用Println()将显示一个包含修改后的第一个元素和末尾附加的新元素的切片,而在主函数中的第二个Println()将只显示包含修改后的第一个元素的切片。
func modify(s []int) {
s[0] = 4
s = append(s, 5)
fmt.Println(s)
}
func main(){
s := []int{1, 2, 3}
modify(s)
fmt.Println(s)
}
函数可以从函数体外部引用变量,这些函数称为闭包。每个函数值都有自己的引用变量副本,可以访问它并为它赋值。我们可以说闭包被绑定到一个变量。下面是一个返回闭包的函数示例。正如我们所看到的,匿名函数受变量a的约束。
func calc() func() int {
a := 0
return func() int() {
a += 1
return a
}
}
=======
Functions are named sections of programs that perform specific tasks. We usually write functions to avoid code repetition (by moving a block of code that is repeated through a project or package into a function) or to make the code more readable (by moving a huge block of code that performs a specific task into a function). In the Go programming language, functions are defined with the the keyword func.
函数是执行特定任务的程序的命名部分。我们通常编写函数是为了避免代码重复(通过将项目或包中重复的代码块移动到函数中)或使代码更具可读性(通过将执行特定任务的大量代码块移动到函数中)。在Go编程语言中,函数是用关键字func定义的。
Arguments are the values provided to a function in order to obtain the result. The function can have zero or more arguments. Here are examples of three functions, the first one without arguments will return the value for the mathematcal constant pi, the second one with one argument will increment the value of the integer variable, and the third one will return the sum of two integers passed as arguments.
参数是为获得结果而提供给函数的值。函数可以有零个或多个参数。下面是三个函数的例子,第一个没有参数的函数将返回数学常数pi的值,第二个有一个参数的函数将增加整数变量的值,第三个函数将返回作为参数传递的两个整数的和。
func pi() float64(){
return 3.14159
}
func inc(a int) int{
return a + 1
}
func add(a int, b int){
return a + b
}
The return value type is at the end of the declaration after arguments and before the function body that is between the curly brackets.
返回值类型位于声明的末尾,在参数之后,在花括号之间的函数体之前。
If we have multiple arguments of the same type (like, in the third example with add() function), we can shorten the argument declaration by omitting the type for all variables except the last one (we see a similar thing for the declaration of variables, where we hadd a list of variabl names before type). Here, is add() function with shortened declaration.
如果我们有多个相同类型的参数(比如,在第三个add()函数的例子中),我们可以通过省略除最后一个变量之外的所有变量的类型来缩短参数声明(我们在变量声明中看到类似的事情,在类型之前我们有一个变量名称列表)。这里是简短声明的add()函数。
func add(a, b int) int {
return a + b
}
In all previous examples, functions returned one result. But functions can return multiple results. Here, is an example of a function returns two results.
在前面的所有示例中,函数返回一个结果。但是函数可以返回多个结果。这里是一个函数返回两个结果的例子。
func swap(a, b int) (int int) {
return b, a
}
We can also create functions that do not return any results. For these functions, we just need to omit the result type at the end of the declaration. The main() function is a good example of a function without a return value.
我们也可以创建不返回任何结果的函数。对于这些函数,我们只需要省略声明末尾的结果类型。main()函数是一个没有返回值的函数的好例子。
We can also name return values. Named return values will be treated as variables declared on the top of the function. With named return values, we can use return without arguments, which will return values assigned to named return values. Argument-less return is often referred to as a naked return. Here, is the add() function from previous examples with named return values.
我们还可以命名返回值。命名的返回值将被视为在函数顶部声明的变量。对于命名返回值,可以使用不带参数的return,它将返回赋给命名返回值的值。无参数返回通常被称为无参数返回。这里是前面示例中的add()函数,具有命名的返回值。
func add(a, b int) (c int) {
c = a + b
return
}
Functions are the values, so they can be passed around like all other values. We can use functions as arguments in other functions or as return values. Here, is an example where we pass one function as an argument of another function.
函数就是值,所以它们可以像所有其他值一样传递。我们可以将函数用作其他函数的参数或返回值。这里有一个例子,我们传递一个函数作为另一个函数的参数。
func calc(fn func(int, int) int) int {
return fn(7, 18)
}
func main() {
add := func(a, b int) int {
return a + b
}
fmt.Println(calc(add))
}
This example will display the sum of numbers 7 and 18 on the standard output. As we can see, the function is assigned to a variable name, but we do not define the function name. These functions are called anonymous functions.
这个例子将在标准输出中显示数字7和18的总和。正如我们所看到的,函数被赋给了一个变量名,但是我们没有定义函数名。这些函数被称为匿名函数。
In the next example, function multiply() will return a function based on the value of param argument. If the passed value is an even number, a function that duplicates the integer value will be returned, otherwise, a function that triples the integer value will be returned.
在下一个示例中,函数multiply()将根据参数参数的值返回一个函数。如果传递的值是偶数,则返回一个与整数值重复的函数,否则返回一个将整数值翻三倍的函数。
func multiply(param int) func(int) int {
if param % 2 == 0 {
return func(a int) int {
return a * 2
}
} else {
return func(a int) int {
return a * 3
}
}
}
func main(){
double := multiply(2)
triple := multiply(3)
fmt.Println(double(5), triple(5))
}
In the previous example, values 10 and 15 will be displayed on the standard output.
在之前的案例中,标准输出会显示10和15。
Arguments are passed by balue, so each time a function is called, a new copy of the passed argument is created. The function will work with that copy, so fi we forgot to return the updatedcopy, changes will have no effect. But if we pass a pointer as an argument, a copy of the memory address will be created so that all chagnes will take effect on the original variable, pointed by the original pointer. It’s good practice to use pointer arguments, especially for large structures in order to avoid unneccessary copying and memory consumption.
参数是按值传递的,因此每次调用函数时,都会创建所传递参数的新副本。该函数将与该副本一起工作,因此如果我们忘记返回updatedcopy,则更改将不起作用。但是如果我们传递一个指针作为参数,就会创建一个内存地址的副本,这样所有的更改都将在原始指针指向的原始变量上生效。为了避免不必要的复制和内存消耗,使用指针参数是一种很好的做法,特别是对于大型结构体。
The following example shows the difference between value and pointer arguments. Function with value argument double() will multiply copy of the passed argument, so the first Println() will display value 5 on standard output. On the other hand, the function doublePointer() will multiply the value stored on the address referenced by the pointer, so the second call of Println() will display value 10.
下面的示例显示了值参数和指针参数之间的区别。带有值参数double()的函数会将传递的参数的副本相乘,因此第一个Println()将在标准输出中显示值5。另一方面,函数doublePointer()将把存储在指针引用的地址上的值乘以,因此第二次调用Println()将显示值10。
func double (a int) {
a = a * 2
}
func doublePointer(a *int){
*a = *a *2
}
func main(){
a := 5
double(a)
fmt.Println(a)
doublePointer(&a)
fmt.Println(a)
}
If we slightly modify functions double() and doublePointer() with the addition of Println() call as the last expression in each function, the following values will be displayed on standard output: 10 5 10 10.
如果我们稍微修改函数double()和doublePointer(),增加Println()调用作为每个函数的最后一个表达式,标准输出将显示以下值:10 5 10 10 10。
As we can seee, a copy of the value will be properly updated, but that result will be lost. Here are an modified function.
正如我们所看到的,值的副本将被正确更新,但结果将丢失。这是一个修改后的函数。
func double(a int) {
a = a * 2
fmt.Println(a)
}
func doublePointer(a *int){
*a = *a * 2
fmt.Println(*a)
}
Functions with slice arguments are interesting ones. Slices have pointer to underlying arrays which means that the content of the slice can be changed even if the slice sis passed as value, but capacity and length cannot. In the following example, the call of Println() inside modfy() function will display a slice with the modified first element and new element appended at the end, while the second Println() inside the main function will display a slice only with the modified first element.
带有slice参数的函数很有趣。切片具有指向底层数组的指针,这意味着即使切片作为值传递,切片的内容也可以更改,但容量和长度不能更改。在下面的示例中,在modify()函数中调用Println()将显示一个包含修改后的第一个元素和末尾附加的新元素的切片,而在主函数中的第二个Println()将只显示包含修改后的第一个元素的切片。
func modify(s []int) {
s[0] = 4
s = append(s, 5)
fmt.Println(s)
}
func main(){
s := []int{1, 2, 3}
modify(s)
fmt.Println(s)
}
Functions can reference a varable from outside their body, these functions are called closures. Each function value has its own copy of referenced variable and can access it and assign values to it. We can say that closure is bound to a variable. Here is an example of the function that returns a closure. As we can see, the anonymous function is bounded with variable a.
函数可以从函数体外部引用变量,这些函数称为闭包。每个函数值都有自己的引用变量副本,可以访问它并为它赋值。我们可以说闭包被绑定到一个变量。下面是一个返回闭包的函数示例。正如我们所看到的,匿名函数受变量a的约束。
func calc() func() int {
a := 0
return func() int() {
a += 1
return a
}
}