Go 由于不支持泛型而臭名昭著,但最近,泛型已接近成为现实.Go 团队实施了一个看起来比较稳定的设计草案,并且正以源到源翻译器原型的形式获得关注.本文讲述的是泛型的最新设计,以及如何自己尝试泛型.
例子
FIFO Stack
假设你要创建一个先进先出堆栈.没有泛型,你可能会这样实现:
type?Stack?[]interface{}func?(s?Stack)?Peek()?interface{}?{
return?s[len(s)-1]
}
func?(s?*Stack)?Pop()?{
*s?=?(*s)[:
len(*s)-1]
func?(s?*Stack)?Push(value?interface{})?{
*s?=?
append(*s,?value)
但是,这里存在一个问题:每当你 Peek 项时,都必须使用类型断言将其从 interface{} 转换为你需要的类型.如果你的堆栈是 *MyObject 的堆栈,则意味着很多 s.Peek().(*MyObject)这样的代码.这不仅让人眼花缭乱,而且还可能引发错误.比如忘记 * 怎么办?或者如果您输入错误的类型怎么办?s.Push(MyObject{})◆ 可以顺利编译,而且你可能不会发现到自己的错误,直到它影响到你的整个服务为止.
通常,使用 interface{} 是相对危险的.使用更多受限制的类型总是更安全,因为可以在编译时而不是运行时发现问题.
泛型通过允许类型具有类型参数来解决此问题:
type?Stack(type?T)?[]Tfunc?(s?Stack(T))?Peek()?T?{
func?(s?*Stack(T))?Pop()?{
func?(s?*Stack(T))?Push(value?T)?{
这会向 Stack 添加一个类型参数,从而完全不需要 interface{}.现在,当你使用 Peek() 时,返回的值已经是原始类型,并且没有机会返回错误的值类型.这种方式更安全,更容易使用.(译注:就是看起来更丑陋,^-^)
此外,泛型代码通常更易于编译器优化,从而获得更好的性能(以二进制大小为代价).如果我们对上面的非泛型代码和泛型代码进行基准测试,我们可以看到区别:
type?MyObject?struct?{
X?
int
var?sink?MyObjectfunc?BenchmarkGo1(b?*testing.B)?{
for?i?:=?0;?i?b.N;?i◆◆?{
var?s?Stack
s.Push(MyObject{})
s.Pop()
sink?=?s.Peek().(MyObject)
var?s?Stack(MyObject)
sink?=?s.Peek()
结果:
在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍.
合约(Contracts)
上面的堆栈示例适用于任何类型.但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码.例如,你可能希望堆栈要求类型实现 String() 函数
检查程序的执行路径和当前状态是非常有用的调试手段.核心文件(core file)包含了一个运行进程的内存转储和状态.它主要是用来作为事后调试程序用的.它也可以被用来查看一个运行中的程序的状态.这两个使用场景使调试文件转储成为一个非常好的诊断手段.我们可以用这个方法来做事后诊断和分析线上的服务(production services).
在这篇文章中,我们将用一个简单的hello world网站服务作为例子.在现实中,我们的程序很容易就会变得很复杂.分析核心转储给我们提供了一个机会去重构程序的状态并且查看只有在某些条件/环境下才能重现的案例.
在我们开始前,需要确保核心转储的ulimit设置在合适的范围.它的缺省值是0,意味着最大的核心文件大小是0.我通常在我的开发机器上将它设置成unlimited.使用以下命令:
此时此刻呢,你需要在你的机器上安装 delve .
下面我们使用的 main.go 文件.它注册了一个简单的请求处理函数(handler)然后启动了HTTP服务.
让我们编译并生产二进制文件.
现在让我们假设,这个服务器出了些问题,但是我们并不是很确定问题的根源.你可能已经在程序里加了很多辅助信息,但还是无法从这些调试信息中找出线索.通常在这种情况下,当前进程的快照会非常有用.我们可以用这个快照深入查看程序的当前状态.
有几个方式来获取核心文件.你可能已经熟悉了奔溃转储(crash dumps).它们是在一个程序奔溃的时候写入磁盘的核心转储.Go语言在缺省设置下不会生产奔溃转储.但是当你把 GOTRACEBACK 环境变量设置成"crash",你就可以用 Ctrl◆backslash 才触发奔溃转储.如下图所示:
上面的操作会使程序终止,将堆栈跟踪(stack trace)打印出来,并把核心转储文件写入磁盘.
另外个方法可以从一个运行的程序获得核心转储而不需要终止相应的进程. gcore 可以生产核心文件而无需使运行中的程序退出.
根据上面的操作,我们获得了转储而没有终止对应的进程.下一步就是把核心文件加载进delve并开始分析.
差不多就这些.delve的常用操作都可以使用.你可以backtrace,list,查看变量等等.有些功能不可用因为我们使用的核心转储是一个快照而不是正在运行的进程.但是程序执行路径和状态全部可以访问.
这选择显然是因人而异的..至于怎么选,要看你是初学者,还是老手?..对性能有要求,还是没要求?
如果是完全没有基础,我建议哪个都不选,如果非要选一个,那就选PYTHON..如果你是初学者,把网上的教程看个遍,再买上几本书...你所学会的也仅仅是语法,而根本不会编程...因为这些教程,也仅仅是教你语法,而没有教你编程..你甚至把网上的教程看个精光,却连个最基本的OA系统都做不出来...只能在一个黑乎乎的控制台上,打印一堆破字符..
-------网上的所有教程都会教你的:
怎么定义一个变量?怎么在控制台打印变量?
怎么写一个循环?怎么在控制台打印一堆变量?
怎么写一个函数?怎么在控制台打印返回值?
怎么创建一个对象?怎么在控制台打印对象属性?
------高级一点的教程,会教你的:
怎么用PYTHON的模块,写一个爬虫?
怎么用RUBY的ROR框架,获取一个表单?
怎么用GO的beego,写一个博客?
-------而这些的教程,从来不教你的:
面向对象有什么用? 委托是什么?事件是什么? 工厂模式,单例模式,观察者模式,这些都是啥?套接字是啥?UDP是啥?TCP/IP是啥?二叉树是什么玩意?状态机又是什么玩意?啥叫逆变?啥叫协变?啥叫异步?啥叫反射?
---------------------------------------------------------------------------------------------
如果一套教程,要把这些都讲明白...可能需要上千集...所以这些教程,都跳过了这些内容..但如果你不明白这些,就根本学不会编程...如果你打算学一门语言,而手上只有几十集教程,外加三五本书...那你只能学会玩控制台...
所以初学者选择一门语言,首先要保证这门语言作为主要开发语言,常年被公司使用,这样才能真正学会编程.然而这三门语言都不具备这样的特点.它们通常都是被当成第二语言,做一些辅助开发的工作.其中Python只在极少数情况下,才被用来作为主要开发语言.至于Go与Ruby,我目前还没听说过它们有被当作主要开发语言的例子.我所推荐的是从C#和JAVA两者之间,二选一...学精其中一门之后,再来考虑PYTHON或GO作为第二语言...不然无论你选哪个,都几乎不可能靠一门语言找到工作.
举例:我的机器:
GOPATH= c:\go;c:\go\src;F:\workspace\goSample01;
GOBIN=c:\go\bin;F:\workspace\goSample01\bin;
其中,c:\go是go的安装路径;
F:\workspace\goSample01是我写的go语言项目的工程目录;
F:\workspace\goSample01\bin是go语言项目的工程目录下的可执行文件路径;
注意:这个基本环境不包含开发工具,也不能直接编译带C代码的go程序.
"fmt"
"github.com/astaxie/beedb"
_ "github.com/ziutek/mymysql/godrv"
为了编译该a.go文件,需要启动Console.bat,然后在该命令行窗口,进入c:\go\src目录下,执行go getgithub.com/astaxie/beedb
Go get github.com/ziutek/mymysql/godrv .
配置goclipse(可选)
(如果不喜欢eclipse开发工具,请跳过这个配置.)
packagemainimport"fmt"func main(){ fmt.Printf("hello, world")}
配置gocode(可选)
如果不需要go语法辅助和eclipse里面的(按ALT◆/)弹出go语言自动辅助功能,请跳过这个配置.
从开发工具这块看,go语言还不够成熟,开发工具都还不完善,有待改进.
Google有个在线运行go语言的教程(),很不错.支持在web上直接运行大部分的go程序,想了解这个教程的源代码的朋友可以通过以下方式获取.如果没兴趣,可以跳过这个步骤.
hg clone
编译带调用C代码的go文件(可选)
set GOOS=windows
set GOROOT=c:\go
set GOBIN=%GOROOT%\bin
set GOPATH=%GOROOT%;F:\workspace\goSample01;
例如:
go build -compiler gccgo test_c.go
运行调用C代码的go文件(可选)
①.、testc.go.
创建rand目录,然后在rand里面创建testc.go. 代码如下:
package rand
/*
//
#include stdio.h
*/
import "C"
func PrintHello() {
C.puts(C.CString("Hello, world\n"))
在rand下创建a.go.代码如下:
import "fmt"
func SayHello(name string){
fmt.Println(name)
在rand的上一级创建test_import.go.代码如下:
package main
import "./rand"
func main(){
rand.SayHello("tom")
rand.PrintHello()
go run test_import.go
在测试其它几个C代码的时候,发现windows版本的cgo还有些编译问题,同样的代码转移到苹果的XCODE下就没有问题.后来终于发现原因了,原来有些例子是unix平台下的,而在windows平台下,方法名和参数需要做调整.
例如:下面代码在windows下编译报一堆错误.
#include stdlib.h
func Random() int {
return int(C.random())
func Seed(i int) {
C.srandom(C.uint(i))
这里需要把return int(C.random()) 修改为"return int(C.rand())"
C.srandom(C.uint(i))修改为"C.srand(C.uint(i))"编译就OK了.
以上就是土嘎嘎小编为大家整理的go语言秒杀辅助相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!