声明

在 Go 语言中,函数声明通用语法如下:

  1. func 函数名称( [参数列表] ) ([返回值列表])
  2. {
  3. 执行语句
  4. }

1.函数的声明以关键词 func开始 。

  1. 函数名和参数列表一起构成了函数签名。
  2. 参数列表定义在 () 之间,声明一个参数的语法采用 参数名 参数类型 的方式,任意多个参数采用类似 (parameter1 type, parameter2 type) 即(参数1 参数1的类型,参数2 参数2的类型)的形式指定。参数是可选的,即函数可以不包含参数。参数就像一个占位符,这是参数被称为形参,当函数被调用时,将具体的值传递给参数,这个值被称为实际参数。
  3. 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) }

  1. 如果有连续若干个参数,它们的类型一致,那么我们无须一一罗列,只需在最后一个参数后添加该类型。例如,a int, b int可以简写为a,b int,所以示例函数也可写成
  2. ```go
  3. func operation(a,b int,symbol string)(result int,err error)

函数调用

如果函数和调用不在同一个包(package)内,需要先通过import关键字将包引入–import “fmt”。函数Println()就属于包fmt。与其他语言不同的是在Go语言中函数名字的大小写不仅仅是风格,更直接体现了该函数的是私有函数还是公有函数。函数名首字母小写为private,大写为public。

  1. package utiles
  2. func Max(a,b int )(m int){
  3. if a>b {
  4. return a
  5. }
  6. return b
  7. }

Max 函数如果被其他包调用 首字母需要大写

  1. import (
  2. "utiles"
  3. "fmt"
  4. )
  5. func min(a ,b int) (m int){
  6. if a>b {
  7. return b
  8. }
  9. return a
  10. }
  11. func main(){
  12. r := utiles.Max(2,4)
  13. d := min(2,4)
  14. fmt.Println(r,d)
  15. }

多值返回

Go 语言支持一个函数可以有多个返回值,定义多值返回的返回参数列表时要使用”()”包裹

  1. import (
  2. "fmt"
  3. )
  4. func swap(a string,b string)(string,string){
  5. return b,a;
  6. }
  7. func main() {
  8. a,b :=swap("abc","def")
  9. fmt.Println(a,b)
  10. }

如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,用一个下划线 _ 来忽略这个返回值。如同一个占位符号,如果我们只关注第一个返回值则可以写成:

  1. a, _ := swap("hello", "world")

如果关注第二返回值则可以写成:

  1. _, b := swap("hello", "world")

函数参数

传值类型 说明
值传递 调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递 调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
  1. import (
  2. "fmt"
  3. )
  4. func changeValue(a string){
  5. a = "new value"
  6. }
  7. func change(a *string){
  8. *a = "new value2"
  9. }
  10. func main() {
  11. a := "old value"
  12. changeValue(a)
  13. fmt.Println(a)
  14. change(&a)
  15. fmt.Println(a)
  16. /*
  17. print result:
  18. old value
  19. new value2
  20. */
  21. }

命名返回值

从函数中可以返回一个命名值。一旦命名了返回值,可以认为这些值在函数第一行就被声明为变量了。命名返回值作为结果形参(result parameters)被初始化为相应类型的零值,当需要返回的时候,我们只需要一条简单的不带参数的return语句。

  1. func getResult(input int) (a int, b int) {
  2. a = 2 * input
  3. b = 3 * input
  4. return
  5. }
  6. func main() {
  7. n,m:= getResult(2);
  8. fmt.Print(n,m)
  9. }

函数中的 return 语句没有显式返回任何值。由于a 和 b 在函数声明中指定为返回值, 因此当遇到 return 语句时, 它们将自动从函数返回。

不定/可变参数

Go函数支持不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参,类型“…type“本质上是一个数组切片,也就是[]type,

  1. func funcName(arg ...type) {
  2. }

arg …type告诉Go这个函数接受不定数量的参数。在下面的例子中参数的类型全部是int。在函数体中,变量arg是一个int的slice.
在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片,特别注意的是在参数后加上“…”即可。

  1. package main
  2. import "fmt"
  3. func min(a ...int) int {
  4. if len(a) == 0 {
  5. return 0
  6. }
  7. min := a[0]
  8. for _, v := range a {
  9. if min > v {
  10. min = v
  11. }
  12. }
  13. return min
  14. }
  15. func main() {
  16. n := min(17, 12, 5, 9, 8)
  17. fmt.Println(n)
  18. arr := []int{26, 17, 35, 6, 18}
  19. r := min(arr...)
  20. fmt.Println(r)
  21. m := min(arr[:3]...)
  22. fmt.Println(m)
  23. }

打印结果

  1. 5
  2. 6
  3. 17

那么如果函数的参数类型不一致需要使用interface{}

  1. import (
  2. "fmt"
  3. "reflect"
  4. )
  5. func diffType(args ...interface{}){
  6. for _, arg :=range args {
  7. fmt.Println(arg)
  8. fmt.Println(reflect.TypeOf(arg))
  9. }
  10. }
  11. func main() {
  12. diffType("test",1,12.5,[5]byte{1,3,4})
  13. }

函数签名

函数签名就是函数定义首行去掉函数名,参数名,函数签名又叫函数类型

  1. func add(a,b int) int{
  2. return a+b
  3. }
  4. func main() {
  5. fmt.Printf("%T\n",add)
  6. }

打印结果如下

  1. func(int, int) int

为了可读性,建议将复杂签名定义为函数类型,使用type定义函数类型

  1. type CalcFunc func(x,y int) int

匿名函数

匿名函数,字面意思没有名字的函数,它只有函数逻辑体,而没有函数名称。 Go中函数是值类型,即可以作为参数,又可以作为返回值。

  1. func(参数列表)(返回参数列表){
  2. 函数体
  3. }

eg:

  1. func(xy int) int {
  2. return x + y
  3. }

匿名函数的调用类似跟我们通常使用的函数调用一样。eg:

  1. package main
  2. import "fmt"
  3. func square(n int)int{
  4. return n*n
  5. }
  6. func negative(n int)int{
  7. return -n
  8. }
  9. func main() {
  10. f :=square
  11. fmt.Println(f(5))
  12. f =negative
  13. fmt.Println(f(5))
  14. }

执行结果

  1. 25
  2. -5

参数

函数作为参数

  1. func Add(x, y int) int {
  2. return x + y
  3. }
  4. func Sub(x, y int) int {
  5. return x - y
  6. }
  7. type CalcFunc func(x,y int) int
  8. func Operation(x, y int, calcFunc CalcFunc) int {
  9. return calcFunc(x, y)
  10. }
  11. func main() {
  12. sum := Operation(1, 2, Add)
  13. difference := Operation(1, 2, Sub)
  14. fmt.Println(sum,difference)
  15. }

返回值

使用匿名函数作为返回值:

  1. // 第一种写法
  2. func add(x, y int) func() int {
  3. f := func() int {
  4. return x + y
  5. }
  6. return f
  7. }
  8. // 第二种写法
  9. func add(x, y int) func() int {
  10. return func() int {
  11. return x + y
  12. }
  13. }

闭包

闭包是由函数及其相关引用环境组合而成的实体,一般通过在匿名函数中引用外部函数的局部变量或包全局变量构成。闭包=函数+引用环境
闭包对闭包外的环境引入是直接引用,编译器检测到闭包,会将闭包引用的外部变 量分配到堆上 。

如果函数返回的闭包引用了该函数的局部变量( 参数或函数内部变量):
(1)多次调用该函数,返回的多个闭包所引用的外部变量是多个副本,原因是每次调用函数都会为局部变量分配内存 。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func foo()func (b int) int{
  6. var a int
  7. return func(b int) int {
  8. fmt.Println(&a,a)
  9. a +=b
  10. return a
  11. }
  12. }
  13. func main() {
  14. f :=foo()
  15. fmt.Println(f(1))
  16. fmt.Println(f(1))
  17. g := foo()
  18. fmt.Println(g(1))
  19. fmt.Println(g(1))
  20. }

打印结果如下

  1. 0xc000092008 0
  2. 1
  3. 0xc000092008 1
  4. 2
  5. 0xc000092030 0
  6. 1
  7. 0xc000092030 1
  8. 2

(2)用一个闭包函数多次,如果该闭包修改了其引用的外部变量,则每一次调用该闭包对该外部变量都有影响,因为闭包函数共享外部引用。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. var a int
  6. func foo()func (b int) int{
  7. return func(b int) int {
  8. fmt.Println(&a,a)
  9. a +=b
  10. return a
  11. }
  12. }
  13. func main() {
  14. f :=foo()
  15. fmt.Println(f(1))
  16. fmt.Println(f(1))
  17. g := foo()
  18. fmt.Println(g(1))
  19. fmt.Println(g(1))
  20. }

如果函数返回的闭包引用的是全局变量 a,则多次调用该函数返回的多个闭包引用的都是同一个a。 每次闭包调用都会影响全局变量a的值。上面程序执行结果如下:

  1. 0x1193980 0
  2. 1
  3. 0x1193980 1
  4. 2
  5. 0x1193980 2
  6. 3
  7. 0x1193980 3
  8. 4

函数式编程

特征:
函数是一等公民:参数,变量,返回值都可以是函数。
不可变性:不能有状态,只有常量和函数
函数只能有一个参数

  1. func Sum() func(x int) int{
  2. result := 0
  3. return func(x int) int {
  4. result +=x
  5. return result
  6. }
  7. }
  8. func main() {
  9. a := Sum()
  10. str := strings.Builder{}
  11. str.WriteString("0")
  12. for i :=1;i<=10;i++{
  13. str.WriteString("+"+strconv.Itoa(i))
  14. fmt.Printf("%s=%d\n",str.String(),a(i))
  15. }
  16. }

https://stackoverflow.com/questions/29002724/implement-ruby-style-cartesian-product-in-go