日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

Go 語言標準庫中最常用的標識符是什么?

 

這篇文章是來自最新 justforfunc 中同標題的一段。這個程序的代碼可以在 justforfunc 倉庫 中找到。

問題陳述

想象一下,對于下面的代碼段,你如何將其中所有的標識符都提取出來。

package main
import "fmt"
func main() {
 fmt.Println("Hello, world")
}

我們期望可以得到一個包含 mainfmt 和 Println 的列表。

標識符到底是什么?

為了回答這個問題, 我們需要了解一下有關計算機語言的理論知識。 但只要一點就足夠了,不用擔心有多復雜。

計算機語言,是由一系列有效的規則組成的。比如下面這個規則:

IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .

上面這個規則告訴我們 if 語句在 Go 語言中的樣子。“if”“;”, 和 “else” 是幫助我們理解程序結構的關鍵詞。與此同時,還有 Expression Block, SimpleStmt 之類的其他規則。

這些規則組成的集合就是語法,你可以在 Go 語言規范中找到它們的詳細定義。

這些規則不是簡單的由程序的單個字符定義的,而是有一系列 token 組成。 這些token除了像 if 和 else 這樣的原子 token 外, 還有像整數 42,浮點數 4.2 和字符串 “hello” 這樣的復合 token, 以及像 main 這樣的標識符。

但是,我們是怎么知道 main 是一個標識符,而不是一個數字呢? 原來它也是有專門的規則來定義的。如果你讀過 Go 語言規范中的標識符部分,你就會發現如下的規則:

identifier = letter { letter | unicode_digit } .

在這條規則中,letter 和 unicode_digit 不是 token 而是字符。 所以有了這些規則,就可以寫一個程序來逐個字符地分析,一旦檢測到一組字符匹配到某一條規則,就 “發射”(emits) 出一個 token。

所以,如果我們以 fmt.Println 為例, 它可以產生這些 token:標識符 fmt“.”, 以及標識符 Println。 這是一個函數調用嗎? 在這里我們還無法確定,而且我們也不關心。它的結構就是一個序列,表明 token 出現的順序。

Go 語言標準庫中最常用的標識符是什么?

 

這種能夠將給定的字符序列生成 token 序列的程序被稱為掃描器。Go 標準庫中的 go/scanner 就自帶一個掃描器。它生成的記號定義在 go/token 里。

使用 go/scanner

我們已經了解了什么是掃描器,那它如何使用呢?

從命令行中讀取參數

讓我們先從一個簡單程序開始,將傳給它的參數打印出來:

package main
import (
 "fmt"
 "os"
)
func main() {
 if len(os.Args) < 2 {
 fmt.Fprintf(os.Stderr, "usage:nt%s [files]n", os.Args[0])
 os.Exit(1)
 }
 for _, arg := range os.Args[1:] {
 fmt.Println(arg)
 }
}

接下來,我們需要掃描從參數傳進來的文件:需要先創建一個新的掃描器,然后用文件的內容來初始化。

打印每個 token

在我們調用 scanner.Scanner 的 Init 方法之前,需要先讀取文件內容,然后為每個掃描過的文件創建一個 token.FileSet 以便來保存 token.File

掃描器一經初始化,我們就能調用其 Scan 方法來打印 token。 一旦我們得到一個 EOF(End Of File) token,就說明達到文件末尾了。

fs := token.NewFileSet()
for _, arg := range os.Args[1:] {
 b, err := ioutil.ReadFile(arg)
 if err != nil {
 log.Fatal(err)
 }
 f := fs.AddFile(arg, fs.Base(), len(b))
 var s scanner.Scanner
 s.Init(f, b, nil, scanner.ScanComments)
 for {
 _, tok, lit := s.Scan()
 if tok == token.EOF {
 break
 }
 fmt.Println(tok, lit)
 }
}

統計 token

太棒了,我們已經能夠打印出所有的 token 了,但是我們還需要跟蹤每個標識符出現的次數,然后按照出現次數排序,并打印出前 5 位。

在 Go 中,實現以上需求的最好的方法是用一個 map,讓標識符來做 key, 其出現次數做 value。

每當一個標識符出現一次,計數器就加一。最后,我們將 map 轉換為一個能夠排序和打印的數組。

counts := make(map[string]int)
// [code removed for clarity]
for {
 _, tok, lit := s.Scan()
 if tok == token.EOF {
 break
 }
 if tok == token.IDENT {
 counts[lit]++
 }
}
// [為了閱讀清晰,移除部分代碼]
type pair struct {
 s string
 n int
}
pairs := make([]pair, 0, len(counts))
for s, n := range counts {
 pairs = Append(pairs, pair{s, n})rm -f 
}
sort.Slice(pairs, func(i, j int) bool {
 return pairs[i].n > pairs[j].n
})
for i := 0; i < len(pairs) && i < 5; i++ {
 fmt.Printf("%6d %sn", pairs[i].n, pairs[i].s)
}

為了不影響理解,有些代碼被刪除了。你可以在這里獲取完整的源碼。

哪些是最常用的標識符?

我們來用這個程序分析一下 github.com/golang/go 上的代碼:

$ go install github.com/campoy/justforfunc/24-ast/scanner
$ scanner ~/go/src/**/*.go
 82163 v
 46584 err
 44681 Args
 43371 t
 37717 x

在短標識符里,最常用的標識符是字母 v 。那我們修改下代碼來計算一些長標識符:

for s, n := range counts {
 if len(s) >= 3 {
 pairs = append(pairs, pair{s, n})
 }
}

再來一次:

$ go install github.com/campoy/justforfunc/24-ast/scanner
$ scanner ~/go/src/**/*.go
 46584 err
 44681 Args
 36738 nil
 25761 true
 21723 AddArg

果不其然,err 和 nil 是最常見的標識符,畢竟每個程序中都有 if err != nil 這樣的語句。 但 Args 出現頻度這么高怎么回事?

欲知詳情如何,且聽下回分解。


via: https://medium.com/@francesc/whats-the-most-common-identifier-in-go-s-stdlib-e468f3c9c7d9

作者:Francesc Campoy 譯者:kaneg 校對:polaris1119

分享到:
標簽:標識符 語言
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定