Archive for golang

golang接口代码:

package main
import "fmt"
type S struct {
    i int
}
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int){
    p.i = v
}
type I interface{
    Get() int
    Put(int)
}
func f(p I){
    fmt.Println(p.Get())
    p.Put(1)
}
func main(){
    var s S
    f(&s)
    fmt.Println(s.Get())

}
go代码分析: 开始定义了一个具有一个字段和两个方法的结构类型S 14-17行,定义了一个接口类型,他是方法的集合。对于接口I,S是其合法实现,因为结构S定义了I所需的两个方法。 往下,函数f()的参数为接口类型。p能够实现I,则有方法Get()和Put()。函数的功能是 打印字段i的值,和将i至1. main()函数中,因为S是I的合法实现,定义S类型的s,并将指针传入函数f()。 空接口 每个类型都能匹配空接口:interface{}.我们可以定义一个空接口作为参数的函数: func g(something interface{}) int { return something.(I).Get() } 这个函数中参数为interface{}空接口,能接受任何类型。.(I)是类型断言,用于转换something到I类型的接口。如果有这个类型,则可以调用Get()。 例如: s=new(S) fmt.Println(g(s)) 这个是成功的。创建S类型满足I,可以调用Get()。再看下面这个: i := 6 fmt.Println(g(i)) 这能编译通过,但运行会出错,因为内建类型int没有Get()方法。他不能实现I。 这种问题解决办法:
package main
import "fmt"
type S struct {
    i int
}
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int){
    p.i = v
}
type I interface{
    Get() int
    Put(int)
}
func f(p I){
    fmt.Println(p.Get())
    p.Put(1)
}
func g(something interface{}){
    if t,ok := something.(I); ok{
        fmt.Println("I:",t.Get())
    }else if t,ok := something.(int); ok{
        fmt.Println("int:",t)
    }else{
        fmt.Println("not found:",something)
    }
}
func h(something interface{}){
    switch t:=something.(type){
        case I:
            fmt.Println("I:",t.Get())
        case int:
            fmt.Println("int:",t)
        case *S:
            fmt.Println("S:",t.Get())
        default:
            fmt.Println("not found:",something)
    }
}
func main(){
    var s S
    f(&s)
    fmt.Println(s.Get())
    g(&s)
    h(&s)
    i := 6
    g(i)
    h(i)
}
与上面相比添加了函数g()h(),与前面不同,这个没有返回值,我修改了一下把fmt.Println写到函数里面了。 g()函数里的switch语句。变量t保存了something的类型,在switch之外使用(type)是非法的。 这里还发现一个问题: 在type switch里不能使用fallthrough,使用时报错:.\9.go:33: cannot fallthrough in type switch 接口类型的方法 接口定义为一个方法的集合。方法包含实际的代码。换句话说,一个接口就是定义,而方法就是实现。 因此,接收者不能定义为接口类型,这样做的话会引起invalid receiver type ... 的编译器错误。 接收者类型必须是T或*T,这里的T是类型名。T叫做接收者基础类型或简称基础类型。基础类型一定不能使指针或接口类型,并且定义在与方法相同的包中。 接收者指 receiver 接口指针 在Go 中创建指向接口的指针是无意义的。实际上创建接口值的指针也是非法的。 var buf bytes.Buffer io.Copy(buf,os.Stdin) 就会复制标准输入到buf的副本,而不是buf本身。这看起来永远不会是一个期望的结果。

Continue

方法:面向对象的味道 go代码:

package main
import (
    "fmt"
    "math"
)

type Rectangle struct{
    width,height float64
    rarea float64
}
type Circle struct{
    radius float64
}

func (r Rectangle) area() float64{
    return r.width * r.height
}
func (c Circle) area() float64{
    return c.radius * c.radius *math.Pi
}
func (r *Rectangle) setarea(a float64) {
    r.rarea = a
}
func (r Rectangle) setarea2(a float64) (Rectangle){
    r.rarea = a
    return r
}

func main(){
    r1 :=Rectangle{12,2,0}
    r1.setarea(r1.area())
    fmt.Println(r1.rarea)

    r2 := Rectangle{3,4,0}
    newr2 := r2.setarea2(r2.area())
    fmt.Println(r2.rarea)
    fmt.Println(newr2.rarea)
    c1 := Circle{10}
    c2 := Circle{2}
    fmt.Println("r1:",r1.area())
    fmt.Println(r2.area())
    fmt.Println(c1.area())
    fmt.Println(c2.area())

}
结果: D:\code>go run 7.go 24 0 12 r1: 24 12 314.1592653589793 12.566370614359172 代码中有两个同名方法area,他们前面括号里是ReceiverType,称作receiver。 拥有不同receiver的方法是不同的,即使他们有相同的名字。 内建类型不能定义方法,但是可以定义一个有方法的这种类型。如: type ages int setarea()修改了字段rarea的值,比较setarea()和setarea2(),会发现如果不使用指针,赋值操作是给了一个复制版的r, 并不是原来的值。 匿名字段代码:
package main
import "fmt"

type Human struct{
    name string
    age int
    phone string
}
type Employee struct{
    Human//Human类型的匿名字段
    speciality string
    phone string
}

func main(){
    Bob := Employee{Human{"bob",34,"777777-3"},"designer","33-22"}
    fmt.Println(Bob.name)
    fmt.Println(Bob.phone)
    fmt.Println(Bob.Human.phone)
    fmt.Println(Bob.Human.name)
}
运行结果: D:\code>go run 8.go bob 33-22 777777-3 bob 从程序中,当有字段冲突时,我们必须写全字段名称;当没有冲突时,两种方式都可以。

Continue

package 包名约定使用小写字符。包中的函数名以大写字母开头的是可导出函数,小写字母是私有函数。这个规定同样适用于新类型、全局变量。 当包导入(通过import)时,包名成为了内容的入口。在 import "bytes" 之后,导入包的可以调用函数bytes.Buffer。任何使用这个包的人,可以使用同样的名字访问到它的内容,因此这样的包名是好的:短的、简洁的、好记的。 根据规则,包名是小写的一个单词;不应当有下划线或混合大小写。由于每个人都可能需要录入这个名字,所以尽可能的简短。不要提前考虑冲突。 包名是导入的默认名称。 在Go中使用混合大小写MixedCaps或者mixedCaps,而不是下划线区分含有多个单词的名字。 包文档: 每个包都应该有包注释,在package 前的一个注释块。对于多文件包,包注释只需要出现在一个文件前,任意一个文件都可以。 包注释应当对包进行介绍,并提供相关于包的整体信息。这会出现在go doc生成的关于包的页面上,并且相关的细节会一并显示。 每个定义(并且导出)的函数应当有一小段文字描述该函数的行为。 常用包:http://golang.org/pkg/ 指针 go有指针,没有指针运算。一个新定义的没有指向的指针值为nil。

package main
import "fmt"

func main(){
var p *int
fmt.Printf("%v\n",p)
fmt.Printf("%T\n",p)
var i int
p = &i
fmt.Printf("%v\n",p)
*p =8
fmt.Printf("%v\n",*p)
}
结果: *int 0x10dd0018 8 *p++,表示(*p)++,先获取指针指向的值,然后加一。 内存分配 使用new分配内存 new(T)分配了零值填充的T类型的内存空间,返回其地址,一个*T类型的值。返回一个指针,指向新分配的类型T的零值。 用make分配内存 make(T,args)只能创建slice,map和channel,并且返回一个有初始值(非零)的T类型,不是*T.原因是指向数据结构的引用在使用前必须被初始化。 v := make([]int,100) 定义自己的类型
type person struct {
    name string
    age int
}

var P person
P.name = "Tom" // Assign "Tom" to P's name field.
P.age = 25 // Set P's age to 25 (an int).
fmt.Println("The person's name is %s", P.name)
P是一个person类型的变量。
package main
import "fmt"

type person struct{
    name string
    age int
}

func main(){
    a := new(person)
    a.name = "wang"
    a.age = 42
    b := person{"Tom",11}
    c := person{"rose",44}
    fmt.Printf("%v,%v,%v",a,b,c)
}
字段名字大写,可以导出,可以在其他包使用。小写则不可以。

Continue

函数 golang作用域,定义在函数外的变量是全局的,函数内部定义的变量是局部的。如果一个局部变量和一个全局变量有相同的名字,在函数执行的时候,局部变量将覆盖全局变量。

package main
var a=6
func main(){
p()
q()
p()
}

func p(){
println(a)
}
func q(){
    //a := 5//定义变量
    a = 5//变量赋值
    println(a)
}
当在函数内定义是,输出656,而赋值为655. 局部变量仅在定义它的函数时有效。
package main
var a int
func main(){
    a=5
    println(a)
    f()
}

func f(){
    a := 6
    println(a)
    g()
}
func g(){
    println(a)
}
输出565 多值返回。 教程上有趣的例子:
package main

func nextInt(b []byte, i int) (int, int){
    x :=0
    for ; i

定义一个函数,传递一个byte数组和一个位置i,返回两个int类型。

看到golang的格言: 用更少的代码做更多的事

延迟代码defer。
在defer后指定的函数会在函数退出前调用。

func ReadWrite() bool{
file.Open("file")
//do something
if failureX{
file.Close()
return false
}
if failureY{
file.Close()
return false
}
file.Close()
return true
}
可以用defer修改为:
func ReadWrite() bool{
file.Open("file")
defer file.Close()
//do something
if failureX{
return false
}
if failureY{
return false
}
return true
}
函数在退出前会执行file.Close() 延迟的函数是按照后进先出的顺序执行。
for i:=0;i<5;i++{
defer fmt.Print("%d",i)
}
defer可以修改返回值。
package main
func f() (ret int){
    println(ret)
    defer func(){
        ret++
    }()
    return 0
}
func main(){
    println(f())
}
将输出1,ret是一个int型的命名返回值,这个不要看错了。 变参 例子代码:
package main

func myfunc(arg ...int){
    for _,n := range arg{
        println(n)
    }
}
func main(){
    myfunc(1,2,3,4,5)
}
将打印所有参数。arg ...int说明这个函数接受不定数量的参数。参数类型全部是int。在函数体中,变量arg是一个int类型的slice。 函数作为值
package main
import "fmt"
func main(){
    a := func(){
        println("hello")
    }
    a()//
    fmt.Printf("%T",a)
}
先定义一个匿名函数,赋值给a,然后调用函数a(),下面语句是输出a的类型。 接受函数作为参数的函数 回调 Panic和Recover golang没有像Java那样的异常机制:不能抛出异常。作为替代,使用panic和recover机制。这要作为最后手段使用。 没看懂。。。

Continue

array array由[n]定义,n标示array长度,type是类型。 var arr [10]int arr[0] = 42 arr[1] = 13 当向函数内传递一个数组的时候,会获得一个数组的副本,而不是数组的指针。 声明数组时,必须在方括号内输入一些内容:数字或者三个点。三个点,go会自动统计元素个数。 a:= [2][2]int{[2]int{1,2},[2]int{3,4}} a:= [2][2]int{[...]int{1,2},[...]int{3,4}} 或者 a:= [2][2]int{{1,2},{3,4}}//当元素复合声明的类型和外部一致时 slice slice与数组区别,slice可以怎讲啊长度,slice总是指向底层的一个array。slice是指向array的指针。slice是引用类型的,当一个函数需要一个slice参数,函数内对slice修改。slice总是与一个固定长度的array承兑出现。 int:var array[m]int //创建一个m个元素的array。 slice := array[0:n] 几个长度 len(slice)==n cap(slice)==m len(array)==cap(array)==m map 定义map的方法:map[] monthdays := map[string]int{ "Jan":31,"Feb":28, } 最后一个逗号是必须的。 先写到这里了,准备粗略把教程看完。原因是现在用golang学习语法时,太麻烦了。每次都是修改源文件--打开shell--go run,感觉要受不了了,决定写一个像python在shell里一样方便的东西学习语法。

Continue

放在同一行的多个语句,必须用分号分隔。 变量的类型在变量名后面。 var a int var b bool a = 15 b = false 等价: a := 15 b := false 多个var声明可以成组。 var{ x int b bool } 同样适用于const和import。 相同类型的多个变量可以在一行内完成声明: a,b := 20,16 一个特殊的变量名_(下划线),任何赋值给他的值都被丢弃。 _,b := 34,35 golang编译器对声明却未使用的变量报错。 变量名 declared and not used。 布尔类型: true和false 数字类型: int ,根据硬件决定长度。32位硬件是32位,64位为64.也可以明确其长度,完整的整数类型表; int8,int16,int32,int64 byte, uint8,uint16,uint32 uint64.byte是uint8的别名。 浮点型有float32,float64(没有float类型)。 64位的整数和浮点数都是64位的,即便是在32位的架构上。这些类型全部独立,混合这些类型向变量赋值引起编译器错误。 常量:只能是数字、字符串和布尔值。可以使用iota生成枚举值。 const( a = iota b = iota ) 第一个iota表示0,因此a为0,当iota再次进行新的一行使用时,值增加1,b的值为1. 可以省略重复的iota。 const( a = iota b ) 字符串: string s := "hello world!" go中字符串是UTF-8的由双引号包裹。单引号表示一个字符,不是string。一旦给变量赋值,字符串就不能修改了。 一下做法非法: var s string = "hello" s[0] = 'c' go中实现的方法:

s := "hello"
c := []byte(s) //转换s为字节数组
c[0] = 'c' //修改数组
s2 := string(c) //创建新字符串
多行字符串
//错误写法:
s := "starting part"
    + "ending part"
//会被转换成
s := "starting part";
    + "ending part";
//正确写法
s := "starting part" + 
    "ending part"
//另一种写法,使用`反引号作为原始字符串符号
s := `starting part
    ending part`
//最后一个s将包含换行。在原始字符串标识中的值字符是不转义的。
//可以写一个例子,将上面修改为:
s := `starting \n part
ending part`
fmt.Printf("%s",s)
//会发现starting和part间没有换行而是‘\n’
rune :int32别名。用UTF-8进行编码。需要遍历字符串中的字符。可以循环每个字符。 复数:变量类型是complex128(64位虚数部分),complex64(32位虚数部分) 复数写为re+imi,re是实数部分,im虚数部分。 var c complex64 = 5+5i 错误:error。var a error定义a为一个error,a的值为int。

Continue

安装过程很简单,官网有详细文档:http://golang.org/doc/install#windows 前几天还记得可以访问,今天不行了。汗,我直接下载的zip file 解压到c盘,将c:\go\bin添加到系统变量中,安装完成了。。。 下面说说vim的golang配置,下载地址:https://github.com/jnwhiteh/vim-golang,在直接拖到Vim\vimfiles,搞定。官方给的应该和这个一样,没看,http://golang.org/misc/。 上hello world代码:没高亮(决定改掉他)

package main

import "fmt"

func main(){
fmt.Printf("hello world~")
}
编译:go build hello.go 运行: hello 花了五六分钟将插件修改了一下,半支持golang了。golang语法和C比较相似,直接用C的文件修改的,主要修改了一下关键字什么的。现在对golang还不熟悉,没有随便修改。以后发现不正确慢慢修改吧。 当时安装代码高亮插件时,不知道这个东西是在插入数据库的时候修改了文章内容,现在想脱离也必须开着这个插件。想想还是这样吧,插件不更新自己慢慢修改着,反正其他插件也不一定支持,也要修改,只是可能是js实现的

Continue