From 4a3dbe68b4593171b5cdf80992c4d36278cd621e Mon Sep 17 00:00:00 2001 From: prife Date: Thu, 21 Jan 2016 19:55:25 +0800 Subject: [PATCH] polish the first part of ch1-02.md the remind part of ch1-02 will be polished soon. --- ch1/ch1-02.md | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/ch1/ch1-02.md b/ch1/ch1-02.md index 36b83ad..e95c941 100644 --- a/ch1/ch1-02.md +++ b/ch1/ch1-02.md @@ -1,14 +1,14 @@ ## 1.2. 命令行參數 -大多數的程序都是處理輸入,産生輸出;這也正是“計算”的定義。但是一個程序要如何獲取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸出、用戶的鍵盤、命令行的參數或其它類似輸入源。下面幾個例子會討論其中的一些輸入類型,首先是命令行參數。 +大多數的程序都是處理輸入,産生輸出;這也正是“計算”的定義。但是, 程序如何獲取要处理的輸入数据呢?一些程序生成自己的數據,但通常情況下,輸入來自於程序外部:文件、網絡連接、其它程序的輸出、敲鍵盤的用户、命令行參數或其它類似輸入源。下面幾個例子會討論其中几个輸入源,首先是命令行參數。 -os這個package提供了操作繫統無關(跨平台)的,與繫統交互的一些函數和相關的變量,運行時程序的命令行參數可以通過os包中一個叫Args的變量來獲取;當在os包外部使用該變量時,需要用os.Args來訪問。 +`os`包以跨平台的方式,提供了一些與操作繫統交互的函數和變量。程序的命令行參數可从os包的Args變量获取;os包外部使用os.Args访问該變量。 -os.Args這個變量是一個字符串(string)的slice(譯註:slice和Python語言中的切片類似,是一個簡版的動態數組),slice在Go語言里是一個基礎的數據結構,之後我們很快會提到。現在可以先把slice當一個簡單的元素序列,可以用類似s[i]的下標訪問形式獲取其內容,併且可以用形如s[m:n]的形式來獲取到一個slice的子集(譯註:和python里的語法差不多)。其長度可以用len(s)函數來獲取。和其它大多數編程語言類似,Go語言里的這種索引形式也采用了左閉右開區間,包括m~n的第一個元素,但不包括最後那個元素(譯註:比如a = [1, 2, 3, 4, 5], a[0:3] = [1, 2, 3],不包含最後一個元素)。這樣可以簡化我們的處理邏輯。比如s[m:n]這個slice,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。 +os.Args變量是一個字符串(string)的*切片*(slice)(譯註:slice和Python語言中的切片類似,是一個簡版的動態數組),切片是Go語言的基礎概念,稍后会详细介绍。現在先把切片s当作数组元素序列, 序列的成长度动态变化, 用`s[i]`访问单个元素,用`s[m:n]`獲取子序列(譯註:和python里的語法差不多)。序列的元素数目为len(s)。和大多數編程語言類似,区间索引时,Go言里也采用左閉右開形式, 即,区间包括第一个索引元素,不包括最後一个, 因为这样可以簡化邏輯。(譯註:比如a = [1, 2, 3, 4, 5], a[0:3] = [1, 2, 3],不包含最後一個元素)。比如s[m:n]這個切片,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。 -os.Args的第一個元素,卽os.Args[0]是命令行執行時的命令本身;其它的元素則是執行該命令時傳給這個程序的參數。前面提到的切片表達式,s[m:n]會返迴第m到第n-1個元素,所以下一個例子里需要用到的os.Args[1:len(os.Args)]卽是除了命令本身外的所有傳入參數。如果我們省略s[m:n]里的m和n,那麽默認這個表達式會填入0:len(s),所以這里我們還可以省略掉n,寫成os.Args[1:]。 +os.Args的第一個元素,os.Args[0], 是命令本身的名字;其它的元素則是程序启动时傳給它的參數。s[m:n]形式的切片表達式,产生从第m个元素到第n-1個元素的切片,下個例子用到的元素包含在os.Args[1:len(os.Args)]切片中。如果省略切片表达式的m或n,会默认传入0或len(s),因此前面的切片可以简写成os.Args[1:]。 -下面是一個Unix里echo命令的實現,這個命令會在單行內打印出命令行參數。程序中import了兩個package,併且用括號把這兩個package包了起來,這是import多個package時的簡化寫法。當然了分開寫import也沒有什麽問題,隻是這麽寫更加方便。這里的導入順序併不重要,因爲gofmt工具格式化時會按照字母順序來排列好這些被導入的包名。(本書中代碼范例的不同版本會用編號來標記) +下面是Unix里echo命令的一份實現,echo把它的命令行參數打印成一行。程序导入了兩個包,用括號把它们括起来写成列表形式, 而没有分开写成独立的`import`声明。两种形式都合法,列表形式习惯上用得多。包導入順序併不重要;gofmt工具格式化時按照字母順序对包名排序。(示例有多个版本时,我们会对示例编号, 这样可以明确当前正在讨论的是哪个。) gopl.io/ch1/echo1 ```go @@ -30,39 +30,37 @@ func main() { } ``` -Go語言里的註釋是以//來表示。//之後的內容一直到行末都是這條註釋的一部分,這些註釋會被編譯器忽略。 +註釋语句以`//`开头。对于程序员来说,//之后到行末之间所有的内容都是註釋,被編譯器忽略。按照慣例,我们在每个包的包声明前添加注释;對於`main package`,註釋包含一句或几句话,从整体角度对程序做个描述。 -按照慣例,每一個package前都需要有詳盡的註釋對該package進行説明;對於main package來説,這段註釋一般會包含幾句話,説明這個項目/程序整體是做什麽用的。 +var声明定义了兩個string类型的變量s和sep。變量会在聲明时直接初始化。如果变量沒有顯式初始化,则会被隐式地賦予其類型的*零值*(zero value),數值類型是0,字符串類型是空字符串""。這個例子里,声明把s和sep隱式地初始化成空字符串。第2章再来詳細地講解變量和聲明。 -var關鍵字用來聲明變量。這個程序聲明了s和sep兩個string變量。變量可以在聲明期間直接進行初始化。如果沒有顯式初始化,Go語言會隱式地給這些未初始化的變量賦予對應其具體類型的零值,比如數值類型就是0,字符串類型就是空字符串""。在這個例子里的s和sep被隱式地賦值爲了空字符串。在第2章中我們會更詳細地講解變量和聲明。 - -對於數值類型,Go語言提供了常規的數值/邏輯運算符。而對於string類型,+號表示字符串的連接(譯註:和C++或者js是一樣的)。所以下面這個表達式: +對數值類型,Go語言提供了常規的數值和邏輯運算符。而對string類型,`+`运算符连接字符串(譯註:和C++或者js是一樣的)。所以表達式: ```go sep + os.Args[i] ``` -表示將sep字符串和os.Args[i]字符串進行連接。我們在程序里用的另外一個表達式: +表示連接字符串sep和os.Args。程序中使用的语句: ```go s += sep + os.Args[i] ``` -會將sep與os.Args[i]連接,然後再將得到的結果與s進行連接,再將結果併賦值給s,和下面的表達是等價: +是一条*赋值语句*, 將s的旧值跟sep與os.Args[i]連接后賦值回s,等价于: ```go s = s + sep + os.Args[i] ``` -運算符+=是一個賦值運算符(assignment operator),每一種數值/邏輯運算符,例如*或者+都有其對應的賦值運算符。 +運算符`+=`是賦值運算符(assignment operator),每種數值运算符或邏輯運算符,如`+`或`*`,都有對應的賦值運算符。 -echo程序可以每循環一次輸出一個參數,不過我們這里的版本是不斷地將其結果連接到一個字符串的末尾。s這個字符串在聲明的時候是一個空字符串,而之後循環每次都會被在末尾添加一段字符串;第一次迭代之後,一個空格會被插入到字符串末尾,所以每插入一個新值,都會和前一個中間有一個空格隔開。這是一種非線性的操作,當我們的參數數量變得龐大的時候(當然不是説這里的echo,一般echo也不會有太多參數)其運行開銷也會變得龐大。下面我們會介紹一繫列的echo改進版,來改進這個程序的運行效率。 +echo程序可以每循環一次輸出一個參數,這个版本却是不斷地把新文本追加到末尾来构造字符串。字符串s开始为空,即值为"",每次循環會添加一些文本;第一次迭代之後,还会再插入一個空格,因此循环结束时每个参数中間都有一個空格。這是一種二次加工(quadratic process),当參數數量龐大时,开销很大,但是对于echo,这种情形不大可能出现。本章会介紹echo的若干改進版,下一章解决低效问题。 -在for循環中,我們用i來做下標索引,用:=符號來給i進行初始化和賦值,這是var xxx=yyy的一種簡寫形式,Go語言會根據等號右邊的值的類型自動判斷左邊的值類型,下一章會對這一點進行詳細説明。 +循环索引变量i在for循环的第一部分中定义。符號`:=`是短变量声明(short variable declaration)的一部分, 这是定义一个或多个变量并根据它们的初始值为这些变量赋予适当类型的语句。下一章有这方面更多説明。 -自增表達式i++會爲i加上1;這和i += 1以及i = i + 1都是等價的。對應的還有i--是給i減去1。這些在Go語言里是語句,而不像C繫的其它語言里是表達式。所以在Go語言里j = i++是非法的,而且++和--都隻能放在變量名後面,因此--i也是非法的。 +自增语句`i++`给`i`加1;這和`i += 1`以及`i = i + 1`都是等價的。對應的還有`i--`給`i`減1。它们是語句,而不像C繫的其它語言那样是表達式。所以`j = i++`非法,而且++和--都隻能放在變量名後面,因此`--i`也非法。 -在Go語言里隻有for循環一種循環。當然了爲了滿足需求,Go的for循環有很多種形式,下面是其中的一種: +Go語言隻有for循環这一種循環语句。for循環有多种形式,其中一种如下所示: ```go for initialization; condition; post { @@ -70,7 +68,7 @@ for initialization; condition; post { } ``` -需要註意的是,for循環的兩邊是不需要像其它語言一樣寫括號的。併且左花括號需要和for語句在同一行。 +for循環三个部分不需括號包围。大括号强制要求, 左大括號必须和*post*语句在同一行。 initialization部分是可選的,如果你寫了這部分的話,在for循環之前這部分的邏輯會被執行。initalization部分必須是一個簡單的語句,具體可以是一個簡短的變量聲明,一個賦值語句,或是一個函數調用。condition部分必須是一個結果爲boolean值的表達式,在每次循環之前,語言都會檢査當前是否滿足這個條件,若不滿足的話便會結束循環;post部分的語句則是在每次循環迭代結束之後被執行,之後conditon部分會在下一次執行前再次進行判斷,依此往複。當condition條件里的判斷結果變爲false之後,循環卽結束。