回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,13 +1,13 @@
## 12.3. Display遞歸打印
## 12.3. Display递归打印
接下來,讓我們看看如何改善聚合數據類型的示。我們併不想完全隆一fmt.Sprint函,我們隻是像建一個用於調式用的Display函數,給定一聚合型x打印這個值對應的完整的結構,同時記録每個發現的每元素的路徑。讓我們從一個例子始。
接下来,让我们看看如何改善聚合数据类型的示。我们并不想完全隆一fmt.Sprint函,我们只是像建一个用于调式用的Display函数,给定一聚合型x打印这个值对应的完整的结构,同时记录每个发现的每元素的路径。让我们从一个例子始。
```Go
e, _ := eval.Parse("sqrt(A / pi)")
Display("e", e)
```
在上面的調用中,入Display函數的參數是在7.9節一個表達式求值函數返迴的語法樹。Display函數的輸出如下:
在上面的用中,入Display函数的参数是在7.9节一个表达式求值函数返回的语法树。Display函数的输出如下:
```Go
Display e (eval.call):
@@ -20,7 +20,7 @@ e.args[0].value.y.type = eval.Var
e.args[0].value.y.value = "pi"
```
在可能的情下,你應該避免在一包中暴露和反射相的接口。我們將定義一個未導出的display函數用於遞歸處理工作,出的是Display函,它是display函數簡單的包以接受interface{}型的參數
在可能的情下,你应该避免在一包中暴露和反射相的接口。我们将定义一个未导出的display函数用于递归处理工作,出的是Display函,它是display函数简单的包以接受interface{}型的参数
<u><i>gopl.io/ch12/display</i></u>
```Go
@@ -30,9 +30,9 @@ func Display(name string, x interface{}) {
}
```
在display函中,我使用了前面定的打印基礎類型——基本型、函和chan等——元素值的formatAtom函,但是我們會使用reflect.Value的方法來遞歸顯示聚合型的每一個成員或元素。在遞歸下降程中path字符串從最開始傳入的起始值(里是“e”逐步增以表示如何達到當前值例如“e.args[0].value”
在display函中,我使用了前面定的打印基础类型——基本型、函和chan等——元素值的formatAtom函,但是我们会使用reflect.Value的方法来递归显示聚合型的每一个成员或元素。在递归下降程中path字符串从最开始传入的起始值(里是“e”逐步增以表示如何达到当前值例如“e.args[0].value”
爲我們不再模fmt.Sprint函,我們將直接使用fmt包來簡化我的例子實現
为我们不再模fmt.Sprint函,我们将直接使用fmt包来简化我的例子实现
```Go
func display(path string, v reflect.Value) {
@@ -72,21 +72,21 @@ func display(path string, v reflect.Value) {
}
```
讓我們針對不同型分别討論
让我们针对不同型分别讨论
**Slice和數組** 兩種的處理邏輯是一的。Len方法返slice或數組值中的元素個數Index(i)活索引i對應的元素,返的也是一reflect.Value型的值如果索引i超出范圍的話將導致panic常,些行爲和數組或slice類型內建的len(a)和a[i]等操作似。display針對序列中的每元素遞歸調用自身理,我們通過在遞歸處理時向path附加“[i]”表示訪問路徑
**Slice和数组** 两种的处理逻辑是一的。Len方法返slice或数组值中的元素个数Index(i)活索引i对应的元素,返的也是一reflect.Value型的值如果索引i超出范围的话将导致panic常,些行为和数组或slice类型内建的len(a)和a[i]等操作似。display针对序列中的每元素递归调用自身理,我们通过在递归处理时向path附加“[i]”表示访问路径
然reflect.Value類型帶有很多方法,但是有少的方法任意值都是可以安全調用的。例如Index方法隻能對Slice、數組或字符串型的值調用,其它型如果調用將導致panic常。
然reflect.Value类型带有很多方法,但是有少的方法任意值都是可以安全用的。例如Index方法只能对Slice、数组或字符串型的值用,其它型如果调用将导致panic常。
**結構體** NumField方法報告結構體中成員的數Field(i)以reflect.Value型返第i個成員的值。成列表包含了匿名成員在內的全部成。通在path添加“.f”表示成員路徑,我們必須獲得結構體對應的reflect.Type型信息,包含結構體類型和第i個成員的名字。
**结构体** NumField方法报告结构体中成员的数Field(i)以reflect.Value型返第i个成员的值。成列表包含了匿名成员在内的全部成。通在path添加“.f”表示成员路径,我们必须获得结构体对应的reflect.Type型信息,包含结构体类型和第i个成员的名字。
**Maps:** MapKeys方法返迴一個reflect.Value型的slice每一個都對應map的可以。和往常一,遍map時順序是隨機的。MapIndex(key)返map中key對應的value。我向path添加“[key]”表示訪問路徑。(我們這里有一未完成的工作。其map的key的類型併不局限formatAtom能完美理的型;數組、結構體和接口都可以作map的key。針對這種類完善key的示信息是練習12.1的任。)
**Maps:** MapKeys方法返回一个reflect.Value型的slice每一个都对应map的可以。和往常一,遍map时顺序是随机的。MapIndex(key)返map中key对应的value。我向path添加“[key]”表示访问路径。(我们这里有一未完成的工作。其map的key的类型并不局限formatAtom能完美理的型;数组、结构体和接口都可以作map的key。针对这种类完善key的示信息是练习12.1的任。)
**指** Elem方法返迴指針指向的量,是reflect.Value型。技術指針是nil這個操作也是安全的,在這種情況下指是Invalid無效類型,但是我可以用IsNil方法來顯式地測試一個空指針,這樣我們可以打印更合的信息。我在path前面添加“*”,用括弧包含以避免歧
**指** Elem方法返回指针指向的量,是reflect.Value型。技术指针是nil这个操作也是安全的,在这种情况下指是Invalid无效类型,但是我可以用IsNil方法来显式地测试一个空指针,这样我们可以打印更合的信息。我在path前面添加“*”,用括弧包含以避免歧
**接口:** 再一次,我使用IsNil方法來測試接口是否是nil如果不是可以調用v.Elem()來獲取接口對應的動態值,且打印對應的類型和值。
**接口:** 再一次,我使用IsNil方法来测试接口是否是nil如果不是可以用v.Elem()来获取接口对应的动态值,且打印对应的类型和值。
在我的Display函數總算完工了,讓我們看看它的表吧。下面的Movie型是在4.5節的電影類型上演變來的:
在我的Display函数总算完工了,让我们看看它的表吧。下面的Movie型是在4.5节的电影类型上演变来的:
```Go
type Movie struct {
@@ -99,7 +99,7 @@ type Movie struct {
}
```
讓我們聲明一個該類型的量,然看看Display函如何示它:
让我们声明一个该类型的量,然看看Display函如何示它:
```Go
strangelove := Movie{
@@ -125,7 +125,7 @@ strangelove := Movie{
}
```
Display("strangelove", strangelove)調用將顯strangelove電影對應的中文名是《奇博士》):
Display("strangelove", strangelove)调用将显strangelove电影对应的中文名是《奇博士》):
```Go
Display strangelove (display.Movie):
@@ -146,7 +146,7 @@ strangelove.Oscars[3] = "Best Picture (Nomin.)"
strangelove.Sequel = nil
```
也可以使用Display函數來顯示標準庫中類型的內部結構,例如`*os.File`型:
也可以使用Display函数来显示标准库中类型的内部结构,例如`*os.File`型:
```Go
Display("os.Stderr", os.Stderr)
@@ -157,7 +157,7 @@ Display("os.Stderr", os.Stderr)
// (*(*os.Stderr).file).nepipe = 0
```
意的是,結構體中未出的成員對反射也是可的。需要心的是這個例子的出在不同操作繫統上可能是不同的,併且隨着標準庫的發展也可能導致結果不同。(也是將這些成員定義爲私有成的原因之一!)我深圳可以用Display函數來顯示reflect.Value來査`*os.File`型的部表示方式。`Display("rV", reflect.ValueOf(os.Stderr))`調用的出如下,然不同境得到的果可能有差
意的是,结构体中未出的成员对反射也是可的。需要心的是这个例子的出在不同操作系统上可能是不同的,并且随着标准库的发展也可能导致结果不同。(也是将这些成员定义为私有成的原因之一!)我深圳可以用Display函数来显示reflect.Value来查`*os.File`型的部表示方式。`Display("rV", reflect.ValueOf(os.Stderr))`用的出如下,然不同境得到的果可能有差
```Go
Display rV (reflect.Value):
@@ -174,7 +174,7 @@ Display rV (reflect.Value):
...
```
察下面兩個例子的别:
察下面两个例子的别:
```Go
var i interface{} = 3
@@ -191,11 +191,11 @@ Display("&i", &i)
// (*&i).value = 3
```
在第一例子中Display函數將調用reflect.ValueOf(i),它返迴一個Int型的值。正如我在12.2中提到的reflect.ValueOf是返迴一個值的具體類型,因它是從一個接口值提取的容。
在第一例子中Display函数将调用reflect.ValueOf(i),它返回一个Int型的值。正如我在12.2中提到的reflect.ValueOf是返回一个值的具体类型,因它是从一个接口值提取的容。
在第二例子中Display函數調用的是reflect.ValueOf(&i),它返迴一個指向i的指針,對應Ptr型。在switch的Ptr分支中過調用Elem來返迴這個值,返迴一個Value表示i對應Interface型。一個間接獲得的Value就像這一個,可能代表任意型的值,包括接口型。部的display函數遞歸調用自身,次它打印接口的動態類型和值。
在第二例子中Display函数调用的是reflect.ValueOf(&i),它返回一个指向i的指针,对应Ptr型。在switch的Ptr分支中过调用Elem来返回这个值,返回一个Value表示i对应Interface型。一个间接获得的Value就像这一个,可能代表任意型的值,包括接口型。部的display函数递归调用自身,次它打印接口的动态类型和值。
目前的實現Display如果示一個帶環的數據結構將會陷入死循,例如首位項鏈的鏈表:
目前的实现Display如果示一个带环的数据结构将会陷入死循,例如首位项链的链表:
```Go
// a struct that points to itself
@@ -205,7 +205,7 @@ c = Cycle{42, &c}
Display("c", c)
```
Display會永遠不停地行深度遞歸打印:
Display会永远不停地行深度递归打印:
```Go
Display c (display.Cycle):
@@ -216,10 +216,10 @@ c.Value = 42
...ad infinitum...
```
多Go言程序都包含了一些循環的數據結果。Display支持這類帶環的數據結構是比棘手的,需要增加一個額外的記録訪問的路;代是昂的。一般的解方案是采用不安全的言特性,我們將在13.3看到具的解方案。
多Go言程序都包含了一些循环的数据结果。Display支持这类带环的数据结构是比棘手的,需要增加一个额外的记录访问的路;代是昂的。一般的解方案是采用不安全的言特性,我们将在13.3看到具的解方案。
帶環的數據結構很少會對fmt.Sprint函造成問題,因它很少嚐試打印完整的數據結構。例如,它遇到一個指針的時候,它隻是簡單第打印指針的數值。在打印包含自身的slice或map可能遇到睏難,但是不保證處理這種是罕見情況卻可以避免外的麻
带环的数据结构很少会对fmt.Sprint函造成问题,因它很少尝试打印完整的数据结构。例如,它遇到一个指针的时候,它只是简单第打印指针的数值。在打印包含自身的slice或map可能遇到困难,但是不保证处理这种是罕见情况却可以避免外的麻
**練習 12.1** 展Displayhans以便它可以示包含以結構體或數組作爲map的key型的值。
**练习 12.1** 展Displayhans以便它可以示包含以结构体或数组作为map的key型的值。
**練習 12.2**display函數的穩健性,通過記録邊界的步數來確保在超出一定限前放棄遞歸在13.3,我們會看到另一種探測數據結構是否存在的技。)
**练习 12.2**display函数的稳健性,通过记录边界的步数来确保在超出一定限前放弃递归在13.3,我们会看到另一种探测数据结构是否存在的技。)