[macros] 是什麼讓Lisp宏如此特別?



Answers

你會在這裡找到關於lisp宏的全面辯論。

該文章的一個有趣的子集:

在大多數編程語言中,語法很複雜。 宏必須拆開程序語法,分析它,然後重新組裝它。 他們無法訪問程序的解析器,因此他們必須依賴啟發式和最佳猜測。 有時他們的削減率分析是錯誤的,然後他們破裂。

但是Lisp是不同的。 Lisp宏可以訪問解析器,它是一個非常簡單的解析器。 Lisp宏不是一個字符串,而是一個列表形式的源代碼,因為Lisp程序的源不是一個字符串; 這是一個列表。 Lisp程序非常擅長將列表拆分並重新組合。 他們每天都可靠地做到這一點。

這是一個擴展的例子。 Lisp有一個叫做“setf”的宏來執行任務。 最簡單的setf形式是

  (setf x whatever)

它將符號“x”的值設置為表達式“whatever”的值。

Lisp也有列表; 您可以使用“car”和“cdr”函數分別獲取列表的第一個元素或列表的其餘部分。

現在,如果你想用新值替換列表的第一個元素呢? 這樣做有一個標準功能,令人難以置信的是,它的名字比“汽車”還要糟糕。 這是“rplaca”。 但是你不必記得“rplaca”,因為你可以寫

  (setf (car somelist) whatever)

設置somelist的汽車。

這裡真正發生的是“setf”是一個宏。 在編譯時,它檢查它的論點,並且它看到第一個有形式(汽車SOMETHING)。 它對自己說:“噢,程序員正在試圖設置一些東西,這個功能就是'rplaca'。” 它悄悄地將代碼重寫為:

  (rplaca somelist whatever)
Question

閱讀Paul Graham關於編程語言的散文 ,人們會認為Lisp宏是唯一的出路。 作為一名繁忙的開發人員,在其他平台上工作,我沒有使用Lisp宏的特權。 作為想要了解這一嗡嗡聲的人,請解釋是什麼讓這個功能如此強大。

請將此與我從Python,Java,C#或C開發世界中理解的內容聯繫起來。




雖然上面的所有內容都解釋了宏是什麼,甚至有很酷的例子,但我認為宏和普通函數之間的主要區別在於,在調用函數之前,LISP首先評估所有參數。 使用宏是相反的,LISP將未評估的參數傳遞給宏。 例如,如果將(+ 1 2)傳遞給某個函數,則該函數將接收到值3.如果將其傳遞給宏,它將收到一個List(+ 1 2)。 這可以用來做各種令人難以置信的有用的東西。

  • 添加一個新的控制結構,例如循環或解構列表
  • 測量執行傳入函數所需的時間。使用函數時,參數將在控制傳遞給函數之前進行評估。 使用宏,您可以在秒錶的開始和停止之間拼接代碼。 下面的代碼在宏和函數中完全相同,輸出是非常不同的。 注意:這是一個人為設計的例子,選擇了實現,以便更好地突出區別。

    (defmacro working-timer (b) 
      (let (
            (start (get-universal-time))
            (result (eval b))) ;; not splicing here to keep stuff simple
        ((- (get-universal-time) start))))
    
    (defun my-broken-timer (b)
      (let (
            (start (get-universal-time))
            (result (eval b)))    ;; doesn't even need eval
        ((- (get-universal-time) start))))
    
    (working-timer (sleep 10)) => 10
    
    (broken-timer (sleep 10)) => 0
    



Lisp宏代表幾乎任何大型編程項目中都會出現的模式。 最終在一個大型程序中,你有一段代碼,你意識到它會更簡單,更不容易出錯,因為你編寫的程序將源代碼輸出為文本,然後你可以直接粘貼。

在Python對像中有兩個方法__repr____str____str__只是人類可讀的表示。 __repr__返回一個有效Python代碼的表示形式,也就是說,可以作為有效的Python輸入到解釋器中。 通過這種方式,您可以創建一小段Python代碼,生成可以粘貼到實際源代碼中的有效代碼。

在Lisp中,整個過程已經由宏觀系統形式化。 當然,它可以讓你創建語法的擴展並做各種各樣的花哨的事情,但它的實際用處總結在上面。 當然,這有助於Lisp宏觀系統允許您使用整個語言的全部功能來操縱這些“片段”。




Lisp宏允許您決定何時(如果有的話)任何部分或表達式將被評估。 舉個簡單的例子,想想C:

expr1 && expr2 && expr3 ...

這說的是:評估expr1 ,如果它是真的,則評估expr2等。

現在嘗試使這個&&成為一個函數......這就是,你不能。 調用類似於:

and(expr1, expr2, expr3)

無論expr1是否為假,都將在得出答案之前評估所有三個exprs

利用lisp宏,你可以編寫如下代碼:

(defmacro && (expr1 &rest exprs)
    `(if ,expr1                     ;` Warning: I have not tested
         (&& ,@exprs)               ;   this and might be wrong!
         nil))

現在你有一個&& ,你可以調用它就像一個函數,它不會評估你傳遞給它的任何表單,除非它們全都是真實的。

要看到這是如何有用的,對比:

(&& (very-cheap-operation)
    (very-expensive-operation)
    (operation-with-serious-side-effects))

和:

and(very_cheap_operation(),
    very_expensive_operation(),
    operation_with_serious_side_effects());

你可以用宏做的其他事情是創建新的關鍵字和/或小型語言(例如查看(loop ...)宏),將其他語言集成到lisp中,例如,可以編寫一個宏讓你說一些像:

(setvar *rows* (sql select count(*)
                      from some-table
                     where column1 = "Yes"
                       and column2 like "some%string%")

這甚至沒有進入Reader宏

希望這可以幫助。




在Python中你有裝飾器,你基本上有一個函數,將另一個函數作為輸入。 你可以做任何你想做的事情:調用函數,做其他事情,將函數調用包裝在資源獲取版本中等等,但是你不能窺視那個函數。 假設我們想讓它更強大,比方說你的裝飾器將函數的代碼作為一個列表接收,那麼你不僅可以按原樣執行函數,而且現在可以執行它的一部分,重新​​排序函數的行數等等。




我不確定我可以為每個人的(優秀)帖子添加一些洞察,但是......

由於Lisp語法本質,Lisp宏很好用。

Lisp是一種非常規則的語言(想像一切都是列表 ); 宏允許您將數據和代碼視為相同(不需要字符串解析或其他修改lisp表達式的操作)。 您可以將這兩個功能結合起來,並且您有一個非常乾淨的方式來修改代碼

編輯:我想說的是,Lisp是homoiconic ,這意味著lisp程序的數據結構是用lisp本身編寫的。

所以,你最終會在語言之上用自己的全部功能創建自己的代碼生成器(例如,在Java中你必須用字節碼編織來破解你的方式,儘管一些像AspectJ這樣的框架可以讓你使用不同的方法來做到這一點,它基本上是一個黑客)。

在實踐中,通過使用宏,您最終可以在lisp之上構建自己的迷你語言 ,而無需學習其他語言或工具,並且可以充分利用語言本身的全部功能。




lisp宏將程序片段作為輸入。 這個程序片段代表一個數據結構,可以任何你喜歡的方式進行操作和轉換。 最後宏輸出另一個程序片段,這個片段是在運行時執行的。

C#沒有宏設施,但是如果編譯器將代碼解析為CodeDOM樹並將其傳遞給一個方法,該方法將其轉換為另一個CodeDOM,然後將其編譯為IL,則該方法相當於。

這可以用來實現“糖”語法,如using -clause,linq select -expressions等for each語句作為轉換為底層代碼的宏。

如果Java有宏,則可以在Java中實現Linq語法,而不需要Sun更改基本語言。

這是用於實現using C#中的lisp風格宏如何看起來的偽代碼:

define macro "using":
    using ($type $varname = $expression) $block
into:
    $type $varname;
    try {
       $varname = $expression;
       $block;
    } finally {
       $varname.Dispose();
    }



Links