原文在gitbook,字字原创,版权没有,转载随意。

在写本文的前一天,2017维密在上海开始了。 为了纪念屌丝界的盛世,特为本节起名维多利亚的秘密。现在的社会,要想出名只有抓眼球。所以写份技术文章,还要蹭一下维密的光。 蹭对了,不出彩。蹭不对了,还要挨骂。昨天就有一位匿名的同学,说我写的罗里吧嗦,重点内容没多少,扯淡话语占半篇。这能怪我吗? 可能怪我吧。但去百度,google随手一搜,哪种语言没有<21天学会XXX>,<从XXX到XXX>,<XXX实战>,<XXXX入门教程>。要想在这些书中突围,让别人看到我的东西,没点特色行么? 别人的特色是写的多,讲得好。 那我的特色只能讲的轻松,看着不累。如果你能看着看着,学会东西,又能时不时的笑一笑,我认为就够了。如果哪天我也满肚子学问了,再写份正儿八经的入门教程吧。

好,言归正传,来一首定场小诗,算是告别维密,拥抱golang。

隔河看见一锭金,山又高来水又深。
有心过河把金子捡,又恐怕王八咬了脚后跟。
舍了罢来舍了罢,外财不富命穷人。
猛虎虽瘦雄心在,君子身贫志不贫。
穷在街前无人问,富在深山有远亲。

下面开始聊Golang的函数(Function)

在入坑第一式中,我们提到过函数。当时以main函数为例进行的讲解。 那会是为了demo需要,拿来main就用。在此节中,开始详细讲解函数。

函数,字方法(method),又名子例程(sub-routine),别名过程(procedure)。在不同的语言种,有不同的叫法。 但本质都是一样的,都是一组执行不同任务的语句。别的语言暂且不提,下面所说的都是Golang的家规。

在Golang程序中,每个Go程序都至少有一个main函数。如果你写的是库(package),那就不需要提供main函数。 在编写代码时可以根据逻辑功能划分出不同的函数。划分的尺度完全掌握在你手中。但是从实际生产环境和代码管理上来说,每个函数尽可能的只完成一件工作。

在组织函数时,每个函数必须有函数名、返回类型和参数。在函数划分方面,Golang标准库可以作为一个范例。标准库提供了许多内置函数。例如,len()函数接受各种类型的参数,并返回类型的长度。如果将字符串传递给它,函数将以字节的形式返回字符串的长度。如果将数组传递给它,函数将返回数组的长度。我们在编写函数名或者实现函数时,可以参考标准库中的函数定义。

Golang提供的函数声明语法如下:

func function_name( [parameter list] ) [return_types]
{
body of the function
}
  • func 表示这是一个函数。 在此之后的语义块都是函数。
  • function_name 函数名。函数名和参数列表共同构成函数签名,而函数签名是用来查找函数的唯一ID.
  • parameter 参数列表。参数就像一个占位符。当调用一个函数时,您传递一个值到参数。这个值被称为实际参数或参数(有的场合叫做入参)。参数列表是指函数的参数的类型、顺序和数量。参数是可选的。也就是说,参数可以为空。
  • return_type 返回值。函数可以返回一个值列表。return_types是函数返回值的数据类型列表。返回值是可选的,也就是说函数允许不返还任何数据。
  • fuction body. 函数体,也就是执行语句。

比对着下面的例子,我们来看函数的定义:

func max(num1, num2 int) int {
result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}

这个函数,名称是max。 参数有两个,num1, num2都是int类型。 当然你也可以写成 num1 int, num2 int, 同样正确。 Golang里面规定如果参数类型相同,可以简化为 num1,num2 int。 所以使用哪种,看你心情。 高兴就好。

返回值是一个int类型的值。

函数只有被调用了,才能体现价值。 好比程序员,只有写代码才能体现价值,如果让程序员去做客服,呵呵,这是让你自行离职的前奏。

当程序调用一个函数时,程序控制权就会被转移到被调用的函数。只有这个程序结束,或者返回了数值之后,控制权才会回到原程序中。

要调用max函数,只需要通过函数名,然后传递参数就可以。例如:

package main

import "fmt"

func main() {
var a int = 100
var b int = 200
var ret int ret = max(a, b) fmt.Printf( "Max value is : %d\n", ret )
} func max(num1, num2 int) int {
var result int if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}

通过ret保存max返回的数据值。Golang函数允许多值返回(多值返回并非是Golang的独创,也并非所有语言都支持)。如果函数返回多个值时,使用多个变量来保存,例如:

package main

import "fmt"

func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Mahesh", "Kumar")
fmt.Println(a, b)
}

swap函数返回了两个值,调用时使用a和b分别保存。 如果返回三个值呢? 那就使用a,b和c来保存。 如果某一个值,不需要保存。例如返回三个值,但第二返回值,不需要处理。 通过"_"来忽略保存。例如:

package main

import "fmt"

func swap(x, y string) (string, string, string) {
return y, x,"ok"
}
func main() {
a, _, c := swap("Mahesh", "Kumar")
fmt.Println(a, c)
}

"_"表示此值不需要保存。 在golang代码中会经常看到这样忽略的情况。

函数的变量

函数的变量和其它的变量一样,存在生命周期。 一般来说,在进入函数的时候,会创建变量。在退出函数的时候,会销毁变量。下面是传递参数的两种方法:

  • 值传递
  • 引用传递
值传递

此时仅仅是将参数的实际值复制到函数参数中。在这种情况下,对函数内部的参数进行的更改不会对参数产生影响。通俗点,就是两个同学A和B。 A告诉了B,他的考试答案,然后B自由发挥随便改。 因为A的答案在自己手中,所以无论B怎么改对A都没有影响。这就是传说中的打小抄。

以上面的函数为例:

package main

import "fmt"

func main() {

   var a int = 100
var b int = 200 fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b ) swap(a, b) fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x, y int) int {
var temp int temp = x
x = y
y = temp return temp;
}

看似是把a和b给了swap函数,但swap函数其实只是接受了a和b的值,在swap内部使用的变量和a,b没有半毛钱关系。这种情况下,swap内部随便改,外部的a和b都不会变。

这种调用就叫做值传递。

引用传递

这个时候是将参数的地址复制到函数参数中。在函数内,地址用于访问实际参数。这意味着函数内部参数的变化会影响到外部函数。还拿作弊的时候为例,值传递是打小抄。 而引用传递就是传递试卷了,A直接把他的试卷给了B,B在试卷上面随意修改。那当然会影响到A。所以这种作弊方法,有很大风险性,谁知道B的智商够不够呀。 万一改错了怎么办。

我们将上面的例子稍加改造:

package main

import "fmt"

func main() {

   var a int = 100
var b int = 200 fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b ) swap(&a, &b) fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x
*x = *y
*y = temp
}

在swap函数中,是*int(不熟悉的同学,可以返回前几节,去看变量那一节内容)。 表示的是地址。而&a表示的是取a的内存地址。而后swap内部所有的操作都是在a和b的内存中进行的,所以无论swap内部做了什么,都会反映到a和b上面。

看完是不是感觉引用传递好厉害,是不是担心驾驭不了?说实话,应该是你看到了指针,地址这些C的东西才会感觉到内心害怕吧。虽说有两种调用方式,但Golang默认情况都是第一种,值传递。 只有在编码时使用了指针,才会是引用传递。 所以如果你感觉Hold不住,那就老老实实的去值传递。 如果你感觉小case,那就大胆的去引用传递。究竟哪个好,公婆无定论,没有最好的,只有最合适的。

聊完参数的传递,我们来聊函数的最后一点内容。

如何创建函数

上面的函数创建方式,是最普遍也是最通用的。 但作为一本教程而言,不能只说普通的,也要说到"回"的五种写法。至于能不能用得上,我就不知道了。 没准可以哄住你的面试考官。

  • 作为变量时创建。先看下面的例子:
package main

import (
"fmt"
"math"
) func main(){
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
} fmt.Println(getSquareRoot(9))
}

getSquareRoot在上面例子中,就和变量的创建方式一样,随用随创建。建完即用,用完即扔,扔完即删,删完即清。

  • 匿名函数。同样看下面的例子:
package main

import "fmt"

func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
} func main(){
nextNumber := getSequence() fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber()) nextNumber1 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber1())
}

不要以为是函数声明错了。 没错,因为上面getSequence()这个函数就是返回一个函数。 也就是函数返函数。 比对一下函数语法,可以看到 func() int 是返回的数据类型(函数也是一种类型). 然后函数体中就return了一个函数(fun() int)。 只不过这个函数没有名字而已。

无名没关系,在调用的时候给了一个名字,叫做nextNumber。 所以这个函数就等同于 func nextNumbe() int{}。这下就没问题了吧。

  • 类型中的方法,我们说过函数,有名方法。 在什么情况下叫做方法呢? 只有在这个函数独属于某一种类型的时候才叫做方法。例如下面的情况:
package main

import (
"fmt"
"math"
) type Circle struct {
x,y,radius float64
} func(circle Circle) area() float64 {
return math.Pi * circle.radius * circle.radius
} func main(){
circle := Circle{x:0, y:0, radius:5}
fmt.Printf("Circle area: %f", circle.area())
}

通过type xxx struct定义了一个新的数据类型(现在还没讲到,如果你懂Java,可以类比为java里面的类)。在area之前添加(circle Circle)就告诉了编译器,这个函数是独属于Circle类的。 换句话说,area()是Circle的成员方法。只不过Java是定义在class中,而golang是定义在外面而已。 本质都是那么一回事。

既然属于Circle,所以调用的时候,必须声明一个Circle实例,然后才能调用。所以也就有了circle := Circle{x:0, y:0, radius:5}这句。

Golang中的函数内容,基本就这么多了。 因为是想到多少就写多少,如果有哪些没写上的,还希望提醒一下我。 最后,来一段软文推广:

谢谢Apple公司,有偿提供的mac笔记本,让我将手写书变成了手敲书。 谢谢网易公司无偿提供的有道词典,让我可以翻译英文单词。但也只能翻译单词了,因为翻译大段英文,经常词不达意。谢谢Gitbook提供的平台可以存储我写的文章,没啥可以吐槽的,非要吐槽的,那就是希望PC端呈现的样式可以和客户端呈现的一样。感谢河北工业大学,让我走上了开发的道路,虽然我当时想转专业,但最后还是屈服走了这条不归路。

如果你也希望出现在这里,联系我。如果符合我的风格,一定给软文留个推广位。 如果不符合,那就想法子给你留个推广位。 不用担心费用,免费Free。 只为一乐,干嘛较真呢。 嘛钱不钱的,乐呵乐呵得了。

维多利亚的秘密 golang入坑系统的更多相关文章

  1. 维多利亚的秘密 golang入坑系列

    原文在gitbook,字字原创,版权没有,转载随意. 在写本文的前一天,2017维密在上海开始了. 为了纪念屌丝界的盛世,特为本节起名维多利亚的秘密.现在的社会,要想出名只有抓眼球.所以写份技术文章, ...

  2. 入坑第二式 golang入坑系统

    史前必读: 这是入坑系列的第二式,如果错过了第一式,可以去gitbook( https://andy-zhangtao.gitbooks.io/golang/content/ )点个回放,看个重播.因 ...

  3. 我们是80后 golang入坑系统

    现在这个系列,已经开始两极分化了. 点赞的认为风格轻松,看着不困.反之,就有人嫌写的罗里吧嗦,上纲上线.所以善意提醒,里面不只是技术语言,还有段子.专心看技术的,千万别点!别怪我没提醒!差点忘说,版权 ...

  4. 初生牛犊不怕虎 golang入坑系列

    读前必读,下面所有内容都是来自这里. 放到这里的目的,就是为了比对一下,哪里的读者多.平心而论,同样的Markdown,博客园排版真心X看,怎么瞅怎么X看.(X := '难' || X :='耐' | ...

  5. 我们是80后 golang入坑系列

    现在这个系列,已经开始两极分化了. 点赞的认为风格轻松,看着不困.反之,就有人嫌写的罗里吧嗦,上纲上线.所以善意提醒,里面不只是技术语言,还有段子.专心看技术的,千万别点!别怪我没提醒!差点忘说,版权 ...

  6. 入坑第二式 golang入坑系列

    史前必读: 这是入坑系列的第二式,如果错过了第一式,可以去gitbook( https://andy-zhangtao.gitbooks.io/golang/content/ )点个回放,看个重播.因 ...

  7. 坐忘峰 golang入坑系列

    读前必读: 本文写于20日,首发于gitbook. 迟到的是日期,没变的是内容. 点击进入 https://andy-zhangtao.gitbooks.io/golang/content/ 可以看到 ...

  8. 分水岭 golang入坑系列

    第三式开篇语有些负面, 所以这里就不贴了.有兴趣的自己可以去看看 https://andy-zhangtao.gitbooks.io/golang/content/ .怒发冲冠,意气之作.看完就完了, ...

  9. 崩溃 golang入坑系列

    早上(11.30)收到邮件,Vultr东京机房网络故障.当时搭建SS时,考虑到了机房故障.所以特意分出了日本和香港两条线路.但千算万算,忘记数据库还在东京机房中. 现在网络故障,SS服务器无法读取数据 ...

随机推荐

  1. 吾八哥学Python(五):Python基本数学运算

    今天我们学习Python里的基本数学运算方法,还是通过例子来练习吧! 加减乘除求余 #加法 print(12+34) #减法 print(30-10.0) #乘法 print(3*5) #除法 pri ...

  2. layer.msg 添加在Ajax之前 显示进度条。

    一.使用方法:1)必须先引入jQuery1.8或以上版本 <script src="jQuery的路径"></script> <script src= ...

  3. (10.20)Java小作业!

    今天想要和大家分享一道我最近遇到的题,里面既包括了嵌套循环的运用,还有函数的定义与调用,我个人觉得挺有价值的. 打印一个由*号构成的等腰三角形: 具体的解题方法如下: public class get ...

  4. Python程序练习4--模拟员工信息数据库操作

    1.功能简介 此程序模拟员工信息数据库操作,按照语法输入指令即能实现员工信息的增.删.改.查功能.   2.实现方法 架构: 本程序采用python语言编写,关键在于指令的解析和执行:其中指令解析主要 ...

  5. inotify-tools + php脚本实现Linux服务器文件监控并邮件提醒

      需求简介: 由于服务器被挂马,经常被写入涉敏感的html网页,领导时常被网监请去喝茶,呵呵你懂的.所以有两个需求,一是找出服务器的木马后门和修复代码漏洞,二是监控服务器涉及增删改查的文件. 第一个 ...

  6. 为Android设备添加A2SD支持

          相信很多用Android设备的用户都有这个问题,内部存储太小导致应用只能装那么几个,虽然rom也有提供移动到sd卡的选项,但是仅仅是移动程序文件到sd卡,并不能解决多少问题,多装几个还是会 ...

  7. JS难点--组件开发

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Consolas; color: #a5b2b9 } span.Apple-tab-span ...

  8. Ubuntu/deppin 系统安装Nginx

    Ubuntu/deppin 系统安装Nginx 添加密钥 打开Nginx官网 ,并找到如下位置: 在桌面新建"nginx_signing.key"文件,文件内容为请点击图1中的标注 ...

  9. JS的简单用法

    JS的简单用法 参考:http://www.w3school.com.cn/js/js_switch.asp JavaScript 是网络的脚本语言 JavaScript 是可插入 HTML 页面的编 ...

  10. MongoDB索引限制

    1. 额外开销: 每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作.所以,如果你很少对集合进行读取操作,建议不使用索引. 2. 内存使用: 由于索引是存储在内存(RAM)中 ...