声明
在 Go 语言中,函数声明通用语法如下:
func 函数名称( [参数列表] ) ([返回值列表])
{
执行语句
}
1.函数的声明以关键词 func开始 。
- 函数名和参数列表一起构成了函数签名。
- 参数列表定义在
(
和)
之间,声明一个参数的语法采用 参数名 参数类型 的方式,任意多个参数采用类似(parameter1 type, parameter2 type) 即(参数1 参数1的类型,参数2 参数2的类型)
的形式指定。参数是可选的,即函数可以不包含参数。参数就像一个占位符,这是参数被称为形参,当函数被调用时,将具体的值传递给参数,这个值被称为实际参数。 - Go函数支持多返回值。 函数可以有返回值也可以没有。如果一个函数在声明时,包含返回值列表,该函数必须以 return语句结尾。
下面的例子展示了一个简单的加减乘除运算的函数: ```go package main
import ( “errors” “fmt” )
func operation(a,b int,symbol string)(result int,err error){ switch symbol { case “+”: return a+b,nil case “-“: return a-b,nil case ““: return ab,nil case “/“: if b==0 { return 0,errors.New(“division by zero”) } return a/b,nil } return 0,errors.New(“unsupported symbol:”+symbol) }
func main() { r,_:=operation(2,3,”+”) fmt.Println(r) }
如果有连续若干个参数,它们的类型一致,那么我们无须一一罗列,只需在最后一个参数后添加该类型。例如,a int, b int可以简写为a,b int,所以示例函数也可写成
```go
func operation(a,b int,symbol string)(result int,err error)
函数调用
如果函数和调用不在同一个包(package)内,需要先通过import关键字将包引入–import “fmt”。函数Println()就属于包fmt。与其他语言不同的是在Go语言中函数名字的大小写不仅仅是风格,更直接体现了该函数的是私有函数还是公有函数。函数名首字母小写为private,大写为public。
package utiles
func Max(a,b int )(m int){
if a>b {
return a
}
return b
}
Max 函数如果被其他包调用 首字母需要大写
import (
"utiles"
"fmt"
)
func min(a ,b int) (m int){
if a>b {
return b
}
return a
}
func main(){
r := utiles.Max(2,4)
d := min(2,4)
fmt.Println(r,d)
}
多值返回
Go 语言支持一个函数可以有多个返回值,定义多值返回的返回参数列表时要使用”()”包裹
import (
"fmt"
)
func swap(a string,b string)(string,string){
return b,a;
}
func main() {
a,b :=swap("abc","def")
fmt.Println(a,b)
}
如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,用一个下划线 _ 来忽略这个返回值。如同一个占位符号,如果我们只关注第一个返回值则可以写成:
a, _ := swap("hello", "world")
如果关注第二返回值则可以写成:
_, b := swap("hello", "world")
函数参数
传值类型 | 说明 |
---|---|
值传递 | 调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 |
引用传递 | 调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 |
import (
"fmt"
)
func changeValue(a string){
a = "new value"
}
func change(a *string){
*a = "new value2"
}
func main() {
a := "old value"
changeValue(a)
fmt.Println(a)
change(&a)
fmt.Println(a)
/*
print result:
old value
new value2
*/
}
命名返回值
从函数中可以返回一个命名值。一旦命名了返回值,可以认为这些值在函数第一行就被声明为变量了。命名返回值作为结果形参(result parameters)被初始化为相应类型的零值,当需要返回的时候,我们只需要一条简单的不带参数的return语句。
func getResult(input int) (a int, b int) {
a = 2 * input
b = 3 * input
return
}
func main() {
n,m:= getResult(2);
fmt.Print(n,m)
}
函数中的 return 语句没有显式返回任何值。由于a 和 b 在函数声明中指定为返回值, 因此当遇到 return 语句时, 它们将自动从函数返回。
不定/可变参数
Go函数支持不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参,类型“…type“本质上是一个数组切片,也就是[]type,
func funcName(arg ...type) {
}
arg …type告诉Go这个函数接受不定数量的参数。在下面的例子中参数的类型全部是int。在函数体中,变量arg是一个int的slice.
在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片,特别注意的是在参数后加上“…”即可。
package main
import "fmt"
func min(a ...int) int {
if len(a) == 0 {
return 0
}
min := a[0]
for _, v := range a {
if min > v {
min = v
}
}
return min
}
func main() {
n := min(17, 12, 5, 9, 8)
fmt.Println(n)
arr := []int{26, 17, 35, 6, 18}
r := min(arr...)
fmt.Println(r)
m := min(arr[:3]...)
fmt.Println(m)
}
打印结果
5
6
17
那么如果函数的参数类型不一致需要使用interface{}
import (
"fmt"
"reflect"
)
func diffType(args ...interface{}){
for _, arg :=range args {
fmt.Println(arg)
fmt.Println(reflect.TypeOf(arg))
}
}
func main() {
diffType("test",1,12.5,[5]byte{1,3,4})
}
函数签名
函数签名就是函数定义首行去掉函数名,参数名,函数签名又叫函数类型
func add(a,b int) int{
return a+b
}
func main() {
fmt.Printf("%T\n",add)
}
打印结果如下
func(int, int) int
为了可读性,建议将复杂签名定义为函数类型,使用type定义函数类型
type CalcFunc func(x,y int) int
匿名函数
匿名函数,字面意思没有名字的函数,它只有函数逻辑体,而没有函数名称。 Go中函数是值类型,即可以作为参数,又可以作为返回值。
func(参数列表)(返回参数列表){
函数体
}
eg:
func(x,y int) int {
return x + y
}
匿名函数的调用类似跟我们通常使用的函数调用一样。eg:
package main
import "fmt"
func square(n int)int{
return n*n
}
func negative(n int)int{
return -n
}
func main() {
f :=square
fmt.Println(f(5))
f =negative
fmt.Println(f(5))
}
执行结果
25
-5
参数
函数作为参数
func Add(x, y int) int {
return x + y
}
func Sub(x, y int) int {
return x - y
}
type CalcFunc func(x,y int) int
func Operation(x, y int, calcFunc CalcFunc) int {
return calcFunc(x, y)
}
func main() {
sum := Operation(1, 2, Add)
difference := Operation(1, 2, Sub)
fmt.Println(sum,difference)
}
返回值
使用匿名函数作为返回值:
// 第一种写法
func add(x, y int) func() int {
f := func() int {
return x + y
}
return f
}
// 第二种写法
func add(x, y int) func() int {
return func() int {
return x + y
}
}
闭包
闭包是由函数及其相关引用环境组合而成的实体,一般通过在匿名函数中引用外部函数的局部变量或包全局变量构成。闭包=函数+引用环境
闭包对闭包外的环境引入是直接引用,编译器检测到闭包,会将闭包引用的外部变 量分配到堆上 。
如果函数返回的闭包引用了该函数的局部变量( 参数或函数内部变量):
(1)多次调用该函数,返回的多个闭包所引用的外部变量是多个副本,原因是每次调用函数都会为局部变量分配内存 。
package main
import (
"fmt"
)
func foo()func (b int) int{
var a int
return func(b int) int {
fmt.Println(&a,a)
a +=b
return a
}
}
func main() {
f :=foo()
fmt.Println(f(1))
fmt.Println(f(1))
g := foo()
fmt.Println(g(1))
fmt.Println(g(1))
}
打印结果如下
0xc000092008 0
1
0xc000092008 1
2
0xc000092030 0
1
0xc000092030 1
2
(2)用一个闭包函数多次,如果该闭包修改了其引用的外部变量,则每一次调用该闭包对该外部变量都有影响,因为闭包函数共享外部引用。
package main
import (
"fmt"
)
var a int
func foo()func (b int) int{
return func(b int) int {
fmt.Println(&a,a)
a +=b
return a
}
}
func main() {
f :=foo()
fmt.Println(f(1))
fmt.Println(f(1))
g := foo()
fmt.Println(g(1))
fmt.Println(g(1))
}
如果函数返回的闭包引用的是全局变量 a,则多次调用该函数返回的多个闭包引用的都是同一个a。 每次闭包调用都会影响全局变量a的值。上面程序执行结果如下:
0x1193980 0
1
0x1193980 1
2
0x1193980 2
3
0x1193980 3
4
函数式编程
特征:
函数是一等公民:参数,变量,返回值都可以是函数。
不可变性:不能有状态,只有常量和函数
函数只能有一个参数
func Sum() func(x int) int{
result := 0
return func(x int) int {
result +=x
return result
}
}
func main() {
a := Sum()
str := strings.Builder{}
str.WriteString("0")
for i :=1;i<=10;i++{
str.WriteString("+"+strconv.Itoa(i))
fmt.Printf("%s=%d\n",str.String(),a(i))
}
}
https://stackoverflow.com/questions/29002724/implement-ruby-style-cartesian-product-in-go