Clojure 宏允许在没有 let 绑定的情况下编写代码

Fai*_*lde 1 macros clojure

我正在尝试编写一个允许我执行以下操作的宏

(without-nesting
  (:= x 1)
  (:= y 2)
  (:= z 3)
  (db-call x y z)
  (:= p 33)
  (db-call x y z p))
Run Code Online (Sandbox Code Playgroud)

变成

(let [x 1 
      y 2 
      z 3]
  (db-call x y z)
  (let [p 33]
    (db-call x y z p)))  
Run Code Online (Sandbox Code Playgroud)

到目前为止,我的实现如下

(defn assignment?
  [[s _]]
  (= s ':=))

(defmacro without-nesting
  [& body]
  (let [[bindings xs] (split-with assignment? body)
        [non-bindings remaining] (split-with (complement assignment?) xs)]
    `(let [~@(mapcat rest bindings)]
       ~@non-bindings
       ~(when (seq remaining)
          `(without-nesting ~@remaining)))))
Run Code Online (Sandbox Code Playgroud)

我有问题什么时候remaining会是空的。在我当前的实现中nil,放置了一个防止最后一个表单non-bindings返回它的值。我不知道如何继续使用递归宏。有人可以帮忙吗

更新:

所以我能够摆脱 nil 但我只想知道是否有更好的方法来处理这个

(defmacro without-nesting
  [& body]
  (let [[bindings xs] (split-with assignment? body)
        [non-bindings remaining] (split-with (complement assignment?) xs)]
    `(let [~@(mapcat rest bindings)]
       ~@non-bindings
       ~@(if (seq remaining)
           [`(without-nesting ~@remaining)]
           []))))  
Run Code Online (Sandbox Code Playgroud)

这也是编写代码的好方法吗?或者你看到任何警告?对我来说,与嵌套相比,它看起来更线性let

我确实看到 ppl 可能会滥用这一点。在 的情况下let,如果嵌套变得太多,那么这是重构代码的提示。这可能会隐藏

A. *_*ebb 6

只需使用let. 它已经是递归的。要将函数调用合并到您只关心副作用的地方,约定是绑定到下划线。

(let [x 1 
      y 2 
      z 3
      _ (db-call x y z)
      p 33
      _ (db-call x y z p)])
Run Code Online (Sandbox Code Playgroud)