Go语言学习个人小记(一)

galactico411 · · 1391 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

当我还是个学生的时候,总感觉服务器端编程是非常困难的一件事。老师们只是简单的那么一讲:建立一个socket侦听客户端连接,把连接上来的客户端弄个map保存起来,然后不断轮询。。。这种当然是无法在实际工作中使用的,关键的一些东西,比如并发如何处理啊,数据库操作怎样优化啊,安全性如何保证啊,这些课堂上都学不到。我也试图自学一些这部分的知识,结果是被我心爱的C++坑的不浅。本来我一直体会不到指针难用在哪里,自己开辟的内存自己释放,不是件很简单的事么?在尝试服务器端多线程编程的时候才真正发现内存管理的难点。。。多线程下很多东西变得不同了,我一直以来认为正确的析构写法突然就不正确了。而且往往还要用上很多第三方库。我不知道第三方库的写法,第三方库的作者也不知道我有没有按他希望的那样去用它。其结果就是内存报错不可避,野指针漫天飞。当然,我相信只要耐心认真的学习,这些问题用C++也是一定能解决的。只不过由于毕业之后自己的本职工作成了手游客户端开发,也没那么多时间去了解这些了。

最近工作不太顺心,乱七八糟的事情很多。于是就决定再研究一下服务器端的编程。工作之后接触的语言多了,才知道用其他语言开发一个服务器端并没有过去相信中那么难。公司的服务器大神用的是java,我看了一下觉得也不是那么复杂。更何况手机上的网游大多数都是弱联网类型的,用一些web框架都够用了,前段时间我就用django试验了一下,感觉用这种方式实在是简单。不过django本身只是个web开发框架,并不是个web服务器,还是要部署到apache或者nginx上。于是当时我配置各种环境的时间花得比写代码的时间长多了。

今天我打算要研究的这门叫go的语言,是google2009年推出的。据说处理多线程有独到之处。坦白说,简单的看了一下语法之后我十分讨厌她。比如go规定定义函数时后面第一个花括号不能另起一行写到下一行去。这个实在是夸张,因为我的习惯一向是写到下一行去的,而且从没有哪个语言说你必须写在这一行,否则不能通过编译。。。你可能会说区区一个花括号的写法这种小事怎么能成为讨厌一门语言的理由,然而翻开历史就会发现天主教徒和穆斯林们已经为我们这些无神论者认为并不存在的神争斗了一千多年。编程语言就像女人,你喜不喜欢她的关键在于写法也就是外表。至于你说windows应用编程c#第一啊,erlang处理多线程强大啊,lua与c++配合写游戏速度快啊,这些是你在某些领域选择她的理由,相当于一个女人的内在,也是你最终向选择和一个女人结婚的理由,但不一定代表你喜欢她。就好像因为花括号不能写到下一行去,我现在就很讨厌go语言,但还是选择用它来写个服务器一样。

那么就让我开始先了解一下它的变量类型,函数声明,选择和循环这些基础吧。

先让我尝试写一个用牛顿迭代法求平方根的小东西:

package main

import (
       "fmt"
       "math"
)

func main(){
       var hellostr = "Hello, Go.\n"
       fmt.Print(hellostr)
       tipstr := "请给我输入一个正数让我来求它的平方根:\n"
       fmt.Print(tipstr)
       var clacnum float64 //如果只写var clacnum,不明确指定类型要报错
       if clacnum < 0{
              fmt.Print("你在逗我,负数并没有平方根\n")
       }else if clacnum == 0 {
              fmt.Print("0的平方根还是0\n")
       }else {
              fmt.Printf("%g的平方根是:%g\n", clacnum, NewtonSqrt(clacnum))
       }
}

func NewtonSqrt(x float64) (iguess float64){ //牛顿迭代法求平方根,只是为了学习一下循环
       iguess = 1
       for math.Abs(iguess * iguess - x) > 1e-12{
              //for ;math.Abs(iguess * iguess - x) > 1e-12;{}相同
              iguess = (iguess + x /iguess) / 2
       }
       return
}

首先是go语言的几种变量声明方式:第一种就是var hellostr = "Hello, Go.\n"这样的,不需要声明类型,由初始值决定变量的类型。第二种则是像tipstr := ""这样的,与上面一种等价。第三种则是像var clacnum float64这种不赋初值得声明方式。float64是go的基本类型之一,没什么好说的。需要注意的是go语言声明过的变量最少必须使用一次,不然编译会报错。老实说这个要求虽然是个很有助于编程规范的要求,但是刚刚写下的变量IDE就在下面画条红线提示你”unused“的感觉很不爽有没有!

接下来是正常的if...else选择结构,花括号还是不能另起一行写,连else if也不能另起一行必须很花括号写在同一行,简直了。go果然是个令人讨厌的家伙。

main下面是个叫做NewtonSqrt的函数,第一个括号里是参数列表,后面是返回值。最后我并没有写出return iguess,因为在声明的时候已经声明了iguess是返回值了。当然也可以写成下面的形式:

func NewtonSqrt(x float64) float64{ //牛顿迭代法求平方根,只是为了学习一下循环
	var iguess float64 = 1
	for math.Abs(iguess * iguess - x) > 1e-12{
	//与for ;math.Abs(iguess * iguess - x) > 1e-12;{}相同
		iguess = (iguess + x /iguess) / 2
	}
	return iguess
}

函数里则是用牛顿迭代法求平方根的循环,for math.Abs(iguess * iguess - x) > 1e-12这句看上去有点怪,为什么是for而不是while呢?go语言里是没有while的,这个for其实省略了两个分号,等价于for;math.Abs(iguess * iguess - x) > 1e-12;。这样看起来就跟C++是一样的了。

运行一下试试看:


到这里似乎结束了,然而若是调皮的人不输入数字而是输入些奇怪的字符串进来呢?


程序倒是没崩溃,但我们显然不应该让这种情况发生。如果是其他语言,你肯定会说try...catch...,然而go语言并没有try...catch。实际上fmt.Scanf这个方法是有返回值的,而且是2个。奇怪么,函数具有有两个返回值。实际上,如果是一个把go当作第一门编程语言在学习的人,你去问他的对函数能返回两个值的感受,他肯定不会回答奇怪的。老实说当年初学C语言的时候我还对函数只能返回一个值感动奇怪,为什么不能直接返回两个值呢?定义个结构体也好把变量指针传进去也好,怎么想都没这个方便。当然熟悉了C之或java后的人,自然是觉得返回两个值奇怪了。其实在其他主流的编程语言里,Lua还有苹果的Swift的函数也是可以返回多个值的。

func Scanf(format string, a ...interface{}) (n int, err error) {
	return Fscanf(os.Stdin, format, a...)
}

前面的int就不说了,后面的err一看就知道是干什么的了。于是将代码改成下面这样:

package main

import (
       "fmt"
       "math"
)

func main(){
       var hellostr = "Hello, Go.\n"
       fmt.Print(hellostr)
       tipstr := "请给我输入一个正数让我来求它的平方根:\n"
       fmt.Print(tipstr)
       var clacnum float64 //如果只写var clacnum,不明确指定类型要报错
       _, err := fmt.Scanf("%g", &clacnum) //_用来占位,忽略不想要的返回值
       if err != nil{
              fmt.Print("不要乱输入奇怪的东西!")
              return
       }
       if clacnum < 0{
              fmt.Print("你在逗我,负数并没有平方根\n")
       }else if clacnum == 0 {
              fmt.Print("0的平方根还是0\n")
       }else {
              fmt.Printf("%g的平方根是:%g\n", clacnum, NewtonSqrt(clacnum))
       }
}

func NewtonSqrt(x float64) (iguess float64){ //牛顿迭代法求平方根,只是为了学习一下循环
       iguess = 1
       for math.Abs(iguess * iguess - x) > 1e-12{
              //for ;math.Abs(iguess * iguess - x) > 1e-12;{}相同
              iguess = (iguess + x /iguess) / 2
       }
       return
}

在接收多个返回值的时候,可以用下划线_来占位,忽略某些返回值,这里我忽略掉了Scanf的第一个int类型的返回值。然后判断err是否为空(nil)。

再运行一下试着输入一些奇怪的东西:


在go语言中,这种处理方式就是go的设计者们推荐的处理方式。当然还有另一种类似try...catch的异常处理:defer, panic, recover,但是go设计者们似乎不推荐使用,理由是将异常与控制结构混在一起会很容易使得代码变得混乱,而他们追求简洁优雅。。。嗯,毕竟是一群不让人把花括号写到下一行的异教徒,才不用理会他们推不推荐呢。
今天就先研究到这里好了。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:galactico411

查看原文:Go语言学习个人小记(一)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1391 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传