2010-08-11 61 views
5

我正在傳遞函數的名稱以用於其他方法。在Clojure中通過方法名稱進行評估的習慣性方法?

(defn mapper [m function] 
    (cond 
    (= '() m) '() 
    true (cons (function (first m)) 
      (mapper (rest m) function)))) 

(println (mapper '((blue red)(green red)(white red)) #'first)) 

在clojure有沒有更習慣的方式來做到這一點?

+2

我猜測,_most_慣用的方式做到這一點是使用[圖](http://clojure.github.com/clojure/clojure.core-api .html#clojure.core/map)函數:'(map first'((blue red)(green red)(white red)))'。 – Jonas 2010-08-11 12:46:54

回答

8
  • 更喜歡向量列表。大多數情況下,您不必引用矢量,並且它對很多事情(例如隨機訪問)具有更好的性能。在Clojure中使用列表的次數比在其他Lisp中少得多。
  • 首選引用符號的關鍵字。關鍵字突出顯示爲「常量字符串」或枚舉值。 Clojure中的關鍵詞可以屬於一個名稱空間,因此它們具有符號的所有優點。再次,沒有必要引用關鍵字,這很好。引用符號在Clojure中很少使用,除非您正在編寫宏。
  • #'first是被稱爲「第一」的變種; first是稱爲「第一」的var的值,即fn。在這種情況下,(#'first foo)(first foo)給出相同的答案,但每次調用它時,#'first都會有額外的解除引用。所以不要這樣做,除非你想讓這個解除引用反覆發生。通常不需要使用#'
  • 內置的map是懶惰的,而你的不是。內置的map利用分塊seqs更好的性能,而你的沒有。語法代碼不必懶惰或使用分塊seq,但請記住,內建的這些魔法正在發生。所以利用好是件好事。
  • 而不是(= '() x),空seq的慣用測試是(seq x),如果x爲空,則返回nil。請注意,在Clojure中,(= '() nil)是錯誤的。
  • 如果您確實需要使用空列表(您應該很少需要這樣做),則不必引用它。只需使用()即可。
  • 內置map首先接受函數參數,因爲它接受多個集合參數。當一個函數接受多個參數時,這些參數必須在參數列表中最後一個。我認爲它也可以更好地讀取它:「(map f coll):在這個集合中映射這個函數」。
  • 如果您只有兩個選項,則無需使用cond。您可以改用if。如果您的if中的某個分支返回nil,則可以使用when。在適當的時候使用whenif是很好的,因爲它們會立即向讀者表明您的意圖,而cond可以做任何事情並迫使讀者閱讀更多內容。

RafałDowgird的版本是慣用的,除了我翻轉參數的順序。而且我這樣稱呼它:

user> (mapper first [[:blue :red] [:green :red] [:white :red]]) 
(:blue :green :white) 
+0

同意翻轉論據。無論是那個還是'​​(defn mapper [coll f&args] ...)','args'都作爲附加參數傳遞給f,這樣''(mapper [5 2 3] + 1)'是可能的。 – 2010-08-11 20:37:21

+0

提示的大集合。 – edbond 2010-08-12 07:02:19

4

我相信你主要是慣用的。 Clojure的自己map用途:

(defn mapper [coll f] 
(when-let [s (seq coll)] 
    (cons (f (first s)) (mapper (rest s) f)))) 

我已經嚴重縮短了 - 原來產生懶惰的序列,與多個集合,分塊,seqs等。順便說一句交易 - 我猜你想傳遞的實際功能,不是它的名稱

collf分別是代表集合和函數的慣用arg名稱。

4

您的版本看起來不錯。在clojure代碼庫中會看到的通常名稱是集合的「coll」。我認爲,我也看到了'xs'這是Haskell風格。您也可以參考各種約定Clojure library coding standards

回到這個例子:兩個觀察。

  1. 使用:else將'cond'作爲轉義條件,而不是Common Lisp樣式'T'。
  2. 而不是假設列表,思考序列。

有了這兩個想法,如果我重寫代碼:

user> (defn mapper [coll f] 
     (cond 
      (not (seq coll)) nil 
      :else (conj (mapper (next coll) f) 
         (f (first coll))))) 
#'user/mapper 
user> (mapper '(1 2 3) #(* % %)) 
(1 4 9) 
user> (mapper [1 2 3] #(* % %)) 
(1 4 9) 

注意連詞做「正確的事」儘可能收集有關。它將新元素添加到列表的頭部,添加到矢量的尾部等等。還要注意在傳統的lisp中使用'next'而不是第一個/其餘的習語。 'next'返回第一個元素之後的一系列元素。所以,可以通過選擇集合來檢查空白性,該集合將返回空列表或空向量的零。這樣它適用於所有集合。