紅藥丸,仍是藍藥丸

前一篇《 周而復始》講述了遞歸函數的周而復始能夠做爲程序的動力源。

Murphis 拿出兩顆藥丸,一顆紅色,一顆藍色,讓 Neo 選。吃了紅藥丸,能夠覺醒,看到真相;吃了藍藥丸,則能夠繼續渾噩地生活下去。編程

這樣的程序,用 Emacs Lisp 該怎麼寫?segmentfault

(defun murphis (答案)
  (cond
   ((string= 答案 "紅藥丸") '紅藥丸)
   ((string= 答案 "藍藥丸") '藍藥丸)
   (t (message "沒有第三種選擇!"))))

(murphis "紅藥丸")

用 Emacs 打開 init.el 文件,將上面的代碼複製到 init.el 文件的尾部,而後將光標移動到 murphis 這個函數定義的末尾,執行 C-x C-e。這樣 Emacs Lisp 解釋器就知道了 murphis 這個函數的定義了。編輯器

接下來,(murphis "紅藥丸") 是對 murphis 函數進行求值。在《黑客帝國》裏,等同於 Neo 對 Murphis 說,我要紅藥丸!假若將光標移到這個表達式的末尾,而後執行 C-x C-e,結果就會在微緩衝區裏顯現 紅藥丸函數

在 Emacs Lisp 裏,上述的 cond 表達式,就是所謂的條件表達式。debug

cond 表達式的形式以下:code

(cond
 (<謂詞 1> <表達式 1>)
 (<謂詞 2> <表達式 2>)
 ... ... ...
 (<謂詞 n> <表達式 n>))

什麼是謂詞?上文代碼中的 (string= 答案 "紅藥丸") 就是謂詞(Predicate)。orm

簡單地說,謂詞就是一個求值結果爲真(t)或假(nil)的表達式。像 (string= 答案 "紅藥丸") 這樣的表達式,string= 是一個函數,它會判斷本身所接受的兩個字符串原子是否相同,假若相同,則結果爲真,不然爲假。遞歸

tnil 自己也能構成謂詞。由於在 Emacs Lisp 裏,表達式不必定必須是以 ( 開頭,以 ) 結尾的語句。Emacs Lisp 的原子自己就能夠構成表達式。字符串

在上述代碼中,'紅藥丸'藍藥丸 以及 "沒有第三種選擇!",它們都是原子,但也都是表達式。只要是表達式,Emacs Lisp 解釋器就能夠對它們進行求值,求值結果就是原子自己。get

這裏須要解釋一下,'紅藥丸'藍藥丸 都是符號原子。之因此要以單引號 ' 做爲前綴,是爲了不 Emacs Lisp 將它們視爲函數或參數(變量)。不妨將符號原子視爲標籤,把它貼到函數上,它就是表明函數。把它貼到函數的參數(變量)上,它就表明參數。當這個標籤的前面放上 ' 時,它就是一張未貼任何事物上的標籤。用一樣的辦法,能夠避免 Emacs Lisp 將列表視爲函數求值表達式。例如,'(list 1 2 3),在 Emacs Lisp 解釋器看來,它是由 list 這個符號原子與三個數字原子構成的列表,而不是 (list 1 2 3) 的求值結果 (1 2 3)

爲何要將 (string= 答案 "紅藥丸") 這樣的表達式稱爲謂詞呢?假若將 string= 理解爲 is,那麼這個表達式的含義就變成了 is 答案 "紅藥丸",在這句話的後面再加上個問號的話,就變成了 is 答案 "紅藥丸"?。基於初中英語知識,能夠發現這是個英文與漢語夾雜的通常疑問句。因此……差很少就這意思。

在上述的 cond 表達式中,對謂詞的求值順序是從上而下,直到某個謂詞的求值結果爲真(即 t),便對該謂詞所關聯的表達式進行求值,所得結果即爲 cond 表達式的求值結果。

假若一個 cond 表達式中全部謂詞的求值結果皆爲假(nil),那麼這個 cond 表達式的結果就是未定義。不過 Emacs Lisp 在遇到 cond 表達式的求值結果爲未定義的時候,會以 nil 做爲求值結果。

例如,

(defun murphis (答案)
  (cond
   ((string= 答案 "紅藥丸") '紅藥丸)
   ((string= 答案 "藍藥丸") '藍藥丸)))

(murphis "黑藥丸")

(murphis "黑藥丸") 的求值結果就是 nil

有的時候,只存在兩種選擇,非此即彼。像這樣的選擇,用 cond 表達式來模擬,有點繁瑣,這時能夠考慮用 若是...就...不然... 這樣的表達式。例如:

(defun 齒輪 (m n)
  (if (< m n)
      (progn
        (insert (format "%d-" m))
        (齒輪 (+ m 1) n))
    (insert (format "%d\n" m))))

在推敲這段代碼以前,先看一下 if 表達式的結構:

(if <謂詞> <表達式 1> <表達式 2>)

這個結構能夠這樣解讀,若是 <謂詞> 爲真,就對 <表達式 1> 求值,不然就對 <表達式 2> 求值。

將這個語法結構套到 齒輪 函數裏的 if 表達式,結果是,若是 (< m n) 的求值結果爲真,那麼就對錶達式 (progn (insert (format "%d-" m)) (齒輪 (+ m 1) n)) 進行求值,不然就對 (insert (format "%d\n" m)) 進行求值。這幾個較長的表達式看上去有點複雜,須要有點耐心,逐層進行拆解。

首先來看 progn 表達式,它的做用是讓 Emacs Lisp 解釋器依序對一組並列的表達式進行求值:

(progn
  <表達式 1>
  <表達式 2>
  ... ... ...
  < 表達式 n>)

因爲 progn 自己也是一個表達式。按照 Emacs Lisp 解釋器的法律,每一個表達式必須有一個值。所以,progn 表達式會將它所囊括的這組表達式中的最後一個表達式的求值結果做爲自身的結果。

實際上,list 表達式也能產生相似 progn 的效用。例如:

(list
  <表達式 1>
  <表達式 2>
  ... ... ...
  < 表達式 n>)

Emacs Lisp 解釋器會對 list 囊括的這組表達式依序求值。只不過 list 返回的是這組表達式的所有的求值結果,亦即列表。

insert 函數,咱們已經見識過屢次了。雖然一直沒對它作太多介紹,可是經過實踐,不難發現,它能夠在緩衝區裏光標所在的位置插入文本——字符串原子。

format 函數,它能夠用於構造字符串原子,只不過是以格式化的方式。所謂格式化,就至關於……作語文填空題。例如,(format "%d-" m),就至關於在 "( )-" 這句話的括號內填寫一個數字。假若你填 1,那麼這句話就變成了 "1-"。同理 (format "%d\n" m)),假若填上 10,結果就是 "10\n"。在字符串原子中,\n 這個字符表示換行,至關於你在文本編輯器裏輸入一行文本以後,單擊了一次回車鍵。

還有一個很簡單的表達式,(+ m 1),它的意思是 m + 1+ 是數字原子的運算符,它也是一個函數。相似的還有 -*/<>=>=<=。雖然這些運算符的用法不太符合咱們在小學數學裏學到的運算方法,但有的時候它也頗有用。例如,你去超市買了許多東西,回家後想覈對一下錢數,能夠這樣算 (+ 2.5 8.75 9 10 20 35.5)

弄懂了這些瑣碎的函數的含義,齒輪 函數的定義差很少能夠看懂了吧?這個函數最難看懂的部分實際上是它在內部對自身進行求值——遞歸求值。

還記得那個 c-malloc 函數嗎?

(defun c-malloc (name type n)
  (interactive
   (list (read-string "變量名稱?")
     (read-string "類型?")
     (read-string "數量?")))
  (insert (format "%s *%s = malloc(%s * sizeof(%s));" type name n type)))

當我依序回答這個函數問個人三個問題以後,它就會在緩衝區裏顯現:

double *foo = malloc(n * sizeof(double));

這個函數有個問題,當 n 爲 1 時,它會給出:

double *foo = malloc(1 * sizeof(double));

在 C 語言中,1 * sizeof(double) 表示拿 1 去乘一個數。小學生都知道,拿 1 去乘一個數就等於那個數自己。所以 c-malloc 若想表現本身已經小學畢業了,那麼對於這樣的狀況,它應該給出:

double *foo = malloc(sizeof(double));

爲此,須要對 c-malloc 略做改進:

(defun c-malloc (name type n)
  (interactive
   (list (read-string "變量名稱?")
     (read-string "類型?")
     (read-string "數量?")))
  (if (string= n "1")
      (insert (format "%s *%s = malloc(sizeof(%s));" type name type))
    (insert (format "%s *%s = malloc(%s * sizeof(%s));" type name n type))))

以上,就是 Emacs Lisp 語言中經常使用的條件表達式的用法。在程序中,咱們能夠用它們來模擬「選擇」。

一些熱衷於從哲學層面探究人生意義的成功人士喜歡說這樣的話,人生的意義,就在於你的選擇!

他們如果略微懂一點編程,就會發現這樣的話有些愚蠢。假若人生的意義真的是在於選擇,這就是變相地認可本身活在一個編制好的程序裏。任何選擇,在這個程序裏,不過是一個謂詞,而與這個謂詞相關聯的表達式早已在那裏了。也就是說,不管你怎麼選擇,你的人生早已肯定。所以,說人生的意義在於選擇,無異於說本身相信天命所歸,這就是所謂的宿命論。假若一切都是程序安排好的結果,那麼你選與不選,本質上並無區別,你並無權力不選擇什麼……

在我看來,人生的意義至少在於 debug。不管怎麼看,去消除這個世界的 bug,總比被這個世界逼迫着成天做各類選擇更有意義。

下一篇周遊抑或毀滅世界
相關文章
相關標籤/搜索