簡體   English   中英

fn 並讓里面的 clojure 宏

[英]fn and let inside clojure macro

我遇到了 Clojure 宏的一些限制。 我想知道如何優化以下代碼?

(defmacro ssplit-7-inefficient [x]
  (let [t 7]
    ;;                    Duplicated computation here!
    `(do [(first          (split-with #(not (= '~t %)) '~x))
          (drop 1 (second (split-with #(not (= '~t %)) '~x)))])))

(ssplit-7-inefficient (foo 7 bar baz))
;; Returns: [(foo) (bar baz)]

以下是一些不起作用的方法:

(defmacro ssplit-7-fails [x]
  (let [t 7]
    `(do ((fn [[a b]] [a (drop 1 b)]) (split-with #(not (= '~t %)) '~x)))))

(ssplit-7-fails (foo 7 bar baz))
;; Error: Call to clojure.core/fn did not conform to spec.

(defmacro ssplit-7-fails-again [x]
  (let [t 7]
    `(do
       (let [data (split-with #(not (= '~t %)) '~x)]
         ((fn [[a b]] [a (drop 1 b)]) data)))))

(ssplit-7-fails-again (foo 7 bar baz))
;; Error: Call to clojure.core/let did not conform to spec.

請注意, split-with只會拆分一次。 您可以使用一些解構來獲得您想要的東西:

(defmacro split-by-7 [arg]
  `((fn [[x# [_# & z#]]] [x# z#]) (split-with (complement #{7}) '~arg)))

(split-by-7 (foo 7 bar baz))
=> [(foo) (bar baz)]

在其他用例中, partition-by也很有用:

(defmacro split-by-7 [arg]
  `(->> (partition-by #{7} '~arg)
        (remove #{[7]})))

(split-by-7 (foo 7 bar baz))
=> ((foo) (bar baz))

推理 Clojure 中的宏並不容易——(在我看來, macroexpand-1使代碼疏遠了很多——與 Common Lisp 的macroexpand-1相比……)。

我的方法是先建一個helper function。

(defn %split-7 [x]
  (let [y 7]
    (let [[a b] (split-with #(not= y %) x)]
      [a (drop 1 b)])))

這個 function 使用解構,所以split-with是“有效的”。

它幾乎完全符合宏應該做的事情。 只有那個人必須引用這個論點——這樣它才能起作用。

(%split-7 '(a 7 b c)) 
;;=> [(a) (b c)]

從這一步到宏觀並不難。

插入輔助函數的調用時,宏應該自動引用參數。

(defmacro split-7 [x]
  `(%split-7 '~x))

這樣我們就可以調用:

(split-7 (a 7 b c))
;; => [(a) (b c)]

使用這個技巧,甚至可以將 function 概括為:

(defn %split-by [x y]able like this
  (let [[a b] (split-with #(not= y %) x)]
    [a (drop 1 b)]))

(defmacro split-by [x y]
  `(%split-by '~x ~y))

(split-by (a 7 b c) 7)
;; => [(a) (b c)]

(split-by (a 7 b c 9 d e) 9)
;; => [(a 7 b c) (d e)]

在宏體中使用(輔助)函數 - 甚至其他宏 - 或遞歸函數或遞歸宏 - 調用其他宏的宏 - 顯示了 lisp 宏的強大程度。 因為它表明您可以在制定/定義宏時使用整個 lisp。 大多數語言的宏通常無法做到的事情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM