文档章节

Go笔记-函数

漂泊尘埃
 漂泊尘埃
发布于 2017/02/28 10:35
字数 1789
阅读 3
收藏 0

函数

在Go中函数也是一种变量,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型

type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])

Go不支持函数重载,也不支持泛型,因为这会影响性能。

不支持嵌套(nested)、重载(overload)、默认参数(default paremeter)。

函数定义

package main

import (
	"errors"
	"fmt"
)

func main() {
	ret, err := Add(1, 1)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("ret=", ret)
	}
}

func Add(a int, b int) (ret int, err error) {
	if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法
		err = errors.New("Should be non-negative numbers!")
		return
	}
	return a + b, nil
}

返回值列表也要用()括起来


返回值是被初始化成零值的:

func test() (a, b int) {
	b = 1
	return
}
调用:
a, b := test()
fmt.Println(a, b) // 0 1

返回值列表可以不加名称,这样每次return都必须返回所有的值,而且nil不能用作int这样的类型

func Add(a, b int) (int, error) {
	if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法
		return -1, errors.New("Should be non-negative numbers!")
	}
	return a + b, nil
}

如果参数列表中若干个相邻的参数类型的相同,比如上面例子中的a和b,则可以在参数列表中省略前面变量的类型声明

func Add(a, b int) (ret int, err error)

返回值列表也同样


如果只有一个返回值,可以这样

func Add(a, b int) int{ 
	// ... 
} 

命名规范

小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。

这个规则也适用于类型和变量的可见性

不定参数

func main() {
	myfunc(1, 2, 3)
}

func myfunc(args ...int) {
	for _, v := range args {
		fmt.Printf("%v\t", v)
	}
}
  • 形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。它是一个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的机会
  • 从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type

不定参数的传递

funcmyfunc(args ...int) { 
	// 按原样传递
	myfunc3(args...) 
	// 传递片段,实际上任意的int slice都可以传进去
	myfunc3(args[1:]...) 
}

任意类型的不定参数

下面是Go语言标准库中fmt.Printf()的函数原型:

funcPrintf(format string, args ...interface{}) { 
 // ... 
}
func main() {
	var v1 int = 1
	var v2 int64 = 234
	var v3 string = "hello"
	var v4 float32 = 1.234
	myfunc(v1, v2, v3, v4)
}

func myfunc(args ...interface{}) {
	for _, arg := range args {
		switch arg.(type) {
		case int:
			fmt.Println(arg, "is an int value.")
		case string:
			fmt.Println(arg, "is a string value.")
		case int64:
			fmt.Println(arg, "is an int64 value.")
		default:
			fmt.Println(arg, "is an unknown type.")
		}
	}
}

输出结果:

1 is an int value.
234 is an int64 value.
hello is a string value.
1.234 is an unknown type.

如果是传递数组:

func main() {
	values := []string{"a", "b"}
	DO("command", values...)

}

func DO(commandName string, args ...interface{}) {
}

则会报错:

cannot use values (type []string) as type []interface {} in argument to DO。

var intf []interface{}
var strs []string = []string{"a", "b"}
intf = strs // 这个也是会报错的。

匿名函数

在Go里面,函数可以像普通变量一样被传递或使用

赋值给变量

f := func(a, b int) bool {
	return a > b
}
fmt.Println(f(1, 3)) // false

另一种方式是:(var f (func(a, b int) bool)括号里是类型)

var f (func(a, b int) bool) = func(a, b int) bool {
	return a > b
}

直接执行

func(a, b int) {
	fmt.Println(a > b)
}(1, 3)

闭包

Go语言中的闭包同样也会引用到函数外的变量。闭包的实现确保只要闭包还被使用,那么 被闭包引用的变量会一直存在

	var j int = 5
	f := func() func() { // 返回一个函数
		var i int = 10
		return func() {
			fmt.Printf("i,j:%d,%d\n", i, j)
		}
	}() // 执行匿名函数,这样f引用的就是return的func

	f() // i,j:10,5

	j *= 2

	f() // i,j:10,10

变量a指向的闭包函数引用了局部变量i和j,i的值被隔离,在闭包外不能被修改,改变j的值以后,再次调用a,发现结果是修改过的值。

在变量a指向的闭包函数中,只有内部的匿名函数才能访问变量i,而无法通过其他途径访问到,因此保证了i的安全性。

strings.Title()中使用到了闭包。

另一个例子:

func main() {
	//stringsStudy.StudyCount()
	var f = Adder()
	fmt.Print(f(1), " - ")
	fmt.Print(f(20), " - ")
	fmt.Print(f(300))
}
func Adder() func(int) int {
	var x int
	return func(delta int) int {
		x += delta
		return x
	}
}

输出:1 - 21 - 321

闭包函数保存并积累其中的变量的值,不管外部函数退出与否,它都能够继续操作外部函数中的局部变量。

这些局部变量同样可以是参数,例如 Adder(as int)中的as。

工厂函数

一个返回值为另一个函数的函数可以被称之为工厂函数,这在您需要创建一系列相似的函数的时候非常有用:书写一个工厂函数而不是针对每种情况都书写一个函数。下面的函数演示了如何动态返回追加后缀的函数:

func MakeAddSuffix(suffix string) func(string) string {
    return func(name string) string {
        if !strings.HasSuffix(name, suffix) {
            return name + suffix
        }
        return name
    }
}

现在,我们可以生成如下函数:

addBmp := MakeAddSuffix(“.bmp”)
addJpeg := MakeAddSuffix(“.jpeg”)

然后调用它们:

addBmp(“file”) // returns: file.bmp
addJpeg(“file”) // returns: file.jpeg

可以返回其它函数的函数和接受其它函数作为参数的函数均被称之为高阶函数,是函数式语言的特点。

使用闭包调试

当您在分析和调试复杂的程序时,无数个函数在不同的代码文件中相互调用,如果这时候能够准确地知道哪个文件中的具体哪个函数正在执行,对于调试是十分有帮助的。您可以使用 runtime 或 log 包中的特殊函数来实现这样的功能。包 runtime 中的函数 Caller() 提供了相应的信息,因此可以在需要的时候实现一个 where() 闭包函数来打印函数执行的位置:

import (
	"fmt"
	"log"
	"runtime"
)

func main() {
	where := func() {
		_, file, line, _ := runtime.Caller(1)
		log.Printf("%s:%d", file, line)
	}
	where()
	doSomeThing()
	where()
	doSomeThing()
	where()
}

func doSomeThing() {
	for i := 0; i < 10; i++ {
		j := 5
		j += i
		if j > 20 {
			fmt.Println("j=", j)
		}
	}
}

输出:

2014/12/06 15:45:16 E:/Go/projects/src/study/main.go:14
2014/12/06 15:45:16 E:/Go/projects/src/study/main.go:16
2014/12/06 15:45:16 E:/Go/projects/src/study/main.go:18

这是where()的位置,Caller(1)中的1表示堆栈中第1个的信息,第0个是本函数,第1个就是上一个函数,这是是main函数中的where()位置。

传递指针

func Add(i *int) {
	*i++
}

func main() {
	i := 4
	Add(&i)
	fmt.Println(i) // 5
}

Go语言中string,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)

让函数直接实现接口

type Tester interface {
	Do()
}

type FuncDo func()

func (f FuncDo) Do() { f() }

func main() {
	var t Tester = FuncDo(func() {
		fmt.Println("hello world")
	})
	t.Do()
}

© 著作权归作者所有

共有 人打赏支持
漂泊尘埃

漂泊尘埃

粉丝 5
博文 35
码字总数 70992
作品 0
朝阳
MySQL入门很简单-学习笔记 - 索引页

索引一下,方便阅读,后面有完整的PDF下载地址 MySQL入门很简单-学习笔记 - 第1 章 数据库概述 MySQL入门很简单-学习笔记 - 第 2 章 Windows平台下安装与配置MySQL MySQL入门很简单-学习笔...

晨曦之光
2012/03/09
0
0
进程的创建-multiprocessing

进程的创建-multiprocessing multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情 2个while...

祈澈姑娘
2017/12/27
0
0
Mini 容器学习笔记1——环境搭建(基础篇)

一. 环境下载 到Mini 容器的官方网站下载NLite框架的二进制文件,下载并解压后就可以了。 我们使用NLite框架需要用到下面的文件: NLite.dll(必要) 二. 建立NLite应用程序 新建一个控制台应用...

netcasewqs
2011/08/26
0
0
Oracle笔记 目录索引

Oracle笔记 一、oracle的安装、sqlplus的使用 Oracle笔记 二、常用dba命令行 Oracle笔记 三、function 、selectOracle笔记 四、增删改、事务 Oracle笔记 五、创建表、约束、视图、索引、序列...

ibm_hoojo
2011/05/03
0
0
【MoreWindows工作笔记9】OleGetClipboard 访问剪切板的文本内容

剪贴板系列目前有四篇,包括访问剪贴板上的文本数据,查看复制文件时剪贴板上的文件数据,枚举剪贴板上数据格式以及监视剪贴板。 目录如下: 1.《【MoreWindows工作笔记9】OleGetClipboard...

morewindows
2013/12/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

基于TP5的微信的公众号获取登录用户信息

之前讲过微信的公众号自动登录的菜单配置,这次记录一下在TP5项目中获取自动登录的用户信息并存到数据库的操作 基本的流程为:微信设置自动登录的菜单—>访问的URL指定的函数里获取用户信息—...

月夜中徘徊
55分钟前
0
0
youTrack

package jetbrains.teamsys.license.runtime; 计算lis package jetbrains.ring.license.reader; 验证lis 安装后先不要生成lis,要把相关文件进行替换 ring-license-checker-1.0.41.jar char......

max佩恩
58分钟前
0
0
12.17 Nginx负载均衡

Nginx负载均衡 下面的dig看到可以返回2个IP,就是解析出来的IP,这样我们可以做负载均衡。 dig www.qq.com 1.vim /usr/local/nginx/conf/vhost/fuzai.conf 2.添加如下配置 upstream qq //定义...

芬野de博客
今天
0
0
SSE(Server Send Event 服务端发送事件)

package com.example.demo.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframe......

Canaan_
今天
0
0
jvm调优

1.jvm运行模式 client模式:启动快,占用内存少,jit编译器生成代码的速度也更快. server模式:主要优势在于代码优化功能,这个功能对于服务器应用而言尤其重要. tiered server模式:结合了client与...

Funcy1122
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部