mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-20 04:34:20 +08:00
rebuild
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<title>示例: 編碼S表達式 | Go语言圣经</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
<meta name="generator" content="GitBook 2.6.6">
|
||||
|
||||
|
||||
<meta name="HandheldFriendly" content="true"/>
|
||||
@@ -48,7 +48,13 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.4" data-chapter-title="示例: 編碼S表達式" data-filepath="ch12/ch12-04.md" data-basepath=".." data-revision="Thu Dec 31 2015 16:18:40 GMT+0800 (中国标准时间)">
|
||||
<div class="book"
|
||||
data-level="12.4"
|
||||
data-chapter-title="示例: 編碼S表達式"
|
||||
data-filepath="ch12/ch12-04.md"
|
||||
data-basepath=".."
|
||||
data-revision="Sat Jan 02 2016 16:00:23 GMT+0800 (中国标准时间)"
|
||||
data-innerlanguage="">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -2024,7 +2030,128 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="124-示例-編碼s表達式">12.4. 示例: 編碼S表達式</h2>
|
||||
<p>TODO</p>
|
||||
<p>Display是一個用於顯示結構化數據的調試工具,但是它併不能將任意的Go語言對象編碼爲通用消息然後用於進程間通信。</p>
|
||||
<p>正如我們在4.5節中中看到的,Go語言的標準庫支持了包括JSON、XML和ASN.1等多種編碼格式。還有另一種依然被廣泛使用的格式是S表達式格式,采用類似Lisp語言的語法。但是和其他編碼格式不同的是,Go語言自帶的標準庫併不支持S表達式,主要是因爲它沒有一個公認的標準規范。</p>
|
||||
<p>在本節中,我們將定義一個包用於將Go語言的對象編碼爲S表達式格式,它支持以下結構:</p>
|
||||
<pre><code>42 integer
|
||||
"hello" string (with Go-style quotation)
|
||||
foo symbol (an unquoted name)
|
||||
(1 2 3) list (zero or more items enclosed in parentheses)
|
||||
</code></pre><p>布爾型習慣上使用t符號表示true,空列表或nil符號表示false,但是爲了簡單起見,我們暫時忽略布爾類型。同時忽略的還有chan管道和函數,因爲通過反射併無法知道它們的確切狀態。我們忽略的還浮點數、複數和interface。支持它們是練習12.3的任務。</p>
|
||||
<p>我們將Go語言的類型編碼爲S表達式的方法如下。整數和字符串以自然的方式編碼。Nil值編碼爲nil符號。數組和slice被編碼爲一個列表。</p>
|
||||
<p>結構體被編碼爲成員對象的列表,每個成員對象對應一個個僅有兩個元素的子列表,其中子列表的第一個元素是成員的名字,子列表的第二個元素是成員的值。Map被編碼爲鍵值對的列表。傳統上,S表達式使用點狀符號列表(key . value)結構來表示key/value對,而不是用一個含雙元素的列表,不過爲了簡單我們忽略了點狀符號列表。</p>
|
||||
<p>編碼是由一個encode遞歸函數完成,如下所示。它的結構本質上和前面的Display函數類似:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch12/sexpr
|
||||
|
||||
<span class="hljs-keyword">func</span> encode(buf *bytes.Buffer, v reflect.Value) error {
|
||||
<span class="hljs-keyword">switch</span> v.Kind() {
|
||||
<span class="hljs-keyword">case</span> reflect.Invalid:
|
||||
buf.WriteString(<span class="hljs-string">"nil"</span>)
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
fmt.Fprintf(buf, <span class="hljs-string">"%d"</span>, v.Int())
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
fmt.Fprintf(buf, <span class="hljs-string">"%d"</span>, v.Uint())
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.String:
|
||||
fmt.Fprintf(buf, <span class="hljs-string">"%q"</span>, v.String())
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Ptr:
|
||||
<span class="hljs-keyword">return</span> encode(buf, v.Elem())
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Array, reflect.Slice: <span class="hljs-comment">// (value ...)</span>
|
||||
buf.WriteByte(<span class="hljs-string">'('</span>)
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < v.Len(); i++ {
|
||||
<span class="hljs-keyword">if</span> i > <span class="hljs-number">0</span> {
|
||||
buf.WriteByte(<span class="hljs-string">' '</span>)
|
||||
}
|
||||
<span class="hljs-keyword">if</span> err := encode(buf, v.Index(i)); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
}
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">')'</span>)
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Struct: <span class="hljs-comment">// ((name value) ...)</span>
|
||||
buf.WriteByte(<span class="hljs-string">'('</span>)
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < v.NumField(); i++ {
|
||||
<span class="hljs-keyword">if</span> i > <span class="hljs-number">0</span> {
|
||||
buf.WriteByte(<span class="hljs-string">' '</span>)
|
||||
}
|
||||
fmt.Fprintf(buf, <span class="hljs-string">"(%s "</span>, v.Type().Field(i).Name)
|
||||
<span class="hljs-keyword">if</span> err := encode(buf, v.Field(i)); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">')'</span>)
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">')'</span>)
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Map: <span class="hljs-comment">// ((key value) ...)</span>
|
||||
buf.WriteByte(<span class="hljs-string">'('</span>)
|
||||
<span class="hljs-keyword">for</span> i, key := <span class="hljs-keyword">range</span> v.MapKeys() {
|
||||
<span class="hljs-keyword">if</span> i > <span class="hljs-number">0</span> {
|
||||
buf.WriteByte(<span class="hljs-string">' '</span>)
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">'('</span>)
|
||||
<span class="hljs-keyword">if</span> err := encode(buf, key); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">' '</span>)
|
||||
<span class="hljs-keyword">if</span> err := encode(buf, v.MapIndex(key)); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">')'</span>)
|
||||
}
|
||||
buf.WriteByte(<span class="hljs-string">')'</span>)
|
||||
|
||||
<span class="hljs-keyword">default</span>: <span class="hljs-comment">// float, complex, bool, chan, func, interface</span>
|
||||
<span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"unsupported type: %s"</span>, v.Type())
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">nil</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>Marshal函數是對encode的保證,以保持和encoding/...下其它包有着相似的API:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Marshal encodes a Go value in S-expression form.</span>
|
||||
<span class="hljs-keyword">func</span> Marshal(v <span class="hljs-keyword">interface</span>{}) ([]<span class="hljs-typename">byte</span>, error) {
|
||||
<span class="hljs-keyword">var</span> buf bytes.Buffer
|
||||
<span class="hljs-keyword">if</span> err := encode(&buf, reflect.ValueOf(v)); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">nil</span>, err
|
||||
}
|
||||
<span class="hljs-keyword">return</span> buf.Bytes(), <span class="hljs-constant">nil</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>下面是Marshal對12.3節的strangelove變量編碼後的結果:</p>
|
||||
<pre><code>((Title "Dr. Strangelove") (Subtitle "How I Learned to Stop Worrying and Lo
|
||||
ve the Bomb") (Year 1964) (Actor (("Grp. Capt. Lionel Mandrake" "Peter Sell
|
||||
ers") ("Pres. Merkin Muffley" "Peter Sellers") ("Gen. Buck Turgidson" "Geor
|
||||
ge C. Scott") ("Brig. Gen. Jack D. Ripper" "Sterling Hayden") ("Maj. T.J. \
|
||||
"King\" Kong" "Slim Pickens") ("Dr. Strangelove" "Peter Sellers"))) (Oscars
|
||||
("Best Actor (Nomin.)" "Best Adapted Screenplay (Nomin.)" "Best Director (N
|
||||
omin.)" "Best Picture (Nomin.)")) (Sequel nil))
|
||||
</code></pre><p>整個輸出編碼爲一行中以減少輸出的大小,但是也很難閲讀。這里有一個對S表達式格式化的約定。編寫一個S表達式的格式化函數將作爲一個具有挑戰性的練習任務;不過 <a href="http://gopl.io" target="_blank">http://gopl.io</a> 也提供了一個簡單的版本。</p>
|
||||
<pre><code>((Title "Dr. Strangelove")
|
||||
(Subtitle "How I Learned to Stop Worrying and Love the Bomb")
|
||||
(Year 1964)
|
||||
(Actor (("Grp. Capt. Lionel Mandrake" "Peter Sellers")
|
||||
("Pres. Merkin Muffley" "Peter Sellers")
|
||||
("Gen. Buck Turgidson" "George C. Scott")
|
||||
("Brig. Gen. Jack D. Ripper" "Sterling Hayden")
|
||||
("Maj. T.J. \"King\" Kong" "Slim Pickens")
|
||||
("Dr. Strangelove" "Peter Sellers")))
|
||||
(Oscars ("Best Actor (Nomin.)"
|
||||
"Best Adapted Screenplay (Nomin.)"
|
||||
"Best Director (Nomin.)"
|
||||
"Best Picture (Nomin.)"))
|
||||
(Sequel nil))
|
||||
</code></pre><p>和fmt.Print、json.Marshal、Display函數類似,sexpr.Marshal函數處理帶環的數據結構也會陷入死循環。</p>
|
||||
<p>在12.6節中,我們將給出S表達式解碼器的實現步驟,但是在那之前,我們還需要先了解如果通過反射技術來更新程序的變量。</p>
|
||||
<p><strong>練習 12.3:</strong> 實現encode函數缺少的分支。將布爾類型編碼爲t和nil,浮點數編碼爲Go語言的格式,複數1+2i編碼爲#C(1.0 2.0)格式。接口編碼爲類型名和值對,例如("[]int" (1 2 3)),但是這個形式可能會造成歧義:reflect.Type.String方法對於不同的類型可能返迴相同的結果。</p>
|
||||
<p><strong>練習 12.4:</strong> 脩改encode函數,以上面的格式化形式輸出S表達式。</p>
|
||||
<p><strong>練習 12.5:</strong> 脩改encode函數,用JSON格式代替S表達式格式。然後使用標準庫提供的json.Unmarshal解碼器來驗證函數是正確的。</p>
|
||||
<p><strong>練習 12.6:</strong> 脩改encode,作爲一個優化,忽略對是零值對象的編碼。</p>
|
||||
<p><strong>練習 12.7:</strong> 創建一個基於流式的API,用於S表達式的解碼,和json.Decoder(§4.5)函數功能類似。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user