<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                我們一直在使用符號。符號,在看似簡單的表面之下,又好像沒有那么簡單。起初最好不要糾結于背后的實現機制。可以把符號當成數據對象與名字那樣使用,而不需要理解兩者是如何關聯起來的。但到了某個時間點,停下來思考背后是究竟是如何工作會是很有用的。本章解釋了背后實現的細節。 [TOC] ## 8.1 符號名 (Symbol Names)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#symbol-names "Permalink to this headline") 第二章描述過,符號是變量的名字,符號本身以對象所存在。但 Lisp 符號的可能性,要比在多數語言僅允許作為變量名來得廣泛許多。實際上,符號可以用任何字符串當作名字。可以通過調用?`symbol-name`?來獲得符號的名字: ~~~ > (symbol-name 'abc) "ABC" ~~~ 注意到這個符號的名字,打印出來都是大寫字母。缺省情況下, Common Lisp 在讀入時,會把符號名字所有的英文字母都轉成大寫。代表 Common Lisp 缺省是不分大小寫的: ~~~ > (eql 'abc 'Abc) T > (CaR '(a b c)) A ~~~ 一個名字包含空白,或其它可能被讀取器認為是重要的字符的符號,要用特殊的語法來引用。任何存在垂直杠 (vertical bar)之間的字符序列將被視為符號。可以如下這般在符號的名字中,放入任何字符: ~~~ > (list '|Lisp 1.5| '|| '|abc| '|ABC|) (|Lisp 1.5| || |abc| ABC) ~~~ 當這種符號被讀入時,不會有大小寫轉換,而宏字符與其他的字符被視為一般字符。 那什么樣的符號不需要使用垂直杠來參照呢?基本上任何不是數字,或不包含讀取器視為重要的字符的符號。一個快速找出你是否可以不用垂直杠來引用符號的方法,是看看 Lisp 如何印出它的。如果 Lisp 沒有用垂直杠表示一個符號,如上述列表的最后一個,那么你也可以不用垂直杠。 記得,垂直杠是一種表示符號的特殊語法。它們不是符號的名字之一: ~~~ > (symbol-name '|a b c|) "a b c" ~~~ (如果想要在符號名稱內使用垂直杠,可以放一個反斜線在垂直杠的前面。) 譯注: 反斜線是?`\`?(backslash)。 ## 8.2 屬性列表 (Property Lists)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#property-lists "Permalink to this headline") 在 Common Lisp 里,每個符號都有一個屬性列表(property-list)或稱為?`plist`?。函數?`get`?接受符號及任何類型的鍵值,然后返回在符號的屬性列表中,與鍵值相關的數值: ~~~ > (get 'alizarin 'color) NIL ~~~ 它使用?`eql`?來比較各個鍵。若某個特定的屬性沒有找到時,?`get`?返回?`nil`?。 要將值與鍵關聯起來時,你可以使用?`setf`?及?`get`?: ~~~ > (setf (get 'alizarin 'color) 'red) RED > (get 'alizarin 'color) RED ~~~ 現在符號?`alizarin`?的?`color`?屬性是?`red`?。 ![../_images/Figure-8.1.png](http://acl.readthedocs.org/en/latest/_images/Figure-8.1.png) **圖 8.1 符號的結構** ~~~ > (setf (get 'alizarin 'transparency) 'high) HIGH > (symbol-plist 'alizarin) (TRANSPARENCY HIGH COLOR RED) ~~~ 注意,屬性列表不以關聯列表(assoc-lists)的形式表示,雖然用起來感覺是一樣的。 在 Common Lisp 里,屬性列表用得不多。他們大部分被哈希表取代了(4.8 小節)。 ## 8.3 符號很不簡單 (Symbols Are Big)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#symbols-are-big "Permalink to this headline") 當我們輸入名字時,符號就被悄悄地創建出來了,而當它們被顯示時,我們只看的到符號的名字。某些情況下,把符號想成是表面所見的東西就好,別想太多。但有時候符號不像看起來那么簡單。 從我們如何使用和檢查符號的方式來看,符號像是整數那樣的小對象。而符號實際上確實是一個對象,差不多像是由?`defstruct`?定義的那種結構。符號可以有名字、 主包(home package)、作為變量的值、作為函數的值以及帶有一個屬性列表。圖 8.1 演示了符號在內部是如何表示的。 很少有程序會使用很多符號,以致于值得用其它的東西來代替符號以節省空間。但需要記住的是,符號是實際的對象,不僅是名字而已。當兩個變量設成相同的符號時,與兩個變量設成相同列表一樣:兩個變量的指針都指向同樣的對象。 ## 8.4 創建符號 (Creating Symbols)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#creating-symbols "Permalink to this headline") 8.1 節演示了如何取得符號的名字。另一方面,用字符串生成符號也是有可能的。但比較復雜一點,因為我們需要先介紹包(package)。 概念上來說,包是將名字映射到符號的符號表(symbol-tables)。每個普通的符號都屬于一個特定的包。符號屬于某個包,我們稱為符號被包扣押(intern)了。函數與變量用符號作為名稱。包借由限制哪個符號可以訪問來實現模塊化(modularity),也是因為這樣,我們才可以引用到函數與變量。 大多數的符號在讀取時就被扣押了。在第一次輸入一個新符號的名字時,Lisp 會產生一個新的符號對象,并將它扣押到當下的包里(缺省是?`common-lisp-user`?包)。但也可以通過給入字符串與選擇性包參數給?`intern`?函數,來扣押一個名稱為字符串名的符號: ~~~ > (intern "RANDOM-SYMBOL") RANDOM-SYMBOL NIL ~~~ 選擇性包參數缺省是當前的包,所以前述的表達式,返回當前包里的一個符號,此符號的名字是 “RANDOM-SYMBOL”,若此符號尚未存在時,會創建一個這樣的符號出來。第二個返回值告訴我們符號是否存在;在這個情況,它不存在。 不是所有的符號都會被扣押。有時候有一個自由的(uninterned)符號是有用的,這和公用電話本是一樣的原因。自由的符號叫做*gensyms*?。我們將會在第 10 章討論宏(Macro)時,理解?`gensym`?的作用。 ## 8.5 多重包 (Multiple Packages)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#multiple-packages "Permalink to this headline") 大的程序通常切分為多個包。如果程序的每個部分都是一個包,那么開發程序另一個部分的某個人,將可以使用符號來作為函數名或變量名,而不必擔心名字在別的地方已經被用過了。 在沒有提供定義多個命名空間的語言里,工作于大項目的程序員,通常需要想出某些規范(convention),來確保他們不會使用同樣的名稱。舉例來說,程序員寫顯示相關的代碼(display code)可能用?`disp_`?開頭的名字,而寫數學相關的代碼(math code)的程序員僅使用由?`math_`?開始的代碼。所以若是數學相關的代碼里,包含一個做快速傅立葉轉換的函數時,可能會叫做?`math_fft`?。 包不過是提供了一種便捷方式來自動辦到此事。如果你將函數定義在單獨的包里,可以隨意使用你喜歡的名字。只有你明確導出(`export`?)的符號會被別的包看到,而通常前面會有包的名字(或修飾符)。 舉例來說,假設一個程序分為兩個包,?`math`?與?`disp`?。如果符號?`fft`?被?`math`?包導出,則?`disp`?包里可以用?`math:fft`?來參照它。在?`math`?包里,可以只用?`fft`?來參照。 下面是你可能會放在文件最上方,包含獨立包的代碼: ~~~ (defpackage "MY-APPLICATION" (:use "COMMON-LISP" "MY-UTILITIES") (:nicknames "APP") (:export "WIN" "LOSE" "DRAW")) (in-package my-application) ~~~ `defpackage`?定義一個新的包叫做?`my-application`?[[1]](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#id4)?它使用了其他兩個包,?`common-lisp`?與?`my-utilities`?,這代表著可以不需要用包修飾符(package qualifiers)來存取這些包所導出的符號。許多包都使用了?`common-lisp`?包 ── 因為你不會想給 Lisp 自帶的操作符與變量再加上修飾符。 `my-application`?包本身只輸出三個符號:?`WIN`?、?`LOSE`?以及?`DRAW`?。由于調用?`defpackage`?給了?`my-application`?一個匿稱?`app`?,則別的包可以這樣引用到這些符號,比如?`app:win`?。 `defpackage`?伴隨著一個?`in-package`?,確保當前包是?`my-application`?。所有其它未修飾的符號會被扣押至?`my-application`?── 除非之后有別的?`in-package`?出現。當一個文件被載入時,當前的包總是被重置成載入之前的值。 ## 8.6 關鍵字 (Keywords)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#keywords "Permalink to this headline") 在?`keyword`?包的符號 (稱為關鍵字)有兩個獨特的性質:它們總是對自己求值,以及可以在任何地方引用它們,如?`:x`?而不是`keyword:x`?。我們首次在 44 頁 (譯注: 3.10 小節)介紹關鍵字參數時,?`(member?'(a)?'((a)?(z))?test:?#'equal)`?比?`(member'(a)?'((a)?(z))?:test?#'equal)`?讀起來更自然。現在我們知道為什么第二個較別扭的形式才是對的。?`test`?前的冒號字首,是關鍵字的識別符。 為什么使用關鍵字而不用一般的符號?因為關鍵字在哪都可以存取。一個函數接受符號作為實參,應該要寫成預期關鍵字的函數。舉例來說,這個函數可以安全地在任何包里調用: ~~~ (defun noise (animal) (case animal (:dog :woof) (:cat :meow) (:pig :oink))) ~~~ 但如果是用一般符號寫成的話,它只在被定義的包內正常工作,除非關鍵字也被導出了。 ## 8.7 符號與變量 (Symbols and Variables)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#symbols-and-variables "Permalink to this headline") Lisp 有一件可能會使你困惑的事情是,符號與變量的從兩個非常不同的層面互相關聯。當符號是特別變量(special variable)的名字時,變量的值存在符號的 value 欄位(圖 8.1)。?`symbol-value`?函數引用到那個欄位,所以在符號與特殊變量的值之間,有直接的連接關系。 而對于詞法變量(lexical variables)來說,事情就完全不一樣了。一個作為詞法變量的符號只不過是個占位符(placeholder)。編譯器會將其轉為一個寄存器(register)或內存位置的引用位址。在最后編譯出來的代碼中,我們無法追蹤這個符號 (除非它被保存在調試器「debugger」的某個地方)。因此符號與詞法變量的值之間是沒有連接的;只要一有值,符號就消失了。 ## 8.8 示例:隨機文本 (Example: Random Text)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#example-random-text "Permalink to this headline") 如果你要寫一個操作單詞的程序,通常使用符號會比字符串來得好,因為符號概念上是原子性的(atomic)。符號可以用?`eql`?一步比較完成,而字符串需要使用?`string=`?或?`string-equal`?逐一字符做比較。作為一個示例,本節將演示如何寫一個程序來產生隨機文本。程序的第一部分會讀入一個示例文件(越大越好),用來累積之后所給入的相關單詞的可能性(likeilhood)的信息。第二部分在每一個單詞都根據原本的示例,產生一個隨機的權重(weight)之后,隨機走訪根據第一部分所產生的網絡。 產生的文字將會是部分可信的(locally plausible),因為任兩個出現的單詞也是輸入文件里,兩個同時出現的單詞。令人驚訝的是,獲得看起來是 ── 有意義的整句 ── 甚至整個段落是的頻率相當高。 圖 8.2 包含了程序的上半部,用來讀取示例文件的代碼。 ~~~ (defparameter *words* (make-hash-table :size 10000)) (defconstant maxword 100) (defun read-text (pathname) (with-open-file (s pathname :direction :input) (let ((buffer (make-string maxword)) (pos 0)) (do ((c (read-char s nil :eof) (read-char s nil :eof))) ((eql c :eof)) (if (or (alpha-char-p c) (char= c #\')) (progn (setf (aref buffer pos) c) (incf pos)) (progn (unless (zerop pos) (see (intern (string-downcase (subseq buffer 0 pos)))) (setf pos 0)) (let ((p (punc c))) (if p (see p))))))))) (defun punc (c) (case c (#\. '|.|) (#\, '|,|) (#\; '|;|) (#\! '|!|) (#\? '|?|) )) (let ((prev `|.|)) (defun see (symb) (let ((pair (assoc symb (gethash prev *words*)))) (if (null pair) (push (cons symb 1) (gethash prev *words*)) (incf (cdr pair)))) (setf prev symb))) ~~~ **圖 8.2 讀取示例文件** 從圖 8.2 所導出的數據,會被存在哈希表?`*words*`?里。這個哈希表的鍵是代表單詞的符號,而值會像是下列的關聯列表(assoc-lists): ~~~ ((|sin| . 1) (|wide| . 2) (|sights| . 1)) ~~~ 使用[彌爾頓的失樂園](http://zh.wikipedia.org/wiki/%E5%A4%B1%E6%A8%82%E5%9C%92)作為示例文件時,這是與鍵?`|discover|`?有關的值。它指出了 “discover” 這個單詞,在詩里面用了四次,與 “wide” 用了兩次,而 “sin” 與 ”sights” 各一次。(譯注: 詩可以在這里找到?[http://www.paradiselost.org/](http://www.paradiselost.org/)?) 函數?`read-text`?累積了這個信息。這個函數接受一個路徑名(pathname),然后替每一個出現在文件中的單詞,生成一個上面所展示的關聯列表。它的工作方式是,逐字讀取文件的每個字符,將累積的單詞存在字符串?`buffer`?。?`maxword`?設成?`100`?,程序可以讀取至多 100 個單詞,對英語來說足夠了。 只要下個字符是一個字(由?`alpha-char-p`?決定)或是一撇 (apostrophe) ,就持續累積字符。任何使單詞停止累積的字符會送給`see`?。數種標點符號(punctuation)也被視為是單詞;函數?`punc`?返回標點字符的偽單詞(pseudo-word)。 函數?`see`?注冊每一個我們看過的單詞。它需要知道前一個單詞,以及我們剛確認過的單詞 ── 這也是為什么要有變量?`prev`?存在。起初這個變量設為偽單詞里的句點;在?`see`?函數被調用后,?`prev`?變量包含了我們最后見過的單詞。 在?`read-text`?返回之后,?`*words*`?會包含輸入文件的每一個單詞的條目(entry)。通過調用?`hash-table-count`?你可以了解有多少個不同的單詞存在。鮮少有英文文件會超過 10000 個單詞。 現在來到了有趣的部份。圖 8.3 包含了從圖 8.2 所累積的數據來產生文字的代碼。?`generate-text`?函數導出整個過程。它接受一個要產生幾個單詞的數字,以及選擇性傳入前一個單詞。使用缺省值,會讓產生出來的文件從句子的開頭開始。 ~~~ (defun generate-text (n &optional (prev '|.|)) (if (zerop n) (terpri) (let ((next (random-next prev))) (format t "~A " next) (generate-text (1- n) next)))) (defun random-next (prev) (let* ((choices (gethash prev *words*)) (i (random (reduce #'+ choices :key #'cdr)))) (dolist (pair choices) (if (minusp (decf i (cdr pair))) (return (car pair)))))) ~~~ **圖 8.3 產生文字** 要取得一個新的單詞,?`generate-text`?使用前一個單詞,接著調用?`random-next`?。?`random-next`?函數根據每個單詞出現的機率加上權重,隨機選擇伴隨輸入文本中?`prev`?之后的單詞。 現在會是測試運行下程序的好時機。但其實你早看過一個它所產生的示例: 就是本書開頭的那首詩,是使用彌爾頓的失樂園作為輸入文件所產生的。 (譯注: 詩可在這里看,或是瀏覽書的第 vi 頁) Half lost on my firmness gains more glad heart, Or violent and from forage drives A glimmering of all sun new begun Both harp thy discourse they match’d, Forth my early, is not without delay; For their soft with whirlwind; and balm. Undoubtedly he scornful turn’d round ninefold, Though doubled now what redounds, And chains these a lower world devote, yet inflicted? Till body or rare, and best things else enjoy’d in heav’n To stand divided light at ev’n and poise their eyes, Or nourish, lik’ning spiritual, I have thou appear. ── Henley ## Chapter 8 總結 (Summary)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#chapter-8-summary "Permalink to this headline") 1. 符號的名字可以是任何字符串,但由?`read`?創建的符號缺省會被轉成大寫。 2. 符號帶有相關聯的屬性列表,雖然他們不需要是相同的形式,但行為像是 assoc-lists 。 3. 符號是實質的對象,比較像結構,而不是名字。 4. 包將字符串映射至符號。要在包里給符號創造一個條目的方法是扣留它。符號不需要被扣留。 5. 包通過限制可以引用的名稱增加模塊化。缺省的包會是 user 包,但為了提高模塊化,大的程序通常分成數個包。 6. 可以讓符號在別的包被存取。關鍵字是自身求值并在所有的包里都可以存取。 7. 當一個程序用來操作單詞時,用符號來表示單詞是很方便的。 ## Chapter 8 練習 (Exercises)[](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#chapter-8-exercises "Permalink to this headline") 1. 可能有兩個同名符號,但卻不?`eql`?嗎? 2. 估計一下用字符串表示 “FOO” 與符號表示 foo 所使用內存空間的差異。 3. 只使用字符串作為實參 來調用 137 頁的?`defpackage`?。應該使用符號比較好。為什么使用字符串可能比較危險呢? 4. 加入需要的代碼,使圖 7.1 的代碼可以放在一個叫做?`"RING"`?的包里,而圖 7.2 的代碼放在一個叫做?`"FILE"`?包里。不需要更動現有的代碼。 5. 寫一個確認引用的句子是否是由 Henley 生成的程序 (8.8 節)。 6. 寫一版 Henley,接受一個單詞,并產生一個句子,該單詞在句子的中間。 腳注 [[1]](http://acl.readthedocs.org/en/latest/zhCN/ch8-cn.html#id2) | 調用?`defpackage`?里的名字全部大寫的緣故在 8.1 節提到過,符號的名字缺省被轉成大寫。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看