go学习记录——第九天
精密计算和big包
众所周知编程实现的计算一般来说是不准确的,会出现float64
的最多15位等等情况。而当我们对精度有非常严格的要求的时候就不能使用浮点数了,毕竟在内存里是近似表示的。
那么我们就得用自带的big
包,这个是包含在math
包下的。举几个例子
big.Int
用来表示大整数big.Rat
用来表示大有理数(可以表示分数2/5或者小数3.1415926,不能表示π这种无理数)big.NewInt(n)
用来表示大的整型数字,其中n
为int64
类型整数big.NewRat(n, d)
用来构造大有理数,其中n
为分母d
为分子,且均为init64
类型
前面提到的数字类型其实包含了我们常见的所有数字类型,不管是整数浮点数有理数都算,只要我们的内存够大够能装,实际上都是可以装得下的能够运行的。但是这里需要说明的是由于更大的内存需要更多的开销,故而处理时间上来看会比我们内置的数字类型慢很多(当然客户不介意时间的话那就无所谓了)。
由于Golang
不支持运算符重载,所以所有的大数字类型都有像Add()
Mul()
这些方法,主要作用就是作为receiver
的整数和有理数,大多数情况下它们会修改receiver
并以reciver
作为返回结果。由于没必要创建big.Int
类型的临时变量来存放中间结果,所以运算可以被链式的调用,故而可以节省内存(再牵扯到前面的消耗内存,这不妥妥风险对冲)
示例:
1 |
|
输出结果:
1 |
|
自定义包和可见性
这部分就是说下怎么去写自己的包
首先命名需要使用不含_
的小写字母来命名,这里举一个小例子(抄的),以此来说明包是如何相互调用以及可见性是如何实现的。
在目录下有个叫package_mytest.go
(address)的程序,其使用了自定义包pack1
中的pack1.go
的代码,最后是生成了pack1.a
并存放在pack1
目录下。
示例pack1.go
1 |
|
这个就包含了一个整型变量Pack1Int
和一个返回字符串的函数ReturnStr
,由于没有main
函数,所以运行了也无事发生。
在主程序package_mytest.go
中这个包通过声明被导入
1 |
|
import的一般格式如下:
1 |
|
for example:
1 |
|
路径指的是目录的相对路径
1 |
|
输出结果:
1 |
|
根据实际检验,./pack1
引用嘎嘎报错,所以还是老老实实写绝对路径吧。
1 |
|
上述代码试图访问一个没有引用的变量或函数,就会报错(有一说一,这个错误我前面遇到了,忘记加引用了)
1 |
|
需要注意的是,主程序利用的包都需要在主程序之前被编译,且主程序中引用的每个东西都需要以pack1.Item
的形式来使用。
在使用.
作为别名时,可以不通过包名来使用其中的项目,例如test := ReturenStr()
具体形式为
1 |
|
然后在使用_
作为别名时将导入包的副作用(没明白,我猜大概是只对包内容进行初始化,然后主程序无法进行调用相关函数或变量,不确定后面再研究研究)
导入外部安装包
如果想在程序中引入一个或多个外部包,就必须使用go install
在本地机器上进行安装。
如果想使用被托管在Github
Google Code
Launchpad
上的包,则需要使用类似如下命令:
1 |
|
将一个名为codesite.ext/author/goExample/goex
的map安装在$GOROOT/src/
目录下
通过这种方式一次性安装并导入代码
1 |
|
综上,如果想使用被托管的包,则需要使用URL作为路径,并将包名放在路径前。
包的初始化
程序的执行开始于包,初始化main
包然后调用main()
函数。
在一个包中,可能有多个init()
函数,且他们的执行是无序的。
init()
函数是不能被调用的(只是不能被我们显性调用,他会在包被导入时自动被调用,故而我们在代码中是不能直接写init()
来调用的。
导入的包在包自身被初始化前被初始化(当前包导入了其他包,则在当前包被初始化前其他包会先被初始化,其中包括分配包级变量的初始值和被导入包中的init()
函数。在被导入包初始化完成后,当前包才会进行初始化)
为自定义包使用godoc
godoc
工具在显示自定义包中的注释也有很好的效果(虽然但是,那部分内容我是学的别的教程的,后续再补吧,先贴个链接第3.6节);注释必须以//
开始并无空行放在声明前(包,类型,函数)。godoc
会为每个文件生成一系列的网页。
例如:
- 在doc_examples目录下有第11.7节中的用来排序的go文件,文件中有一些煮熟(文件需未编译)
- 命令行输入
godoc -http=:6060 -goroot="."
(.
指当前路径,-goroot
参数可以是/path/to/my/package1
这样的形式指出package1
在源码中的位置或接受用冒号形式分隔的路径,吴根目录的路径为相对于当前路径的相对路径) - 浏览器打开
https://localhost:6060
完成后就能看到本地的godoc
页面,从左到右依次显示出目录中的包
1 |
|
下面是链接到源码和所有对象时有序概述(很好的浏览和查询源代码的方式),连同文件/注释
sort
包
1 |
|
1 |
|
如果在团队内,将源代码树存储在网络硬盘上,就可以使用godoc给所有团队成员连续支持文档。通过设置sync_minutues = n
,就可以设置为每n
分钟更新一次
使用 go install 安装自定义包
go install
是 Go 中自动包安装工具,如果需要将包安装到本地它就会从远端仓库下载包:检出、编译和安装一气呵成。
在包安装之前的先决条件是要自动处理包自身依赖关系的安装(和python倒是一样,不过python通过pip
或者coda
安装都会自动去得到没安装的依赖包然后安装)
go install
使用了GOPATH
变量(参考第2.2节)
远端包(参考前面内容)
假设我们要安装tideland
(包含了很多帮助示例,参考项目主页)
由于我们需要创建目录在 Go 安装目录下,所以我们需要使用到root
or su
(Linux的,不知道的叉出去)
确保 Go 环境变量已经设置在 root 用户下的 ./bashrc
文件中(好熟悉的东西,之前搞Java也设置过,没写过Java,就是因为Spark和Hadoop要用)。
使用命令go install tideland-cgl.googlecode.com/hg
进行安装。
可执行文件hg.a
被放在了$GOROOT/pkg/linux_amd64/tideland-cgl.googlecode.com
目录下,源码文件被放在了 $GOROOT/src/tideland-cgl.googlecode.com/hg
目录下,同样也有 hg.a
放置在 _obj
的子目录下。
现在就可以在 Go 的代码中使用包中的功能了,就比如使用报名cgl
导入
1 |
|
从Go 1开始 go install
安装Google Code 的导入路径形式是 "code.google.com/p/tideland-cgl"
升级到新版本的 Go 之后本地安装包的二进制文件将被全部删除。如果想更新,重编译、重安装所有的 Go 安装包可以使用:
go install -a
。
由于 Go 的版本发布的非常频繁,所以还是需要注意发布版本和包的兼容性的(python实在不行搞个coda虚拟环境还能设置个指定版本,go不知道有没有类似的功能,理论上应该有吧)。Go 1之后都是自己编译自己了
go install
同样可以使用 go install
编译链接并安装本地自己的包(详细见下一节)