文档章节

Go笔记-函数

漂泊尘埃
 漂泊尘埃
发布于 2017/02/28 10:35
字数 1789
阅读 3
收藏 0
点赞 0
评论 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

进程的创建-multiprocessing

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

祈澈姑娘 ⋅ 2017/12/27 ⋅ 0

Mini 容器学习笔记1——环境搭建(基础篇)

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

netcasewqs ⋅ 2011/08/26 ⋅ 0

Oracle笔记 目录索引

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

ibm_hoojo ⋅ 2011/05/03 ⋅ 0

perl简洁编程,《Perl语言入门(第五版)》读书笔记

整理一下好多年前放在hi.baidu上的笔记! perl简洁编程,《Perl语言入门(第五版)》读书笔记20110316! 书很早就买了,简单翻翻就扔下了! 最近买了ipad,良好的阅读体验,用来看书,感觉书...

daweilang ⋅ 2016/09/14 ⋅ 0

【MoreWindows工作笔记9】OleGetClipboard 访问剪切板的文本内容

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

morewindows ⋅ 2013/12/31 ⋅ 0

JavaScript 学习入门(一)

JavaScript简介   JavaScript 与 Java 是两种完全不同的语言,无论在概念上还是设计上。   Java(由 Sun 发明)是更复杂的编程语言。   ECMA-262 是 JavaScript 标准的官方名称。JavaS...

邪人君子 ⋅ 2017/12/26 ⋅ 0

Golang学习笔记目录

Golang 介绍 Go语言是谷歌2009发布的第二款开源编程语言。 Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。...

ChainZhang ⋅ 2017/12/26 ⋅ 0

Mini 容器学习目录

Mini 容器学习笔记1——环境搭建(基础篇) Mini 容器学习笔记2——组件元数据(基础篇) Mini 容器学习笔记3——组件的注册(基础篇) Mini 容器学习笔记4——组件的生命周期(应用篇) Mini 容器学...

netcasewqs ⋅ 2011/08/26 ⋅ 0

【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现

本系列文章由zhmxy555编写,转载请注明出处。 http://blog.csdn.net/zhmxy555/article/details/7376281 作者:毛星云 邮箱: happylifemxy@qq.com 欢迎邮件交流编程心得 "透明动画”是游戏中...

长平狐 ⋅ 2012/11/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

2018上海云栖大会workshop-日志数据采集与分析对接

摘要: 日志数据采集与分析对接 课程描述 通过日志服务采集用户、数据库、业务等访问数据。演示对于业务日志分析与处理,程序日志查询与监控,打通日志与数据仓库对接案例。 日志种类 网站访...

猫耳m ⋅ 25分钟前 ⋅ 0

SpringMVC中的ContentNegotiatingViewResolver配置

pom.xml <properties><commons-lang.version>2.6</commons-lang.version><slf4j.version>1.7.6</slf4j.version><spring.version>4.1.3.RELEASE</spring.version> <jack......

颖伙虫 ⋅ 27分钟前 ⋅ 0

Spring Boot 2.0正式发布,升还是不升呢?

Spring几乎是每一位Java开发人员都耳熟能详的开发框架,不论您是一名初出茅庐的程序员还是经验丰富的老司机,都会对其有一定的了解或使用经验。在现代企业级应用架构中,Spring技术栈几乎成为...

java高级架构牛人 ⋅ 28分钟前 ⋅ 0

打包时隐藏打印日志

1.打包时不允许项目中有打印的日志,可以在pch中加入下面这段代码即可 #ifdef DEBUG# define Log(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA...

小黑202 ⋅ 29分钟前 ⋅ 0

SSL双向认证的实现

环境 系统:archlinux/centOS nginx:nginx/1.12.2 浏览器:火狐firefox 前提:1.安装nginx。    2.安装openssl。 生成证书 新建工作目录  首先建立一个工作目录,这里以我的工作目录为例...

颖辉小居 ⋅ 35分钟前 ⋅ 0

vscode 代码格式化,语法检查插件ESLint+Prettier

ESLint+Prettier prettier只关注格式化 ,插件prettier-vscode,编辑器的配置setting.json会出现prettier插件的相关配置节点,同时也能看到一些默认的配置信息。 安装成功后,编辑器默认的格式...

momo1987 ⋅ 37分钟前 ⋅ 0

使用TinyProxy搭建代理服务器

什么是TinyProxy 它是一个代理服务器,用来实现http或https代理,windows系统一直在用ccproxy, 非windows系统,怎么开放个http代理呢?那就是TinyProxy。 安装 直接执行命令即可 sudo apt-g...

bengozhong ⋅ 37分钟前 ⋅ 0

掌握需求过程

1,需求必须可度量、可测试 2,需求是什么? 功能需求 非功能需求 限制条件 3,Volere需求过程 采用Volere需求过程,最好采用RUP,增量,迭代,螺旋,Scrum或其他类似的迭代式开发过程 (注意...

霜叶情 ⋅ 42分钟前 ⋅ 0

分布式微服务云架构开发Web应用

举一个简单的例子,在快速入门工程的基础上,举一个简单的示例来通过Thymeleaf渲染一个页面。 @Controller public class HelloController { @RequestMapping("/") pub...

明理萝 ⋅ 43分钟前 ⋅ 0

图应用之关键路径(Critical Path)

之前我们介绍过,在一个工程中我们关心两个问题: (1)工程是否顺利进行 (2)整个工程最短时间。 之前我们优先关心的是顶点(AOV),同样我们也可以优先关心边(同理有AOE)。(Activity ...

临江仙卜算子 ⋅ 44分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部