函数是完成一个特定任务的代码块。一个函数接受输入,对输入进行一些运算并产生输出。

函数声明

在 Go 中声明一个函数的语法为:

func functionname(parametername type) returntype {
//function body
}

函数声明以关键字 func 开头,后面是函数名字,接着是在 ( 和 ) 之间指定的参数列表,然后是函数的返回类型。指定参数的语法为参数名称后面跟着参数类型。可以指定任意数量的参数,形式为: (parameter1 type, parameter2 type)。最后是由 { 和 } 以及它们之间的代码块组成的函数体。

在一个函数中,参数和返回值是可选的。因此下面的语法也是合法的函数声明:

func functionname() {
}

函数的例子

让我们写一个函数,它以单个产品的价格和产品数量作为输入参数,并以总价格(单个产品的价格与产品数量的乘积)作为返回值。

func calculateBill(price int, no int) int {
var totalPrice = price * no
return totalPrice
}

上面的函数接受两个 int 类型的输入参数:price 和 no,并返回 totalPriceprice 与 no 的乘积) 。返回值的类型也是 int

如果连续的参数具有相同的类型,我们可以避免每次都写出它们的类型,只需要在结束的时候写一次就可以了,比如:price int, no int 可以写成:price, no int。 于是上面的函数可以写成:

func calculateBill(price, no int) int {
var totalPrice = price * no
return totalPrice
}

我们已经定义好了我们函数,现在让我们在代码中调用它。调用函数的语法为:functionname(parameters)。上面定义的函数 calculateBill 可以通过下面的代码调用:

calculateBill(, )

下面是完整的程序,它调用 calculateBill 并计算总价格。

package main

import (
"fmt"
) func calculateBill(price, no int) int {
var totalPrice = price * no
return totalPrice
}
func main() {
price, no := ,
totalPrice := calculateBill(price, no)
fmt.Println("Total price is ", totalPrice)
}

多个返回值

一个函数可以返回多个值。让我们写一个函数 rectProps,它接受一个矩形的长和宽,并返回该矩形的面积和周长。矩形的面积为长与宽的积。周长为长与宽的和的 2 倍。

package main

import (
"fmt"
) func rectProps(length, width float64)(float64, float64) {
var area = length * width
var perimeter = (length + width) *
return area, perimeter
} func main() {
area, perimeter := rectProps(10.8, 5.6)
fmt.Printf("Area %f Perimeter %f", area, perimeter)
}

如果一个函数有多个返回值,那么这些返回值应该用小括号()括起来,比如:func rectProps(length, width float64)(float64, float64) 接受两个类型为 float64 的参数(length 和 width),并且同样返回两个类型为 float64 的返回值。上面程序的输出为:Area 60.480000 Perimeter 32.800000

具名返回值

可以给一个函数的返回值指定名字。如果指定了一个返回值的名字,则可以视为在该函数的第一行中定义了该名字的变量。

上面的 rectProps 函数可以用具名返回值的形式重写如下:

func rectProps(length, width float64)(area, perimeter float64) {
area = length * width
perimeter = (length + width) *
return //no explicit return value
}

在上面的函数中,area 和 perimeter 是具名返回值。注意 return 语句没有指定任何返回值。因为在函数声明时已经指定area 和 perimeter 是返回值,在遇到 return 语句时它们会自动从函数中返回。

空指示符

在 Go 中,下划线(_)表示空指示符(blank identifier)。它可以用于代替任何类型的任何值。让我们看看如何使用空指示符。

我们知道上面定义的函数 rectProps 返回矩形的面积(area)和周长(perimeter)。如果我们只需要获取 area 而想要忽略 perimeter该怎么办呢?这时候就可以使用空指示符。

下面的程序仅接收 rectProps 返回的 area

package main

import (
"fmt"
) func rectProps(length, width float64) (float64, float64) {
var area = length * width
var perimeter = (length + width) *
return area, perimeter
}
func main() {
area, _ := rectProps(10.8, 5.6) // perimeter is discarded
fmt.Printf("Area %f ", area)
}

在 area, _ := rectProps(10.8, 5.6) 这一行,我们仅仅获取了 area,而使用空指示符 _ 来忽略第二个返回值(perimeter)。

什么是变参函数?

变参函数是指可以接受可变数量的参数的函数。

语法

如果一个函数的最后一个参数由 ...T 表示,则表示该函数可以接受任意数量的类型为 T 的参数。

请注意只有函数的最后一个参数才能指定为可变参数。

例子

你有没有想过为什么 append 函数可以将任意数量的值追加到切片末尾?这是因为它是一个变参函数。append 的原型为 func append(slice []Type, elems ...Type) []Type,其中 elems 是一个可变参数。

让我们来创建一个自己的变参函数。我们将编写一个程序来判断某个特定整数是否包含在某个整数列表中。

package main

import (
"fmt"
) func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(, , , )
find(, , , , , )
find(, , , )
find()
}

上面的程序中,func find(num int, nums ...int) 可以接受任意数量的参数。...int 在内部表示为切片。在这里 nums的类型为 []int

第 10 行利用 range for 遍历 nums 切片,如果找到 num 则打印 num 所在位置,否则打印没有找到。

上面的程序输出如下:

type of nums is []int
found at index in [ ] type of nums is []int
found at index in [ ] type of nums is []int
not found in [ ] type of nums is []int
not found in []

在第 25 行,find 只有一个参数。我们没有传递任何参数给 nums ...int。这是合法的,(译者注:如果没有给可变参数传递任何值,则可变参数为 nil 切片),在这里 nums 是一个 nil 切片,长度和容量都是0。

传递切片给可变参数

我们已经提到 ...T 在内部表示为类型是 []T 切片。如果真是这样,可以传递一个切片给可变参数吗?让我们从下面的例子中寻找答案:

package main

import (
"fmt"
) func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
nums := []int{, , }
find(, nums)
}

在第 23 行,我们没有将若干数量的参数传递给 find 的最后一个参数, 而是传递了一个切片。这是非法的,我们不能传递一个切片给可变参数。上面的程序将报错:main.go:23: cannot use nums (type []int) as type int in argument to find

这里有一个语法糖可以用来将切片传递给变参函数。可以在切片后面加 ...,这样会将切片展开为其中的各个元素并将它们传递给变参函数。这样该程序将正常工作。

上面的程序如果将第23行的 find(89, nums) 改为 find(89, nums...),程序将通过编译,并输出如下:

type of nums is []int
found at index in [ ]

Golang教程:函数、变参函数的更多相关文章

  1. Golang简单入门教程——函数进阶篇

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是golang专题的第八篇,我们来聊聊golang当中的函数. 我们在之前的时候已经介绍过了函数的基本用法,知道了怎么样设计或者是定义一 ...

  2. golang的函数

    在golang中, 函数是第一类值(first-class object), 即函数可以赋值与被赋值. 换言之, 函数也可以作为ReceiverType, 定义自己的method. 实例: http. ...

  3. golang(06)函数介绍

    原文链接 http://www.limerence2017.com/2019/09/11/golang11/#more 函数简介 函数是编程语言中不可缺少的部分,在golang这门语言中函数是一等公民 ...

  4. golang笔记——函数与方法

    如果你遇到没有函数体的函数声明,表示该函数不是以Go实现的. package math func Sin(x float64) float //implemented in assembly lang ...

  5. Golang教程:数组和切片

    数组 数组是类型相同的元素的集合.例如,整数 5, 8, 9, 79, 76 的集合就构成了一个数组.Go不允许在数组中混合使用不同类型的元素(比如整数和字符串). 声明 var variable_n ...

  6. Golang tips ----- 函数

    1.在函数调用时,Golang没有默认参数值 2.一个函数声明如果没有函数体,表面该函数不是由Golang实现的,这样的声明定义了函数标识符 3.拥有函数名的函数只能在包级语法块中被声明 4.函数值( ...

  7. golang 原子操作函数

    golang中的原子操作在sync/atomic package中. 下文以比较和交换操作函数为例,介绍其使用. CompareAndSwapInt32 比较和交换操作是原子性的. // Compar ...

  8. Golang之函数练习

    小例题: package main import "fmt" /* 函数练习, 可变参数使用 写一个函数add 支持1个或多个int相加,并返回相加结果 写一个函数concat,支 ...

  9. [golang note] 函数定义

    普通函数定义 √ golang函数基本组成:关键字func.函数名.参数列表.返回值.函数体和返回语句. • 语法如下 func 函数名(参数列表) (返回值列表) { // 函数体 } • 示例如下 ...

随机推荐

  1. Javascript中的闭包(转载)

    前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到 ...

  2. Mysql内置功能《一》流程控制

    delimiter // CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSE ...

  3. LAMPer 技能树

  4. [ActionScript 3.0] 像素级碰撞检测

    package { import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.Disp ...

  5. linux和windows之间的文件压缩和解压缩以及^R的问题

    推荐大家使用zip压缩和解压,因为zip一般是linux系统自带: 一.zip和unzip 1. zip压缩 zip -r myfile.zip ./web 将当前目录里的web下的所有文件和文件夹全 ...

  6. win10打开移动热点让手机连接上网教程

    概述: 为什么要这么做呢? 我笔记本插网线可以上网,但是没有买猫盒,所以只能pc开热点,让手机上网. 过程如下: 1开启移动热点,设置密码 1.1开启移动热点,截图如下: 1.2设置热点名称,密码 2 ...

  7. Django上传文件的那些参数

    # ################## 默认文件上传配置 ######################## from django.core.files.uploadhandler import M ...

  8. Maven web项目(简单的表单提交) 搭建(eclipse)

    我们将会搭建一个,基于Maven管理的,具有简单的表单提交功能的web项目,使用DAO--service--WEB三层结构,服务器使用Tomcat 1 项目基本结构的搭建 左上角File---> ...

  9. 20165224 陆艺杰 Exp6 信息搜集与漏洞扫描

    Exp6 信息搜集与漏洞扫描 (1)哪些组织负责DNS,IP的管理. 全球根服务器均由美国政府授权的ICANN统一管理,负责全球的域名根服务器.DNS和IP地址管理. (2)什么是3R信息. 注册人 ...

  10. 04. H5标签有哪些?行内元素有哪些?块级元素有哪些?空(void)元素有哪些?行内元素和块级元素有什么区别?你工作中常用标签有什么?

    4. H5标签有哪些? 2)行内元素有哪些? a - 锚点 em - 强调 img - 图片 font - 字体设定 ( 不推荐 ) i - 斜体 input - 输入框 3)块级元素有哪些? add ...