fn,让内部clojure宏观

发布于 2025-01-21 21:54:41 字数 956 浏览 1 评论 0 原文

我遇到了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.

I'm running into some limitations of Clojure macros. I wonder how to optimize the following code?

(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)]

Here are some approaches that don't work:

(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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦里寻她 2025-01-28 21:54:41

请注意, split-with-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)]

在其他用例中, 也很有用:

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

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

Note that split-with splits only once. You can use some destructuring to get what you want:

(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)]

In other use cases, partition-by can be also useful:

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

(split-by-7 (foo 7 bar baz))
=> ((foo) (bar baz))
二智少女 2025-01-28 21:54:41

关于clojure中的宏的推理并不容易 - (在我的视图中, acroexpand -1 与Common Lisp的 Macroexpand -1 ... )。

我的方式是第一个建立助手功能的方法。

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

此功能使用破坏性,因此 split-with-with-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)]

使用此技巧,甚至将功能概括为:

(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。大多数语言的宏通常无法做的事情。

It is not so easy to reason about macros in Clojure - (in my view macroexpand-1 alienates the code a lot - in contrast to Common Lisp's macroexpand-1 ...).

My way was first to build a helper function.

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

This function uses destructuring so that the split-with is "efficient".

It does nearly exactly what the macro should do. Just that one has to quote
the argument - so that it works.

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

From this step to the macro is not difficult.

The macro should just automatically quote the argument when inserting into the helper function's call.

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

So that we can call:

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

Using this trick, even generalize the function to:

(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)]

The use of (helper) functions in the macro body - and even other macros - or recursive functions or recursive macros - macros which call other macros - shows how powerful lisp macros are. Because it shows that you can use the entirety of lisp when formulating/defining macros. Something what most language's macros usually aren't able to do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文