2015-01-17 45 views
2

在Clojure中可調用的對象,哈希映射和向量實行invoke,使得它們可被用作功能,例如模擬Clojure的風格的Common Lisp中

(let [dict {:species "Ursus horribilis" 
      :ornery :true 
      :diet "You"}] 
    (dict :diet)) 

lein> "You" 

,或者對於載體,

(let [v [42 613 28]] 
    (v 1)) 

lein> 613 

可以通過讓Clojure實現IFn來製作可調用對象。我是Common Lisp的新手 - 是可以調用的對象,如果是的話,那麼執行的內容是什麼?我真的希望能夠做的事情一樣

(let ((A (make-array (list n n) ...))) 
    (loop for i from 0 to n 
     for j from 0 to m 
     do (setf (A i j) (something i j))) 
    A) 

,而不是有aref散落代碼。同樣,如果你可以訪問其他數據結構的條目,這將是很酷的,例如,字典,同樣的方式。

我已經在Lisp/Scheme中查看了函數對象的wiki entry,好像有一個單獨的函數名稱空間會使CL的問題複雜化,而在Scheme中,您可以使用閉包來完成此操作。

回答

4

Common Lisp中

可調用對象的前體可調用的對象的實施例之前已經提供。例如,在Lisp機器的Lisp

Command: ("abc" 1)   ; doesn't work in Common Lisp 
#\b 

綁定Common Lisp中

Common Lisp的有功能和價值的名稱不同的命名空間。所以(array 10 1 20)只會有意義,當array將是一個符號表示函數名稱空間中的函數。因此函數值將是一個可調用的數組。

使得綁定到變量的值作爲函數大多會破壞函數和值的不同名稱空間的用途。

(let ((v #(1 2 3)))   
    (v 10))     ; doesn't work in Common Lisp 

對於具有不同功能和值的命名空間的語言,上面沒有任何意義。

FLET用於功能而不是LET

(flet ((v #(1 2 3 4 5 6 7))) ; doesn't work in Common Lisp 
    (v 4))      

這意味着我們會將數據放入函數名稱空間。我們想要嗎?不是真的。

字形數據作爲函數調用中的函數。

人們還可以想到的,至少讓文字數據的行爲,如直接函數的函數的調用:

(#(1 2 3 4 5 6 7) 4)   ; doesn't work in Common Lisp 

,而不是

(aref #(1 2 3 4 5 6 7) 4) 

Common Lisp中不允許以任何微不足道的或相對簡單的方法。

邊備註:

一個可以實現在整合功能和價值與CLOS方向的東西,因爲CLOS通用功能也是類STANDARD-GENERIC-FUNCTION的CLOS情況下,它是可能有和使用用戶自定義那個的子類。但這通常不會被利用。

建議

因此,最好調整到不同的語言風格和使用CL,因爲它是。在這種情況下,Common Lisp不夠靈活,無法輕鬆整合這種功能。不要忽略用於次要代碼優化的符號,這是一般的CL風格。危險是混淆和只寫代碼,因爲很多信息不是直接在源代碼中,那麼。

+0

實際上,在Clojure中這超出了命名空間的分離。下面的Clojure可以像你期望的那樣工作:'({:a「第一個鍵」:b「第二個鍵」}:a)'會產生第一個鍵「,所以Clojure在這個行爲就像* Lisp Machine Lisp *尊重。所以,是的,在Clojure中,對於某些類型的「數據在函數名稱空間中」。 「我們想要那個嗎?」顯然,korrok對此的回答是肯定的,但Common Lisps的答案是非常不。 JFTR雖然採用Clojure的方式實現了一些非常簡潔的解決方案,但它們有時很難掌握。 – schaueho

+0

@schauerho:問題是,鑑於其語義,我們是否需要Common Lisp? CL是Lisp-2,Clojure是Lisp-1。 –

+0

我也希望字面數據作爲函數在調用中沒有用處,因爲它比較少見。使用綁定數據作爲函數似乎是一個不太少見的用例... –

4

儘管可能沒有辦法完成您想要做的事情,但有一些方法可以將相似的東西拼湊在一起。一種選擇是定義一個新的綁定表單,可調用,允許我們將函數本地綁定到可調用對象。例如,我們可以讓

(with-callable ((x (make-array ...))) 
    (x ...)) 

大致相當於

(let ((x (make-array ...))) 
    (aref x ...)) 

這裏是一個可能的定義與可調用:

(defmacro with-callable (bindings &body body) 
    "For each binding that contains a name and an expression, bind the 
    name to a local function which will be a callable form of the 
    value of the expression." 
    (let ((gensyms (loop for b in bindings collect (gensym)))) 
    `(let ,(loop for (var val) in bindings 
       for g in gensyms 
       collect `(,g (make-callable ,val))) 
     (flet ,(loop for (var val) in bindings 
        for g in gensyms 
        collect `(,var (&rest args) (apply ,g args))) 
     ,@body)))) 

所有剩下的就是定義做出不同的方法 - 可用於訪問對象的返回閉包。例如,下面是將其定義爲數組的方法:

(defmethod make-callable ((obj array)) 
    "Make an array callable." 
    (lambda (&rest indices) 
    (apply #'aref obj indices))) 

由於這句法是一種醜陋的,我們可以使用宏,使其更漂亮。

(defmacro defcallable (type args &body body) 
    "Define how a callable form of TYPE should get access into it." 
    `(defmethod make-callable ((,(car args) ,type)) 
    ,(format nil "Make a ~A callable." type) 
    (lambda ,(cdr args) ,@body))) 

我們做陣列可調用,我們可以使用:

(defcallable array (obj &rest indicies) 
    (apply #'aref obj indicies)) 

好多了。我們現在有一個可調用的表單,它將定義允許我們訪問對象的本地函數,以及一個可僞造的宏,它允許我們定義如何製作其他類型的可調用版本。這種策略的一個缺陷是我們每次想要使對象可調用時都必須明確地使用with-callable。


另一種選擇,類似於可調用的對象是弧形的結構訪問ssyntax。基本上x.5訪問x中索引爲5的元素。我能夠在Common Lisp中實現這一點。你可以看到我爲它編寫的代碼herehere。我也有測試,所以你可以看到使用它看起來像here

我的實現是如何工作的我寫了一個宏w/ssyntax,它查看了正文中的所有符號,併爲其中的一些符號定義了宏和符號宏。例如,x.5的符號宏可以是(get x 5),其中get是我定義的訪問結構的通用函數。這個缺點是我總是不得不在任何我想使用ssyntax的地方使用w/ssyntax。幸運的是,我能夠將其隱藏在像defun一樣行動的宏指令中。

+0

可能。但是'(vn)'比'(aref vn)'慢十到二十倍,因爲調用開銷,內存佔用...... –

+0

@RainerJoswig我想你可以用可調用生成marcolet和symbol-macrolet使可調用對象看起來像函數的綁定。本地宏將在編譯時擴展,使(v n)等價於(aref v n),並且符號宏仍然允許將可調用對象傳遞給其他函數。 (mapcar v'(0 1 2))會得到前三個元素]。這會更復雜,但不是太多。 – malisper

+1

除非您有權訪問編譯時類型信息(以查看它是否爲AREF,GETHASH或其他內容......),否則在編譯時您一般不知道對象的類型。因此,編譯器不會知道某件事是簡單的向量,並將訪問編譯爲內聯簡單向量引用... –

0

我同意Rainer Joswig的建議:最好能夠適應Common Lisp的做事方式 - 就像Common Lisp程序員更適合使用Clojure的做事方式,切換到Clojure 。但是,如果malisper的複雜答案顯示,它可能會做你想要的一部分的。下面是一個簡單的策略開始:

(defun make-array-fn (a) 
    "Return a function that, when passed an integer i, will 
    return the element of array a at index i." 
    (lambda (i) (aref a i))) 

(setf (symbol-function 'foo) (make-array-fn #(4 5 6))) 

(foo 0) ; => 4 
(foo 1) ; => 5 
(foo 2) ; => 6 

symbol-function訪問符號foo的功能細胞,並把setf通過make-array-fn創建的函數對象進去。由於此功能在功能單元中,因此foo可用於列表的功能位置。如果你願意,你可以將整個操作歸納爲一個宏,例如像這樣:

(defmacro def-array-fn (sym a) 
    "Define sym as a function that is the result of (make-array-fn a)." 
    `(setf (symbol-function ',sym) 
     (make-array-fn ,a))) 

(def-array-fn bar #(10 20 30 40)) 

(bar 0) ; => 10 
(bar 1) ; => 20 
(bar 3) ; => 40 

當然,「陣列」這樣定義不再看起來像數組。我想你可以用CL的打印程序做一些事情。也可以允許設置數組的值,但這可能需要單獨的符號。