青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

loop_in_codes

低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

Lisp一瞥:增強型變量Symbol

Lisp一瞥:增強型變量Symbol

Author: Kevin Lynx
Date: 3.21.2011
Contact: kevinlynx at gmail dot com

Note

本文描述的Lisp主要指Lisp的方言Common Lisp。

變量,是所有編程語言里都有的語法概念。在C/C++中,變量用于標示一個內存地址,而變 量名則在語法層面上代表這個地址。當鏈接器最終鏈接我們的程序時,就將這些名字替換 為實際的地址。在其他語言中,變量雖然或多或少有其他不同的含義,但也大致如此。

Lisp中的變量也差不多這樣,但若將variable和Lisp中的 symbol 放在一起,則多少會 帶來些困惑。

Lisp中的“變量"

很多教授Lisp的書中,大概會簡單地告訴我們可以使用如下的方式定義一個全局變量 [1].

(defparameter *var* 1)

如上代碼,我們便定義了一個全局變量 *var*[2] ,它被初始化為數值1。同樣,我們 還可以使用另一種基本相同的方式:

(defvar *var* 1)

除了全局變量,我們還可以定義局部變量。但局部變量的定義稍顯麻煩(卻可能是另一種 設計考慮)。定義局部變量需要使用一些宏,或者特殊運算符,例如:

(let ((var 1))
(format t "~a" var))

好了,就這些了。Lisp中關于變量的細節(jié),也就這些。你甚至能用你在C/C++中的經驗來窺 探一切。但是,我們很快就看到了很多困惑的地方。

我遇到的第一個困惑的地方來源于函數,那么等我講講函數再來分享下坎坷。

Lisp中的函數

Lisp中的函數絕對不復雜,你絕對不用擔心我在忽悠你 [3] 。作為一門函數式語言,其首要 任務就是加強函數這個東西在整個語言里的功能。如果你喜歡廣閱各種與你工作不相干的 技術,你肯定已經對很多函數式語言世界中的概念略有耳聞。例如閉包,以及first class type [4]

Lisp中的函數就是first class type。這什么意思呢?直白來說, Lisp中的函數和變量 沒什么區(qū)別,享有同等待遇 。進一步來說,變量fn的值可以是數值1,也可以是字符串 "hello",甚至是某個函數。這其實就是C++程序員說的functor。

Lisp中定義函數非常簡單:

(defun add2 (x)
(+ 2 x))

這樣,我們就定義了一個名為add2,有1個參數,1個返回值的函數。要調用該函數時,只需 要 (add2 2) 即可。這簡直和我們在Lisp中完成一個加法一模一樣:(+ 2 3)

Lisp作為一門函數式語言,其函數也能作為另一個函數的參數和返回值 [5]

(defun apply-fn (fn x)
(funcall fn x))

apply-fn函數第一個參數是一個函數,它使用funcall函數間接地調用fn指向的函數。作為 一個C++程序員,這簡直太好理解了,這完全就是一個函數指針的語法糖嘛。于是,假設我 們要使用apply-fn來間接調用add2函數:

(apply-fn add2 2) ;; wrong

可是這是不對的。我們需要通過另一個特殊操作符來完成這件事:

(apply-fn #'add2 2) ;; right

#'操作符用于將add2對應的函數取出來,這么說當然不大準確。Again,作為一個C++程序員 ,這簡直就是個取地址操作符&的語法糖嘛。好吧,這么理解起來似乎沒問題了。

Lisp中能甚至能在任何地方定義一個函數,例如我們創(chuàng)建一個函數,該函數返回創(chuàng)建出來的 函數,這是一個典型的講解什么是 閉包 的例子:

(defun get-add-n (n)
#' (lambda (x)
(+ x n)))

無論如何,get-add-n函數返回一個函數,該函數是add2函數的泛型實現。它可以將你傳入 的參數加上n。這些代碼里使用了lambda表達式。lambda表達式直白來說,就是創(chuàng)建一個字 面上的函數。這又是什么意思呢?就像我們在代碼中寫出2,寫出"hello"一樣,2就是個字 面上的數字,"hello"就是個字面上的字符串 [6]

那么,總而言之,通過lambda創(chuàng)建一個函數體,然后通過#'操作符即可得到一個函數,雖然 沒有名字。有了以上知識后,Again and again,作為一個C++程序員,很快我們就能得到一 個程序:定義變量,用變量去保存一個函數,然后通過這個變量來調用這個函數。這是多么 天經地義的事,就像之前那個通過參數調用其指向的函數一樣:

;; wrong
(defvar fn #' (lambda (x) (+ x 2)))
(fn 3)

這樣的代碼是不對的,錯誤發(fā)生于第二行,無論你使用的Lisp實現是哪種,大概會得到如下 的錯誤信息:

"The function FN is undefined."

老實說,這已經算是多么有跡可循的錯誤提示了啊。將以上代碼和之前的apply-fn對比,是 多么得神似啊,可惜就是錯的。這是我們遇到的第一個理解偏差導致的問題。如果你還不深 入探究,你將會在這一塊遇到更多麻煩。及時地拿出你的勇氣,披荊斬棘,刨根究底,絕對 是學習編程的好品質。

“萬惡之源“:SYMBOL

上文中提到的變量函數之類,之所以會在某些時候與我們的理解發(fā)生偏差,并且總是存在些 神秘的地方無法解釋。這完全是因為我們理解得太片面導致。Lisp中的Symbol可以說就是某 個變量,或者某個函數,但這太片面。Lisp中的Symbol擁有更豐富的含義。

Symbol的名字

就像很多語言的變量、函數名一樣,Lisp中的Symbol比其他語言在命名方面更自由: 只 要位于'|'字符之間的字符串,就表示一個合法的Symbol名。 我們可以使用函數 symbol-name來獲取一個Symbol的名字,例如:

(symbol-name '|this is a symbol name|)
輸出:"this is a symbol name"

'(quote)操作符告訴Lisp不要對其修飾的東西進行求值(evaluate)。但假如沒有這個操作符 會怎樣呢?后面我們將看到會怎樣。

Symbol本質

<ANSI Common Lisp>一書中有句話真正地揭示了Symbol的本質: Symbols are real objects 。是的,Symbols是對象,這個對象就像我們理解的C++中的對象一樣,它是一個 復合的數據結構。該數據結構里包含若干域,或者通俗而言:數據成員。借用<ANSI Common Lisp>中的一圖:

imgs/symbol-obj.png

通過這幅圖,可以揭開所有謎底。一個Symbol包含至少圖中的幾個域,例如Name、Value、 Function等。在Lisp中有很多函數來訪問這些域,例如上文中使用到的symbol-name,這個 函數本質上就是取出一個Symbol的Name域。

Symbol與Variable和Function的聯系

自然而然地,翻閱Lisp文檔,我們會發(fā)現果然還有其他函數來訪問Symbol的其他域,例如:

symbol-function
symbol-value
symbol-package
symbol-plist

但是這些又與上文提到的變量和函數有什么聯系呢?真相只有一個, 變量、函數粗略來 說就是Symbol的一個域,一個成員。變量對應Value域,函數對應Function域。一個Symbol 這些域有數據了,我們說它們發(fā)生了綁定(bind)。 而恰好,我們有幾個函數可以用于判 定這些域是否被綁定了值:

boundp ;判定Value域是否被綁定
fboundp;判定Function域是否被綁定

通過一些代碼來回味以上結論:

(defvar *var* 1)
(boundp '*var*) ; 返回真
(fboundp '*var*) ; 返回假
(defun *var* (x) x) ; 定義一個名為*var*的函數,返回值即為參數
(fboundp '*var*) ; 返回真

上面的代碼簡直揭秘了若干驚天地泣鬼神的真相。首先,我們使用我們熟知的defvar定義了 一個名為 *var* 的變量,初值為1,然后使用boundp去判定 *var* 的Value域是否 發(fā)生了綁定。這其實是說: 原來定義變量就是定義了一個Symbol,給變量賦值,原來就 是給Symbol的Value域賦值!

其實,Lisp中所有這些符號,都是Symbol。 什么變量,什么函數,都是浮云。上面的 例子中,緊接著用fboundp判斷Symbol *var* 的Function域是否綁定,這個時候為假。 然后我們定義了一個名為 *var* 的函數,之后再判斷,則已然為真。這也是為什么, 在Lisp中某個函數可以和某個變量同名的原因所在。 從這段代碼中我們也可以看出 defvar/defun這些操作符、宏所做事情的本質。

More More More

事情就這樣結束了?Of course not。還有很多上文提到的疑惑沒有解決。首先,Symbol是 如此復雜,那么Lisp如何決定它在不同環(huán)境下的含義呢?Symbol雖然是個對象,但它并不像 C++中的對象一樣,它出現時并不指代自己!不同應用環(huán)境下,它指代的東西也不一樣。這 些指代主要包括變量和函數,意思是說: Symbol出現時,要么指的是它的Value,要么是 它的Function。 這種背地里干的事情,也算是造成迷惑的一個原因。

當一個Symbol出現在一個List的第一個元素時,它被處理為函數。這么說有點迷惑人,因為 它帶進了Lisp中代碼和數據之間的模糊邊界特性。簡單來說,就是當Symbol出現在一個括號 表達式(s-expression)中第一個位置時,算是個函數,例如:

(add2 3) ; add2位于第一個位置,被當作函數處理
(*var* 3) ; 這里*var*被當作函數調用,返回3

除此之外,我能想到的其他大部分情況,一個Symbol都被指代為它的Value域,也就是被當 作變量,例如:

(*var* *var*) ; 這是正確的語句,返回1

這看起來是多么古怪的代碼。但是運用我們上面說的結論,便可輕易解釋:表達式中第一個 *var* 被當作函數處理,它需要一個參數;表達式第二部分的 *var* 被當作變量 處理,它的值為1,然后將其作為參數傳入。

再來說說'(quote)操作符,這個操作符用于防止其操作數被求值。而當一個Symbol出現時, 它總是會被求值,所以,我們可以分析以下代碼:

(symbol-value *var*) ; wrong

這個代碼并不正確,因為 *var* 總是會被求值,就像 (*var* *var*) 一樣,第二 個 *var* 被求值,得到數字1。這里也會發(fā)生這種事情,那么最終就等同于:

(symbol-value 1) ; wrong

我們試圖去取數字1的Value域,而數字1并不是一個Symbol。所以,我們需要quote運算符:

(symbol-value '*var*) ; right

這句代碼是說,取Symbol *var* 本身的Value域!而不是其他什么地方。至此,我們 便可以分析以下復雜情況:

(defvar *name* "kevin lynx")
(defvar *ref* '*name*) ; *ref*的Value保存的是另一個Symbol
(symbol-value *ref*) ; 取*ref*的Value,得到*name*,再取*name*的Value

現在,我們甚至能解釋上文留下的一個問題:

;; wrong
(defvar fn #' (lambda (x) (+ x 2)))
(fn 3)

給fn的Value賦值一個函數, (fn 3) 當一個Symbol作為函數使用時,也就是取其 Function域來做調用。但其Function域什么也沒有,我們試圖將一個Symbol的Value域當作 Function來使用。如何解決這個問題?想想,symbol-function可以取到一個Symbol的 Function域:

(setf (symbol-function 'fn) #' (lambda (x) (+ x 2)))
(fn 3)

通過顯示地給fn的Function域賦值,而不是通過defvar隱式地對其Value域賦值,就可以使 (fn 3) 調用正確。還有另一個問題也能輕易解釋:

(apply-fn add2 2) ; wrong

本意是想傳入add2這個Symbol的function域,但是直接這樣寫的話,傳入的其實是add2的 Value域 [7] ,這當然是不正確的。對比正確的寫法,我們甚至能猜測#'運算符就是一個 取Symbol的Function域的運算符。進一步,我們還可以給出另一種寫法:

(apply-fn (symbol-function 'add2) 2)

深入理解事情的背后,你會發(fā)現你能寫出多么靈活的代碼。

END

關于Symbol的內容還有更多,例如Package。正確理解這些內容以及他們之間的關系,有助 于更深刻地理解Lisp。

注解

[1] 在Lisp中全局變量又被稱為dynamic variables
[2] Lisp中按照習慣通常在為全局變量命名時會加上星號,就像我們習慣使用g_一樣
[3] 因為我確實在忽悠你
[4] first class type,有人翻譯為“一等公民”,我覺得壓力巨大
[5] 即高階函數
[6] “字面“主要是針對這些信息會被詞法分析程序直接處理
[7] 這可能導致更多的錯誤

posted on 2011-03-22 11:33 Kevin Lynx 閱讀(5249) 評論(8)  編輯 收藏 引用 所屬分類: lisp

評論

# re: Lisp一瞥:增強型變量Symbol 2011-03-22 12:10 OwnWaterloo

用C++去對比lisp, 當然會走彎路……  回復  更多評論   

# re: Lisp一瞥:增強型變量Symbol 2011-03-23 01:59 陳梓瀚(vczh)

你的注釋[1]的超鏈接是“http://m.shnenglu.com/kevinlynx/admin/EditPosts.aspx#id1”,弄錯了吧。  回復  更多評論   

# re: Lisp一瞥:增強型變量Symbol[未登錄] 2011-03-23 09:44 kevin lynx

@陳梓瀚(vczh)
- -| 我用工具自動生成的。。。多謝提醒。  回復  更多評論   

# re: Lisp一瞥:增強型變量Symbol 2011-03-26 10:50 nvn

受益了 :)  回復  更多評論   

# re: Lisp一瞥:增強型變量Symbol 2012-03-02 13:51 無很

很喜歡Lisp,收藏你的博客了!還有你的個人網站。
希望可以多寫點lisp的文章。  回復  更多評論   

# re: Lisp一瞥:增強型變量Symbol 2012-04-27 20:40 Kelvin Hu

寫得很詳細,醍醐灌頂一般,受教了。  回復  更多評論   

# re: Lisp一瞥:增強型變量Symbol 2012-05-23 18:05 LinkLook

哈哈,很有意思, 受教了  回復  更多評論   

# re: Lisp一瞥:增強型變量Symbol 2012-10-21 20:20 Boris

一言驚醒!  回復  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久精品国产99久久精品芒果| 亚洲视频一区二区在线观看| 一区二区在线观看av| 欧美日韩视频在线第一区| 欧美99在线视频观看| 欧美精品偷拍| 国产欧美日韩免费看aⅴ视频| 国产精品视频自拍| 怡红院精品视频在线观看极品| 亚洲国产精品久久久久久女王| 日韩视频专区| 欧美一区二区三区久久精品| 久久久噜噜噜久久| 亚洲免费观看高清完整版在线观看熊 | 国产精品久久9| 欧美裸体一区二区三区| 亚洲人成在线播放网站岛国| 一本色道久久综合亚洲精品不| 国产精品美女主播在线观看纯欲| 欧美阿v一级看视频| 国产一区深夜福利| 亚洲欧美制服中文字幕| 在线综合亚洲欧美在线视频| 免费一级欧美片在线播放| 99成人在线| 久久久亚洲综合| 欧美夜福利tv在线| 欧美日韩一本到| 午夜日韩福利| 欧美激情影音先锋| 久久国产精品高清| 国产精品久久久久永久免费观看| 亚洲激情偷拍| 久久国产一区| 亚洲少妇最新在线视频| 欧美激情视频在线播放| 国内精品视频在线观看| 久久精品中文| 久久精品视频播放| 国产日韩欧美在线看| 亚洲一二三区视频在线观看| 日韩一二在线观看| 国产精品美女一区二区| 久久精品国产v日韩v亚洲| 亚洲小说欧美另类婷婷| 国产一区二区成人久久免费影院| 一区二区三区不卡视频在线观看 | 免费一级欧美在线大片| 国语自产精品视频在线看8查询8| 久久精品国产999大香线蕉| 欧美一级免费视频| 在线成人激情| 男女精品网站| 欧美日韩精品久久| 久久久久成人精品免费播放动漫| 久久久久免费视频| 日韩午夜电影av| 欧美一二三区精品| 9i看片成人免费高清| 亚洲欧美www| 亚洲免费大片| 久久精品久久综合| 亚洲精品一二| 久久久久久久综合狠狠综合| 亚洲专区一区| 欧美日韩三级| 欧美成人亚洲成人| 国内在线观看一区二区三区| 亚洲一级特黄| 欧美一区二区三区婷婷月色 | 欧美成人精品激情在线观看| 亚洲欧美成人在线| 欧美午夜激情小视频| 欧美激情片在线观看| 曰韩精品一区二区| 久久精品二区亚洲w码| 久久精品国产69国产精品亚洲| 欧美激情精品久久久| 影音国产精品| 久久国产精品久久精品国产| 欧美一区二区精品久久911| 欧美精品在线观看91| 亚洲级视频在线观看免费1级| 国产精品每日更新| 性色av一区二区三区在线观看 | 在线观看国产精品网站| 亚洲自拍高清| 美国成人毛片| 亚洲黄页视频免费观看| 免费一区二区三区| 亚洲精品国产精品久久清纯直播| 在线观看一区| 欧美日本一区二区高清播放视频| 亚洲日本电影| 亚洲免费网址| 91久久精品国产91久久| 欧美日韩你懂的| 欧美一区=区| 亚洲欧洲在线看| 久久都是精品| 欧美一级在线视频| 亚洲福利国产精品| 欧美日韩国产123| 久久婷婷国产综合精品青草| 亚洲国产欧美精品| 久久久国产一区二区三区| 韩国一区二区在线观看| 久久国产精品久久久久久电车| av成人国产| 欧美xx视频| 一区二区高清视频在线观看| 国产精品porn| 欧美日韩高清一区| 欧美成人精品不卡视频在线观看| 夜夜躁日日躁狠狠久久88av| 国产精品有限公司| 欧美激情二区三区| 欧美精彩视频一区二区三区| 美女91精品| 免费高清在线一区| 久久夜色精品国产噜噜av| 久久久久免费观看| 欧美黄色免费网站| 欧美日韩在线播放一区| 久久国产精品一区二区三区| 久久国产婷婷国产香蕉| 免费在线欧美黄色| 亚洲第一中文字幕| 日韩视频―中文字幕| 一二三四社区欧美黄| 午夜国产不卡在线观看视频| 久久噜噜噜精品国产亚洲综合 | 一区二区亚洲精品国产| 亚洲大片av| 香港久久久电影| 久久尤物电影视频在线观看| 亚洲日本欧美| 欧美在线观看你懂的| 欧美日韩免费高清一区色橹橹| 国产欧美一区二区精品仙草咪 | 亚洲福利电影| 性欧美精品高清| 亚洲国产日本| 久久久久久亚洲精品中文字幕| 欧美精品1区2区3区| 国产专区一区| 欧美诱惑福利视频| 亚洲自拍都市欧美小说| 欧美日韩在线三区| 91久久久久久久久| 蜜桃精品久久久久久久免费影院| 亚洲性夜色噜噜噜7777| 久久精品一区| 亚洲午夜成aⅴ人片| 欧美日本免费| 91久久久国产精品| 欧美国产一区二区| 欧美xxx成人| 亚洲精品婷婷| 亚洲二区精品| 欧美日本在线播放| 亚洲最新色图| 一本大道久久a久久精二百| 欧美日韩激情网| 亚洲视频观看| 亚洲一区久久久| 激情久久婷婷| 亚洲激情在线观看| 欧美视频在线不卡| 久久精品一区二区三区不卡牛牛| 久久精品成人一区二区三区| 亚洲福利精品| 亚洲伊人伊色伊影伊综合网| 国外成人在线视频网站| 亚洲国产成人久久综合| 国产精品久久二区二区| 牛人盗摄一区二区三区视频| 欧美日韩日日骚| 久久性天堂网| 国产精品蜜臀在线观看| 欧美激情一区在线| 国产日产欧美a一级在线| 欧美日韩另类丝袜其他| 欧美电影资源| 国产欧美日韩不卡免费| 亚洲伦理一区| 亚洲欧洲一区二区三区在线观看| 亚洲免费中文| 欧美一区二区女人| 国产精品美女久久久免费| 亚洲国产99精品国自产| 亚洲丁香婷深爱综合| 久久夜色精品国产亚洲aⅴ | 久久av一区二区三区亚洲| 亚洲综合日韩在线| 欧美日韩成人在线| 亚洲电影免费观看高清完整版在线 | 欧美视频精品一区| 99精品99| 久久久久**毛片大全|