天天看點

go語言用什麼編譯器_Go語言技巧,多用總比少用好

有幾種非正常方式來引入包(package)。接下來我會使用fmt來作為例子:

  • import format "fmt" - 為fmt創造一個别名。把代碼中所有使用到fmt的内容用format.代替fmt.
  • import . "fmt" - 允許包内的内容不加fmt字首而被被直接引用
  • import _ "fmt" - 阻止編譯器為引入fmt卻不使用裡面的内容做引發的警告,執行package中的初始化函數。提醒一句,在這種情況下fmt是不可調用的
go語言用什麼編譯器_Go語言技巧,多用總比少用好

Goimports

指令goimports可以更新您的Go導入行,添加缺少的行,并删除未引用的引導行。

它擁有和gofmt(插入式替換)相同的能力,但是goimports額外增加了修複imports的功能。

組織

Go是一種相對來說易學習的程式設計語言,但對于開發者來說,起初接觸這門語言最困難的事情就是如何組織代碼。scaffolding是人們喜歡Rails的原因之一,它可以給新晉的開發者清晰的方向,讓他們明白在哪裡插入代碼,應該遵循怎樣的程式設計風格。

作為擴充,Go使用go fmt這樣的工具來提供開發者相同的功能。同樣地,Go的編譯器非常嚴格,它不會去編譯沒有使用的變量,或者沒有使用的import聲明。

自定義構造函數

我經常聽到别人問,“我什麼時候應該使用像NewJob這樣的自定義構造函數?”,我的回答是“大多數情形下你沒必要這麼做”。然而,當你需要在初始化的時候就設定值,且你有一些預設值的時候,這就最好使用一個構造函數。在這個例子中,構造函數就比較有意義了,是以我們用如下的代碼可以建構一個預設的logger:

package mainimport ("log""os")type Job struct {Command string*log.Logger}func NewJob(command string) *Job {return &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}}func main() {NewJob("demo").Print("starting now...")}
           

把代碼分解到不同的package中

參考這篇部落格重構Go代碼,第一部分就講了package的組織。

以工程Gobot為例,它可以被分割為一個核心package和一些其他package。gobot的開發者們準備每個部分放在自己的package裡。經過讨論,他們選擇把所有的官方庫放在同一個repository下,讓import路徑變得幹淨而富有邏輯。

是以,他們不打算把路徑設定為:

github.com/hybridgroup/gobotgithub.com/hybridgroup/gobot-spherogithub.com/hybridgroup/gobot-...
           

而是設定為

github.com/hybridgroup/gobotgithub.com/hybridgroup/gobot/spherogithub.com/hybridgroup/gobot/...
           

現在package的名字不再是冗長的gobot-sphero,而變成了簡要的sphero。

集合(Sets)

在其他的程式語言中,經常會有一種資料結構叫做sets,它允許把元素存入,但是不允許重複。Go并不直接支援這種結構,但是這個結構在Go裡面的實作并不困難。

// UniqStr returns a copy if the passed slice with only unique string results.func UniqStr(col []string) []string {m := map[string]struct{}{}for _, v := range col {if _, ok := m[v]; !ok {m[v] = struct{}{}}}list := make([]string, len(m))i := 0for v := range m {list[i] = vi++}return list}
           

Playground連結

在這裡,我會使用一些非常有意思的花招。首先,對空結構的映射:

m := map[string]struct{}{}
           

我們建立了一個map,這可以確定key是獨一無二的,而相關聯的value其實是我們不關心的。

我們當然可以使用:

m := map[string]bool{}
           

但是,使用空結構體可以達到同樣的效率,同時不會占用額外的記憶體。

第二個花招的意味更為深遠:

if _, ok := m[v]; !ok { m[v] = struct{}{}}
           

這裡做的事情就是确認map m中的某個值是否存在,而不關心value本身。如果發現沒有對應的值,就去加一個。當然,不去驗證直接加好像也沒有什麼差別。

一旦我們擁有了一個充滿獨一無二key的map以後,就可以把他們放到一個切片裡,傳回結果了。

這裡有一段測試代碼,正如你所見,這裡使用了一個符合Go語言單元測試風格的表格測試:

func TestUniqStr(t *testing.T) {data := []struct{ in, out []string }{{[]string{}, []string{}},{[]string{"", "", ""}, []string{""}},{[]string{"a", "a"}, []string{"a"}},{[]string{"a", "b", "a"}, []string{"a", "b"}},{[]string{"a", "b", "a", "b"}, []string{"a", "b"}},{[]string{"a", "b", "b", "a", "b"}, []string{"a", "b"}},{[]string{"a", "a", "b", "b", "a", "b"}, []string{"a", "b"}},{[]string{"a", "b", "c", "a", "b", "c"}, []string{"a", "b", "c"}},}for _, exp := range data {res := UniqStr(exp.in)if !reflect.DeepEqual(res, exp.out) {t.Fatalf("%q didn't match %q", res, exp.out)}}}
           

經過測試發現,并非每次都能夠成功,而是有機率的。因為map是使用hashmap實作的,使用range進行周遊的時候,其周遊順序和字元串的内容沒有必然聯系,是以此test有可能失敗。在進行DeapEqual比對的時候,可能會爆出類似于["b" "c" "a"] didn't match ["a" "b" "c"]的錯誤。當然,在Playground中,每次執行的上下文環境一模一樣,是以這裡的test是總能通過的。

看go語言,關注VX公衆号:studytogo

聲明:轉載此文是出于網絡的。若有來源标注錯誤或侵犯了您的合法權益,請作者持權屬證明與本網聯系,我們将及時更正、删除,謝謝。