Effective Go-3

Data

分配

通过new和make分配空间:

  • new分配空间,返回指针
  • make可以进行生成slices,maps,channels,返回类型
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
}
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) }
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)
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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)

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type Transform [3][3]float64 // A 3x3 array, really an array of arrays.
type LinesOfText [][]byte // A slice of byte slices.
type Transform [3][3]float64 // A 3x3 array, really an array of arrays. type LinesOfText [][]byte // A slice of byte 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值判断,键值对是否存在。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
if seconds, ok := timeZone[tz]; ok {
return seconds
}
log.Println("unknown time zone:", tz)
return 0
... if seconds, ok := timeZone[tz]; ok { return seconds } log.Println("unknown time zone:", tz) return 0
...
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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函数: 可以在这个函数中做一些初始化的操作。
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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")
}
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") }
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中指针也用.而不使用->, 用指针减少函数传递时的内存拷贝, 对原有引用数据有影响。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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.