声明
在 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,所以示例函数也可写成```gofunc operation(a,b int,symbol string)(result int,err error)
函数调用
如果函数和调用不在同一个包(package)内,需要先通过import关键字将包引入–import “fmt”。函数Println()就属于包fmt。与其他语言不同的是在Go语言中函数名字的大小写不仅仅是风格,更直接体现了该函数的是私有函数还是公有函数。函数名首字母小写为private,大写为public。
package utilesfunc 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 valuenew value2*/}
命名返回值
从函数中可以返回一个命名值。一旦命名了返回值,可以认为这些值在函数第一行就被声明为变量了。命名返回值作为结果形参(result parameters)被初始化为相应类型的零值,当需要返回的时候,我们只需要一条简单的不带参数的return语句。
func getResult(input int) (a int, b int) {a = 2 * inputb = 3 * inputreturn}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 mainimport "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)}
打印结果
5617
那么如果函数的参数类型不一致需要使用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 mainimport "fmt"func square(n int)int{return n*n}func negative(n int)int{return -n}func main() {f :=squarefmt.Println(f(5))f =negativefmt.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) intfunc 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 mainimport ("fmt")func foo()func (b int) int{var a intreturn func(b int) int {fmt.Println(&a,a)a +=breturn a}}func main() {f :=foo()fmt.Println(f(1))fmt.Println(f(1))g := foo()fmt.Println(g(1))fmt.Println(g(1))}
打印结果如下
0xc000092008 010xc000092008 120xc000092030 010xc000092030 12
(2)用一个闭包函数多次,如果该闭包修改了其引用的外部变量,则每一次调用该闭包对该外部变量都有影响,因为闭包函数共享外部引用。
package mainimport ("fmt")var a intfunc foo()func (b int) int{return func(b int) int {fmt.Println(&a,a)a +=breturn 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 010x1193980 120x1193980 230x1193980 34
函数式编程
特征:
函数是一等公民:参数,变量,返回值都可以是函数。
不可变性:不能有状态,只有常量和函数
函数只能有一个参数
func Sum() func(x int) int{result := 0return func(x int) int {result +=xreturn 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
