2013-05-03 61 views
2

我正在通過優秀的書讓Lambda,我試圖將Common Lisp代碼移植到Clojure。沒有parens反引號

下面的命令生成一個應該採取

(defn defunits-chaining [u units prev] 
    (if (some #(= u %) prev) 
    (throw (Throwable. (str u " depends on " prev)))) 
    (let [spec (first (filter #(= u (first %)) units))] 
    (if (nil? spec) 
     (throw (Throwable. (str "unknown unit " u))) 
     (let [chain (second spec)] 
     (if (list? chain) 
      (* (first chain) 
      (defunits-chaining 
       (second chain) 
       units 
       (cons u prev))) 
      chain))))) 

(defmacro defunits [quantity base-unit & units] 
    `(defmacro ~(symbol (str "unit-of-" quantity)) 
    [valu# un#] 
    `(* ~valu# 
     ~(case un# 
      ~base-unit 1 
      [email protected](map (fn [x] 
        `(~(first x)    ;; <-- PRETTY SURE IT'S THIS `(
         ~(defunits-chaining 
          (first x) 
          (cons `(~base-unit 1) 
           (partition 2 units)) 
          nil))) 
        (partition 2 units)))))) 

(defunits time s m 60 h 3600) 

並把它變成一個可以稱爲像

​​

宏,並給在基座單元的結果的宏(秒這個案例)。我認爲問題是「案例」中的Clojure/CL API改變。 「案例」,在CL看起來是這樣的:

(case 'a (('b) 'no) (('c) 'nope) (('a) 'yes)) 

但Clojure中......

(case 'a 'b 'no 'c 'nope 'a 'yes) 

多麼方便。我在嵌套defmacro改變了我的匿名功能,但它一直像產生

(case un# 
    s 1 
    (m 60) 
    (h 3600) 

如何防止那些外的括號?

回答

4

只要將您的map更改爲mapcat即可。

(defmacro defunits [quantity base-unit & units] 
    `(defmacro ~(symbol (str "unit-of-" quantity)) 
    [valu# un#] 
    `(* ~valu# 
     ~(case un# 
      ~base-unit 1 
      [email protected](mapcat (fn [x] ;; <----- mapcat instead of map is the only change 
         `(~(first x) 
          ~(defunits-chaining 
          (first x) 
          (cons `(~base-unit 1) 
            (partition 2 units)) 
          nil))) 
         (partition 2 units)))))) 

爲了詳細說明,以及當前接受的答案迴應:flatten是永遠,永遠,永遠正確的(除了極少數例外供只有專家)。充其量,這是一種創可貼,它可以爲您提供簡單輸入的正確結果,但是會導致複雜的輸入失敗。相反,通常使用apply concat可以將列表扁平化一級,或者使用mapcat而不是map來生成一個已經很平坦的列表,或者可能是一些更復雜且專門針對特別複雜的數據結構的東西。在這裏,一個簡單的mapcat就是需要的。

我想我應該注意到,defunits宏的輸入是非常嚴格的約束(他們真的必須是符號和數字),實際上不會有輸入flatten會產生錯誤的輸出。但是扁平化是一個不好的習慣,無論如何,它會導致更長,更復雜的代碼。

+0

+1「flatten」的完美描述 – 2013-05-03 03:42:38

+0

這正是我之前想要的,我想我需要在clojure標準函數中進行一些挖掘。 – 2013-05-03 04:17:06

+1

是的,再看一遍這個問題,這是*正確的答案。 – 2013-05-03 04:23:52

3

您可以連接圓括號對(使用,如apply concat)或使用amalloy的答案(最好的主意)。

你也可以使用序列功能的構建你的回報形式:

(defmacro defunits [...] 
    (let [macro-name (symbol ...) 
     valsym (gensym "val__") 
     unitsym (gensym "unit__")] 
    (list `defmacro    ; using ` so the symbol gets ns-qualified 
      macro-name 
      [valsym unitsym] 
      (make-body-expression ...)))) ; define appropriately 

我覺得這是有道理的在這裏,因爲你最終反正unquoting幾乎一切,用的語法引號兩個層次;儘管這很大程度上是品味的問題。

+0

哇,非常感謝,這要花一兩天才能解決。我不知道你可以從seq構建它,這真的很酷。這是非常深思熟慮,非常感謝。 – Steve 2013-05-03 01:29:01

+0

宏只是適當註冊(使用編譯器)函數返回列表結構。帶引號的反引用/語法引用只是產生列表結構的一種可能方式。每個人都可以在沒有其他人的情況下使用(並且有用)(儘管我相信Clojure在宏之外使用語法引用的次數比我在Scheme中使用反引號的次數要少)。 – 2013-05-03 03:48:21

+1

編輯表明我認爲amalloy的答案是最值得的綠色標記。我仍然喜歡展開一層語法引用,所以我會留下這個建議的答案。 – 2013-05-03 04:25:16

6

如果你把地圖放在平坦的地方,它應該會產生你要找的結果。

(defmacro defunits [quantity base-unit & units] 
    `(defmacro ~(symbol (str "unit-of-" quantity)) 
    [valu# un#] 
    `(* ~valu# 
     ~(case un# 
      ~base-unit 1 
      [email protected](flatten ;; <- changed this 
       (map (fn [x] 
         `(~(first x) 
         ~(defunits-chaining 
          (first x) 
          (cons `(~base-unit 1) 
            (partition 2 units)) 
          nil))) 
        (partition 2 units))))))) 

發生了什麼:在您的初始實現中,當您需要的是原子列表時,您的映射會返回一些列表列表。 Flatten需要一個任意深度的列表並將其轉換爲一個值列表。

另一種方法是使用一個reduce,而不是圖:

(defmacro defunits [quantity base-unit & units] 
    `(defmacro ~(symbol (str "my-unit-of-" quantity)) 
    [valu# un#] 
    `(* ~valu# 
     ~(case un# 
      ~base-unit 1 
      [email protected](reduce (fn [x y] ;; <- reduce instead of map 
         (concat x ;; <- use concat to string the values together 
           `(~(first y) 
           ~(defunits-chaining 
            (first y) 
            (cons `(~base-unit 1) 
             (partition 2 units)) 
            nil)))) 
         '() 
         (partition 2 units)))))) 

這將避免在第一時間創建列表的列表,減少捲起的值都傳遞到一個結果。

更重要的是,(感謝amalloy爲察覺這一個),還有mapcat

(defmacro defunits [quantity base-unit & units] 
    `(defmacro ~(symbol (str "unit-of-" quantity)) 
    [valu# un#] 
    `(* ~valu# 
     ~(case un# 
      ~base-unit 1 
      [email protected](mapcat (fn [x] ;; <----- mapcat instead of map is the only change 
         `(~(first x) 
          ~(defunits-chaining 
          (first x) 
          (cons `(~base-unit 1) 
            (partition 2 units)) 
          nil))) 
         (partition 2 units)))))) 

會員名:mapcat有效地做同樣的事情作爲降低版本,但隱式處理CONCAT你。

+0

是的,〜@(flatten(地圖(fn [x] )做得很好,我不確定我錯過了我嘗試過的百件事情。* head smack * – Steve 2013-05-03 01:34:43