- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Go语言中的流程控制结构和函数详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
这小节我们要介绍Go里面的流程控制以及函数操作.
流程控制 。
流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑。Go中流程控制分三大类:条件判断,循环控制和无条件跳转.
if 。
if也许是各种编程语言中最常见的了,它的语法概括起来就是:如果满足条件就做某事,否则做另一件事.
Go里面if条件判断语句中不需要括号,如下代码所示:
。
Go的if还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示:
// 计算获取值x,然后根据x返回的大小,判断是否大于10。 if x := computedValue(); x > 10 { fmt.Println("x is greater than 10") } else { fmt.Println("x is less than 10") } 。
。
//这个地方如果这样调用就编译出错了,因为x是条件里面的变量 fmt.Println(x) 。
。
多个条件的时候如下所示:
。
goto 。
Go有goto语句——请明智地使用它。用goto跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
。
。
标签名是大小写敏感的.
for 。
Go里面最强大的一个控制逻辑就是for,它即可以用来循环读取数据,又可以当作while来控制逻辑,还能迭代操作。它的语法如下:
expression1、expression2和expression3都是表达式,其中expression1和expression3是变量声明或者函数调用返回值之类的,expression2是用来条件判断,expression1在循环开始之前调用,expression3在每轮循环结束之时调用.
。
一个例子比上面讲那么多更有用,那么我们看看下面的例子吧:
package main import "fmt" 。
。
func main(){ sum := 0; for index:=0; index < 10 ; index++ { sum += index } fmt.Println("sum is equal to ", sum) } // 输出:sum is equal to 45 。
。
有些时候需要进行多个赋值操作,由于Go里面没有,操作符,那么可以使用平行赋值i, j = i+1, j-1 。
有些时候如果我们忽略expression1和expression3:
其中;也可以省略,那么就变成如下的代码了,是不是似曾相识?对,这就是while的功能.
。
在循环里面有两个关键操作break和continue ,break操作是跳出当前循环,continue是跳过本次循环。当嵌套过深的时候,break可以配合标签使用,即跳转至标签所指定的位置,详细参考如下例子:
。
break和continue还可以跟着标号,用来跳到多重循环中的外层循环 。
for配合range可以用于读取slice和map的数据:
。
switch 。
有些时候你需要写很多的if-else来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候switch就能很好的解决这个问题。它的语法如下 。
。
sExpr和expr1、expr2、expr3的类型必须一致。Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果switch没有表达式,它会匹配true.
。
。
在第5行中,我们把很多值聚合在了一个case里面,同时,Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码.
上面的程序将输出 。
。
函数 。
函数是Go里面的核心设计,它通过关键字func来声明,它的格式如下:
。
上面的代码我们看出 。
1.关键字func用来声明一个函数funcName 2.函数可以有一个或者多个参数,每个参数后面带有类型,通过,分隔 3.函数可以返回多个值 4.上面返回值声明了两个变量output1和output2,如果你不想声明也可以,直接就两个类型 5.如果只有一个返回值且不声明返回值变量,那么你可以省略 包括返回值 的括号 6.如果没有返回值,那么就直接省略最后的返回信息 7.如果有返回值, 那么必须在函数的外层添加return语句 。
下面我们来看一个实际应用函数的例子(用来计算Max值) 。
package main import "fmt" 。
。
// 返回a、b中最大值. func max(a, b int) int { if a > b { return a } return b } 。
func main() { x := 3 y := 4 z := 5 。
max_xy := max(x, y) //调用函数max(x, y) max_xz := max(x, z) //调用函数max(x, z) 。
fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy) fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz) fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它 } 。
上面这个里面我们可以看到max函数有两个参数,它们的类型都是int,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int),默认为离它最近的类型,同理多于2个同类型的变量或者返回值。同时我们注意到它的返回值就是一个类型,这个就是省略写法.
。
多个返回值 。
Go语言比C更先进的特性,其中一点就是函数能够返回多个值.
我们直接上代码看例子:
package main import "fmt" 。
。
//返回 A+B 和 A*B func SumAndProduct(A, B int) (int, int) { return A+B, A*B } 。
func main() { x := 3 y := 4 。
xPLUSy, xTIMESy := SumAndProduct(x, y) 。
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy) fmt.Printf("%d * %d = %d\n", x, y, xTIMESy) } 。
。
上面的例子我们可以看到直接返回了两个参数,当然我们也可以命名返回参数的变量,这个例子里面只是用了两个类型,我们也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果你的函数是导出的(首字母大写),官方建议:最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差.
。
变参 。
Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参:
arg ...int告诉Go这个函数接受不定数量的参数。注意,这些参数的类型全部是int。在函数体中,变量arg是一个int的slice:
。
传值与传指针 。
当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上.
为了验证我们上面的说法,我们来看一个例子 。
package main import "fmt" 。
。
//简单的一个函数,实现了参数+1的操作 func add1(a int) int { a = a+1 // 我们改变了a的值 return a //返回一个新值 } 。
func main() { x := 3 。
fmt.Println("x = ", x) // 应该输出 "x = 3" 。
x1 := add1(x) //调用add1(x) 。
fmt.Println("x+1 = ", x1) // 应该输出"x+1 = 4" fmt.Println("x = ", x) // 应该输出"x = 3" } 。
。
看到了吗?虽然我们调用了add1函数,并且在add1中执行a = a+1操作,但是上面例子中x变量的值没有发生变化 。
理由很简单:因为当我们调用add1的时候,add1接收的参数其实是x的copy,而不是x本身.
那你也许会问了,如果真的需要传这个x本身,该怎么办呢?
这就牵扯到了所谓的指针。我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。只有add1函数知道x变量所在的地址,才能修改x变量的值。所以我们需要将x所在地址&x传入函数,并将函数的参数的类型由int改为*int,即改为指针类型,才能在函数中修改x变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。请看下面的例子:
package main import "fmt" 。
。
//简单的一个函数,实现了参数+1的操作 func add1(a *int) int { // 请注意, *a = *a+1 // 修改了a的值 return *a // 返回新值 } 。
func main() { x := 3 。
fmt.Println("x = ", x) // 应该输出 "x = 3" 。
x1 := add1(&x) // 调用 add1(&x) 传x的地址 。
fmt.Println("x+1 = ", x1) // 应该输出 "x+1 = 4" fmt.Println("x = ", x) // 应该输出 "x = 4" } 。
。
这样,我们就达到了修改x的目的。那么到底传指针有什么好处呢?
1.传指针使得多个函数能操作同一个对象。 2.传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。 3.Go语言中string,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针) 。
defer 。
Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。如下代码所示,我们一般写打开一个资源是这样操作的:
func ReadWrite() bool { file.Open("file") // 做一些工作 if failureX { file.Close() return false } 。
。
if failureY { file.Close() return false } 。
file.Close() return true } 。
。
我们看到上面有很多重复的代码,Go的defer有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在defer后指定的函数会在函数退出前调用.
如果有很多调用defer,那么defer是采用后进先出模式,所以如下代码会输出4 3 2 1 0 。
。
函数作为值、类型 。
在Go中函数也是一种变量,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型 。
函数作为类型到底有什么好处呢?那就是可以把这个类型的函数当做值来传递,请看下面的例子 。
package main import "fmt" 。
。
type testInt func(int) bool // 声明了一个函数类型 。
func isOdd(integer int) bool { if integer%2 == 0 { return false } return true } 。
func isEven(integer int) bool { if integer%2 == 0 { return true } return false } 。
// 声明的函数类型在这个地方当做了一个参数 。
func filter(slice []int, f testInt) []int { var result []int for _, value := range slice { if f(value) { result = append(result, value) } } return result } 。
func main(){ slice := []int {1, 2, 3, 4, 5, 7} fmt.Println("slice = ", slice) odd := filter(slice, isOdd) // 函数当做值来传递了 fmt.Println("Odd elements of slice are: ", odd) even := filter(slice, isEven) // 函数当做值来传递了 fmt.Println("Even elements of slice are: ", even) } 。
函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到testInt这个类型是一个函数类型,然后两个filter函数的参数和返回值与testInt类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活.
。
Panic和Recover 。
Go没有像Java那样的异常机制,它不能抛出异常,而是使用了panic和recover机制。一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有panic的东西。这是个强大的工具,请明智地使用它。那么,我们应该如何使用它呢?
Panic 。
是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数F调用panic,函数F的执行被中断,但是F中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上,直到发生panic的goroutine中所有调用的函数返回,此时程序退出。恐慌可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组.
Recover 。
是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。recover仅在延迟函数中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果。如果当前的goroutine陷入恐慌,调用recover可以捕获到panic的输入值,并且恢复正常的执行.
下面这个函数演示了如何在过程中使用panic 。
var user = os.Getenv("USER") 。
。
func init() { if user == "" { panic("no value for $USER") } } 。
。
下面这个函数检查作为其参数的函数在执行时是否会产生panic:
。
main函数和init函数 。
Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数.
Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数.
程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过程:
图2.6 main函数引入包初始化流程图 。
import 。
我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:
然后我们代码里面可以通过如下的方式调用 。
上面这个fmt是Go语言的标准库,其实是去GOROOT环境变量指定目录下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:
。
1.相对路径 。
2.绝对路径 。
。
上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事 。
点操作 。
我们有时候会看到如下的方式导入包 。
。
这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world") 。
别名操作 。
别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字 。
别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println("hello world").
。
_操作 。
这个操作经常是让很多人费解的一个操作符,请看下面这个import 。
_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数.
。
最后此篇关于Go语言中的流程控制结构和函数详解的文章就讲到这里了,如果你想了解更多关于Go语言中的流程控制结构和函数详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想做的是让 JTextPane 在 JPanel 中占用尽可能多的空间。对于我使用的 UpdateInfoPanel: public class UpdateInfoPanel extends JP
我在 JPanel 中有一个 JTextArea,我想将其与 JScrollPane 一起使用。我正在使用 GridBagLayout。当我运行它时,框架似乎为 JScrollPane 腾出了空间,但
我想在 xcode 中实现以下功能。 我有一个 View Controller 。在这个 UIViewController 中,我有一个 UITabBar。它们下面是一个 UIView。将 UITab
有谁知道Firebird 2.5有没有类似于SQL中“STUFF”函数的功能? 我有一个包含父用户记录的表,另一个表包含与父相关的子用户记录。我希望能够提取用户拥有的“ROLES”的逗号分隔字符串,而
我想使用 JSON 作为 mirth channel 的输入和输出,例如详细信息保存在数据库中或创建 HL7 消息。 简而言之,输入为 JSON 解析它并输出为任何格式。 最佳答案 var objec
通常我会使用 R 并执行 merge.by,但这个文件似乎太大了,部门中的任何一台计算机都无法处理它! (任何从事遗传学工作的人的附加信息)本质上,插补似乎删除了 snp ID 的 rs 数字,我只剩
我有一个以前可能被问过的问题,但我很难找到正确的描述。我希望有人能帮助我。 在下面的代码中,我设置了varprice,我想添加javascript变量accu_id以通过rails在我的数据库中查找记
我有一个简单的 SVG 文件,在 Firefox 中可以正常查看 - 它的一些包装文本使用 foreignObject 包含一些 HTML - 文本包装在 div 中:
所以我正在为学校编写一个 Ruby 程序,如果某个值是 1 或 3,则将 bool 值更改为 true,如果是 0 或 2,则更改为 false。由于我有 Java 背景,所以我认为这段代码应该有效:
我做了什么: 我在这些账户之间创建了 VPC 对等连接 互联网网关也连接到每个 VPC 还配置了路由表(以允许来自双方的流量) 情况1: 当这两个 VPC 在同一个账户中时,我成功测试了从另一个 La
我有一个名为 contacts 的表: user_id contact_id 10294 10295 10294 10293 10293 10294 102
我正在使用 Magento 中的新模板。为避免重复代码,我想为每个产品预览使用相同的子模板。 特别是我做了这样一个展示: $products = Mage::getModel('catalog/pro
“for”是否总是检查协议(protocol)中定义的每个函数中第一个参数的类型? 编辑(改写): 当协议(protocol)方法只有一个参数时,根据该单个参数的类型(直接或任意)找到实现。当协议(p
我想从我的 PHP 代码中调用 JavaScript 函数。我通过使用以下方法实现了这一点: echo ' drawChart($id); '; 这工作正常,但我想从我的 PHP 代码中获取数据,我使
这个问题已经有答案了: Event binding on dynamically created elements? (23 个回答) 已关闭 5 年前。 我有一个动态表单,我想在其中附加一些其他 h
我正在尝试找到一种解决方案,以在 componentDidMount 中的映射项上使用 setState。 我正在使用 GraphQL连同 Gatsby返回许多 data 项目,但要求在特定的 pat
我在 ScrollView 中有一个 View 。只要用户按住该 View ,我想每 80 毫秒调用一次方法。这是我已经实现的: final Runnable vibrate = new Runnab
我用 jni 开发了一个 android 应用程序。我在 GetStringUTFChars 的 dvmDecodeIndirectRef 中得到了一个 dvmabort。我只中止了一次。 为什么会这
当我到达我的 Activity 时,我调用 FragmentPagerAdapter 来处理我的不同选项卡。在我的一个选项卡中,我想显示一个 RecyclerView,但他从未出现过,有了断点,我看到
当我按下 Activity 中的按钮时,会弹出一个 DialogFragment。在对话框 fragment 中,有一个看起来像普通 ListView 的 RecyclerView。 我想要的行为是当
我是一名优秀的程序员,十分优秀!