1. 程式人生 > >從go語言看“如何快速入門一門語言”

從go語言看“如何快速入門一門語言”

必應-精美桌布

最近在看go語言的語法,使用Python和PHP這種動態語言習慣了之後,再回頭使用靜態語言,多多少少有點彆扭。看了go的語法之後,go給我的最直觀的感受就是C+Python,為什麼這麼說呢?因為從語言的型別角度看,go語言衍生自C語言,很多地方有C語言的身影;但是從易用性和強大的第三方庫支援的角度,go語言和Python有異曲同工之妙。

對我來說,學習一門語言,語法可能不是最為重要的,但是這也不是說語法一點都不重要。從陌生到簡單的應用大致會有如下階段:

  • 先大致瀏覽下這種語言的語法,對變數型別,流程控制,介面實現,然後這個階段結束後,我們就可以寫出簡單的指令碼了。

  • 再就是對各種庫的熟悉,都說站在巨人的肩膀桑,才能看得更遠。而go語言有大量的第三方優秀的庫,可以大大的簡化我們的編碼量,並能提高程式的穩定性。

  • 寫工具。嚴格來說,第三個階段和第二個階段的界限不是很明顯。寫自己的一些工具的時候往往會發現更優秀的實現,所以我們拿過來直接用就好,消化吸收到我們自己的程式碼中。

學習一門語言的目的,實際上就是為了應用。我個人比較推崇的方式就是寫工具。漫無目的的去生搬硬套每一個程式設計正規化,意義不是很大。相反,寫工具的過程中往往會伴隨著各種需求,而這些需求基本上都是我們自己來定義的,所以會有很大的靈活性,對實現每一個小功能,我們都會有比較清晰的認識。這個過程也能不斷提升我們應用一門語言的能力。

至於寫什麼工具,完全有我們自己而定。撇開具體的實現,我們只需要進行大量的頭腦風暴即可。天馬行空的將我們的設想落地,這就夠了。也許你會說,我沒有什麼特別的需求啊,我要做什麼呢?其實,需求是無處不在的,只要去想,總會有的。

另外一個比較好的應用一門語言的例子就是給自己找樂子。當然這個樂子不能保證對每一個人都適用。比如有的人就是喜歡刷題,寫演算法,現在有很多刷題網站,牛客網,LeetCode等等,都是比較好的去處;但是有的人,比如我對寫演算法題就不怎麼感興趣,相反,我對寫爬蟲,做小工具情有獨鍾,所以我只需要把我的想法用這門語言去實現就好了,這也是一種鍛鍊,而且從頭到尾自身的參與度都很高,對語言的認識也能更透徹一點。當然了,這也只是我自己的看法,其他人也許不這麼看。

下面通過一個具體的例子,來“走一遍”應用語言的道路。

需求:給我的Mac弄一些高清桌布

頭腦風暴:

  • 從哪兒能弄到高清桌布呢?
  • 找到桌布了怎麼能搞到本地呢?
  • 怎麼每天自動換桌布呢?

尋求解決方案:

  • 必應的官網每天都有1920*1080解析度的高清圖片,很精美,就從這搞吧。
  • 剛看了看go語言,就用go語言進行實現吧,這裡先不管具體實現,留給後面去思考。
  • Mac有一個功能可以選擇一個資料夾,定時切換桌布。

思考部分:

思考部分是對具體實現的處理,這裡就是如何將必應官網的圖片下載到本地。這就涉及到網路請求,正則表示式,檔案操作。為了更好的使用程式碼,可以適當的放開一些處理,比如儲存位置可以指定,新增額外的引數,這或許就會涉及到命令列引數的使用等等。總之,只要我們具體的去思考實現,就總會有新的需求,伴隨而來的就是更多的思考,以及對庫和語言的更好的認識,這對於鍛鍊自己也有一定的幫助。

因此,針對每一個具體的細節,我們就可以將問題細化到每一個小模組。網路請求可以通過http模組解決;正則表示式通過regex處理,字串拼接、分割等可以使用strings模組,檔案操作使用ioutil,os模組處理,命令列引數我們還會使用flag模組來解決等等。

具體實現:
思考完具體的細節,我們就可以著手進行實現了。對於每一個小功能,我們就可以切分到一個個的函式中。下面簡單來看下程式碼:

package main

import (
    "net/http"
    "log"
    "io/ioutil"
        "regexp"
    "strings"
    "os"
    "io"
    "bytes"
    "fmt"
    "flag"
)

// 獲取必應首頁的背景大圖
func DownloadHTML(url string) string {
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    bytes, error := ioutil.ReadAll(resp.Body)
    if error != nil {
        log.Fatal(error)
    }
    return string(bytes)
}


func GetImageURL(content string) string{
    reg := regexp.MustCompile(`url:(.*?).jpg`)
    ret := reg.FindString(content)
    if len(ret) > 5 {
        // url: "/az/hprichbg/rb/OtterChillin_ZH-CN11488013572_1920x1080.jpg
        ret = "https://cn.bing.com" + strings.Split(ret, "\"")[1]
    }else{
        log.Fatal("解析圖片連結出錯!")
        ret = ""
    }
    return ret
}

func DownloadImage(url string, folderpath string) int {
    splits := strings.Split(url, "_")
    imagename := splits[len(splits)-2] + ".jpg"

    _, err := os.Stat(folderpath)
    if err != nil {
        log.Fatal("路徑:" + folderpath + " 不存在")
    }
    if strings.LastIndex(folderpath, "/") != len(folderpath)-1 {
        folderpath = folderpath + "/"
    }
    imagename = folderpath + imagename

    // 下載圖片,儲存到本地
    resp, error1 := http.Get(url)
    if error1 != nil {
        log.Fatal(error1)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    f, error2 := os.Create(imagename)
    if error2 != nil {
        log.Fatal("檔案建立失敗:", error2)
    }
    io.Copy(f, bytes.NewReader(body))
    // 沒有異常,就是正常
    return 0
}

func main() {
    // 圖片輸出路徑
    folder := flag.String("o", "/tmp", "圖片輸出路徑")
    flag.Parse()
    url := "https://cn.bing.com/"
    html := DownloadHTML(url)
    fmt.Println("輸出檔案路徑:", *folder)
    imgurl := GetImageURL(html)
    if ret := DownloadImage(imgurl, *folder); ret==0 {
        fmt.Println("桌布下載成功啦~")
    }else{
        fmt.Println("對不起,桌布下載失敗!")
    }
}

如此,便可以簡單完成我們的預期目標。下面將程式碼編譯出來,通過crontab來每天自動化幫我們抓取圖片。

go build daily-bing-wallpaper.go

然後就可以在同級目錄下獲得daily-bing-wallpaper名稱的二進位制檔案,這樣就可以直接執行了。
下面寫個crontab,因為我是10點上班,所以安排到10:50去執行就挺合適的。

50 10 * * * cd /abolutepath && ./daily-bing-wallpaper -o /outputpath >> /tmp/get-wallpaper.log 2>&1

最後再設定下Mac桌布的資料夾即可。

總的來說,找到自己的興趣點,然後去一點點的實現就好了,過程中對我自己熟悉,應用一門語言也會大有脾益。