為什麼mathematica的語法和lisp很近?

mathematica到處是括弧嵌套,lisp更是。


題主問的這個問題比較大,簡單來說他們都支持函數式編程。具體的話主要從兩個方面回答一下,一是這兩種語法結構的異同比較,二是我對Mathematica為何這麼設計的一點愚見。

一、S-表達式與M-表達式

在Lisp誕生之初,「人工智慧之父」John McCarthy設計了一種叫M-表達式的語法,與C/C++的語法類似。比如S-表達式(cdr "(1 2 3))用M-表達式就寫成cdr[1, 2, 3]。但是Lisp的程序員們紛紛放棄了M-表達式,選擇直接使用S-表達式。S-表達式的實質是用抽象句法樹(AST)表達程序,直接省去了解析這道工序。

但是,這種M-表達式的語法卻是和Mathematica很相似的。實際上,無論是M-表達式還是S-表達式,理念是一樣的,並且可以相互轉換:

  • 若要把M-表達式例如Integrate[x + y, x]轉換成S-表達式,只要把它的頭部Integrate放到圓括弧里,作為第一項,即(Integrate (+ x y) x),要注意到S-表達式中的空格和M-表達式中的逗號等價。
  • 若要把S-表達式轉成M-表達式,也很容易,只要把圓括弧中的第一項提出去即可,因為第一項本身就是運算符(例如(+ x y)中的加號)。
  • 但存在特例,表示純數據的S-表達式如"(1 2 3),它對應到M-表達式可以認為是List[1, 2, 3],或者是更相似一點的{1, 2, 3},並且後者正是Mathematica中定義列表的語法。

也就是說,兩種語言的語法看起來都像是括弧嵌套,實際上lisp屬於S-表達式,Mathematica屬於M-表達式,並且這兩種表達方式有等價的表達能力。

二、列表的頭部

那Mathematica為何要使用M-表達式來作為它的語法基礎呢?

可能他們都支持函數式編程,這是其一。但我個人覺得,既然列表中的第一項有特殊作用,那把它單獨拎出來又未嘗不可呢?

於是,Mathematica中便有了一個新的概念,叫做頭部(Head)。一個列表不僅包含自己的數據,還包含一個「頭部」。

而對「頭部」的修改,Mathematica中正有一個函數叫做Apply,它的作用便是用指定的頭部去替換原來列表的頭部。一般來說,頭部是一個操作或者函數。

譬如:

In[1]: Apply[g, f[1, 2, 3]]
Out[1]: g[1, 2, 3]
In[2]: g @@ f[1, 2, 3]
Out[2]: g[1, 2, 3]

這裡就用g替換掉了原來的列表的頭部f,@@是Apply的簡寫形式。

特殊地,我們可以用List替換掉一個表達式的頭部,以取得它的參數列表:

In[3]: List @@ (a*b+c*5+f[d])
Out[3]: {a b, 5 c, f[d]}

也可以反過來,用一個指定的函數(比如func)來作用到一個List上:

In[4]: func @@ {param1, param2, param3}
Out[4]: func[param1, param2, param3]

這相當於隱式調用了func函數,{param1, param2, param3}是參數列表,也就意味著我們在運行時動態生成了代碼!這類似於C++中的函數指針、Java中的反射機制等,但實現都比較繁瑣。而在Mathematica中,一個Apply函數就這麼輕鬆的做到了。實際上任何支持函數式編程的語言,都有類似的函數。

因為在函數式編程中,「數據即代碼,代碼即數據」,兩者已經融合到一起了,沒有本質的區別(lambda演算為此提供了堅實的根基)。你可以把要執行的代碼存放在數據中,按照普通的方式存取,然後調用Apply,這些數據便「活」了,它們被當成了代碼運行。通過這種機制,寫一個在運行時修改自身代碼的程序變得很容易,掃描要執行的代碼的結構也很輕鬆,就像是在遍歷一棵樹一樣。(語法樹遍歷)

lisp中當然也有apply函數,但在S-表達式中的解釋卻不直接。而在M-表達式中,卻可以直接把他解釋為替換列表的頭部,而不需要「數據即代碼」這種解釋,這個設計的確是很精妙的。更簡潔一些。

所以總結一下,題主只問了這兩種語言為何接近,是因為他們分別用了M-表達式和S-表達式。其不同之處在於Mathematica多了「頭部」這個概念。至於為何都是括弧嵌套,那是因為它們都基於expression表達式而非statement語句,所以會更容易出現嵌套。

註:以上內容部分摘自我之前整理的 Mathematica中的列表 的內容。


當初Lisp剛出來的時候肩負的一個重要的使命就是解決符號計算問題(引自sicp),當時數值計算已經很發達了, 既然計算機可以做數值積分什麼的, 為啥不能直接推導公式呢? 後來大家發現函數式編程這東西很好玩, 越玩越hi, 拿lisp來做各種事情, 符號計算就變成諸多功能之一了。剛看到sicp第2章, 作者用不到十行scheme(lisp方言)代碼就實現了一個簡單的符號求導功能。

所以個人認為兩者是為了達到同一個目的, 同時Lisp要遠早於MMA, 因此MMA在設計上接近Lisp不是沒有道理的, 另外一個老牌的符號計算程序Maxima直接是用Lisp寫成的。


因為Lisp逼格高,符合Stephen Wolfram的品味。

恰巧Stephen Wolfram是個天才,人家說干就干。

MMA幾乎就是Stephen Wolfram一力主導做下來的。說他一個人幹了一個標準委員會的工作雖然有些誇張,但也和事實相差不遠。

用超級抽象的語言去搞科學計算大概也只有他能把這個點子執行下來吧。


因為mathematica的構建原則就是Lisp解釋器的eval-apply。

看過斯蒂芬mathematica手冊第二章"mathematica原理"和《計算機程序的構造和解釋》第四章「元語言抽象"後就很好理解。

Lisp有最合理的語法


推薦閱讀:

用mathematica寫作業有哪些提高效率的使用技巧?
為何Mathematica解三角函數方程組可行性這麼低呢?
Mathematica 做數值計算時有哪些方式可以達到提速的目的?
如何使用mathematica發送郵件?
為什麼在 Mathematica 中使用循環是低效的?

TAG:WolframMathematica | Lisp | CommonLisp |