Effective Go-3

Data

分配

通过new和make分配空间:

  • new分配空间,返回指针
  • make可以进行生成slices,maps,channels,返回类型
package data_test

import "testing"

func TestCreateslices(t *testing.T) {
    s := make([]string, 3)
    s[0] = "c"
    s[1] = "a"
    s[2] = "o"
    t.Log(s)
}

func TestCreatemap(t *testing.T) {
    m := make(map[string]int)
    m["k1"] = 1
    m["k2"] = 2
    t.Log(m)
}

func TestCreatechannel(t *testing.T) {
    messages := make(chan string)
    go func() { messages <- "ping" }()
    msg := <-messages
    t.Log(msg)
}
数组(Arrays)和切片(Slices)

go中更推荐使用切片而不是数组。

  • 数组长度固定
  • 切片可以动态调整容量(cap)
package data_test

import "testing"


func TestCreatearray(t *testing.T) {
    a := [...]int{1, 2, 3, 4}
    t.Log(a)
    s := make([]int, 4)
    s = a[0:4]
    t.Log(s)
    s = append(s, 5)
    t.Log(a)
    t.Log(s)
}

=== RUN   TestCreatearray
    f:\GO\go_test\data\data_test.go:29: [1 2 3 4]
    f:\GO\go_test\data\data_test.go:32: [1 2 3 4]
    f:\GO\go_test\data\data_test.go:34: [1 2 3 4]
    f:\GO\go_test\data\data_test.go:35: [1 2 3 4 5]
--- PASS: TestCreatearray (0.00s)
二维切片(Two-dimensional Slices)

下面是二位数组和二位切片的示例。

type Transform [3][3]float64  // A 3x3 array, really an array of arrays.
type LinesOfText [][]byte     // A slice of byte slices.
Maps

key,value形式的数据结构,key可以是整数,浮点数,复数,字符串,指针,接口,结构体,数组。

切片不能做为key值由于他是变化的。

获取map中value, 可以通过以下方式,可以通过ok值判断,键值对是否存在。

...
if seconds, ok := timeZone[tz]; ok {
    return seconds
}    
log.Println("unknown time zone:", tz)
return 0
Printing

打印方面类似C语言,主要存放在fmt包中,fmt.Printffmt.Fprintffmt.Sprintf

go by example的 String Formatting

package print_test

import (
    "fmt"
    "os"
    "testing"
)

func TestPrintstring(t *testing.T) {
    type point struct {
        x, y int
    }

    p := point{1, 2}
    fmt.Printf("%v\n", p)
    fmt.Printf("%+v\n", p)
    fmt.Printf("%#v\n", p)
    fmt.Printf("%T\n", p)

    fmt.Printf("%t\n", true)

    fmt.Printf("%d\n", 123)
    fmt.Printf("%b\n", 14)
    fmt.Printf("%c\n", 33)
    fmt.Printf("%x\n", 456)
    fmt.Printf("%f\n", 78.9)

    fmt.Printf("%e\n", 1234000000.0)
    fmt.Printf("%E\n", 1234000000.0)

    fmt.Printf("%s\n", "\"string\"")
    fmt.Printf("%q\n", "\"string\"")

    fmt.Printf("%x\n", "hex this")

    fmt.Printf("%p\n", &p)

    fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
    fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)

    fmt.Printf("|%6s|%6s|\n", "foo", "b")
    fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
    s := fmt.Sprintf("a %s\n", "string")
    fmt.Printf(s)
    fmt.Fprintf(os.Stderr, "an %s\n", "error")

}

=== RUN TestPrintstring
{1 2}
{x:1 y:2}
print_test.point{x:1, y:2}
print_test.point
true
123
1110
!
1c8
78.900000
1.234000e+09
1.234000E+09
"string"
"\"string\""
6865782074686973
0xc00001a360
| 1.20| 3.45|
|1.20 |3.45 |
| foo| b|
|foo |b |
a string
an error
--- PASS: TestPrintstring (0.00s)

初始化(Initialization)

  • 常量
  • 变量
  • init函数: 可以在这个函数中做一些初始化的操作。
package init_test

import (
    "fmt"
    "os"
    "testing"
)

type ByteSize float64

const (
    _           = iota
    KB ByteSize = 1 << (10 * iota)
    MB
    GB
    TB
    PB
    EB
    ZB
    YB
)

func (b ByteSize) String() string {
    switch {
    case b >= YB:
        return fmt.Sprintf("%.2fYB", b/YB)
    case b >= ZB:
        return fmt.Sprintf("%.2fZB", b/ZB)
    case b >= EB:
        return fmt.Sprintf("%.2fEB", b/EB)
    case b >= PB:
        return fmt.Sprintf("%.2fPB", b/PB)
    case b >= TB:
        return fmt.Sprintf("%.2fTB", b/TB)
    case b >= GB:
        return fmt.Sprintf("%.2fGB", b/GB)
    case b >= MB:
        return fmt.Sprintf("%.2fMB", b/MB)
    case b >= KB:
        return fmt.Sprintf("%.2fKB", b/KB)
    }
    return fmt.Sprintf("%.2fB", b)
}

func TestConst(t *testing.T) {
    t.Log(ByteSize(10240))
    t.Log(ByteSize(YB))
    t.Log(ByteSize(1000))
}

func TestVarialbes(t *testing.T) {
    var (
        home   = os.Getenv("HOMEPATH")
        user   = os.Getenv("USERNAME")
        gopath = os.Getenv("GOPATH")
    )
    t.Log(home)
    t.Log(user)
    t.Log(gopath)
}

func init() {
    fmt.Fprintf(os.Stdout, "an %s\n", "init")
}

方法

指针vs值

golang中指针也用.而不使用->, 用指针减少函数传递时的内存拷贝, 对原有引用数据有影响。

package method_test

import (
    "testing"
)

type ByteSlice []byte

func TestValuesAppend(t *testing.T) {
    var s ByteSlice
    x := ByteSlice{1, 2, 3, 4}
    s = s.Append(x)
    t.Log(s)

    y := ByteSlice{5, 6, 7, 8}
    s = s.Append(y)
    t.Log(s)
    t.Log(x)
}

func (slice ByteSlice) Append(data ByteSlice) ByteSlice {
    sliceLen := len(slice)
    if sliceLen+len(data) > cap(slice) {
        newSlice := make(ByteSlice, (sliceLen + len(data)*2))
        copy(newSlice, slice)
        slice = newSlice
    }
    slice = slice[0 : sliceLen+len(data)]
    copy(slice[sliceLen:], data)
    return slice
}

func (p *ByteSlice) AppendPoint(data ByteSlice) {
    sliceLen := len(*p)
    if sliceLen+len(data) > cap(*p) {
        newSlice := make(ByteSlice, (sliceLen + len(data)*2))
        copy(newSlice, *p)
        *p = newSlice
    }
    *p = (*p)[0 : sliceLen+len(data)]
    copy((*p)[sliceLen:], data)
}

func TestValuesAppendPoint(t *testing.T) {
    var s *ByteSlice

    x := ByteSlice{1, 2, 3, 4}
    s = &x
    t.Log(s)

    y := []byte{5, 6, 7, 8}
    s.AppendPoint(y)
    t.Log(s)
    t.Log(x)

}


=== RUN TestValuesAppend
f:\GO\go_test\method\method_test.go:13: [1 2 3 4]
f:\GO\go_test\method\method_test.go:17: [1 2 3 4 5 6 7 8]
f:\GO\go_test\method\method_test.go:18: [1 2 3 4]
--- PASS: TestValuesAppend (0.00s)
PASS
ok loop/method 0.644s


=== RUN TestValuesAppendPoint
f:\GO\go_test\method\method_test.go:49: &[1 2 3 4]
f:\GO\go_test\method\method_test.go:53: &[1 2 3 4 5 6 7 8]
f:\GO\go_test\method\method_test.go:54: [1 2 3 4 5 6 7 8]
--- PASS: TestValuesAppendPoint (0.00s)
PASS
ok loop/method 0.114s

可以通过return或者指针两种方式更新切片的值。另外,对于结构定义的方法通过指针和结构都可以调用, 只是如果方法定义是使用结构模式 func (p struct XX) funcXX(xx), 那么参数传入方式时会进行结构复制,使用指针 func (p *struct XX) funcXX(xx)则不会。

 

参考及引用

Photo by Philippe Donn from Pexels

https://golang.org/doc/effective_go

Comments are closed.