2

我发现自己正在编写一个函数,该函数传递封闭的 let 表达式中绑定的每个变量。这个场景的简化形式,在伪代码中,是

(let [a 1 b 2 c 3 d 4 e 5 f 6]
  (do-something a b c d)
  (f a b c d e f g)
  (compute-another-thing a c))

而我想写一些更像...

(let env-name [a 1 b 2 c 3 d 4 e 5 f 6]
   (do-something (take 4 env-name))
   (f env-name)
   (compute-another-thing a c))

也就是说,clojure 核心(或外部可靠的东西)是否提供了一个let变体,将所有绑定收集到一个带有名称的集合中?

4

2 回答 2

2

解构将使您成为其中的一部分:

(let [[a b c d e f :as env-name] [1 2 3 4 5 6]]
   (apply do-something (take 4 env-name))
   (apply f env-name)
   (compute-another-thing a c))

请注意,使用 env-name 调用函数时必须使用apply 。

对于更优雅的东西(比如你提出的形式),我认为你必须编写自己的宏。

更新:这是一个可以处理简单情况的(非常简单的测试)宏:

(defmacro let-as 
  [as-bind-name bindings & body]
  (let [lhs-bindings (map first (partition 2 bindings))]
    `(let ~bindings
       (let [~as-bind-name [~@lhs-bindings]]
             ~@body))))

只要您的普通 let 绑定的左侧是简单符号(即不使用解构),这应该可以满足您的要求。

一些 REPL 交互来说明:

core> (let-as env-name [a 1 b 2 c 3 d 4 e 5 f 6] (print env-name) (apply + env-name))
[1 2 3 4 5 6]
21

一些解构是表现良好的:

core> (let-as env-name [[a b] [1 2] [c d] [3 4]] (print env-name) (map first env-name))
[[1 2] [3 4]]
(1 3)

其他解构不是:

core> (let-as env-name [{:keys [a b]} {:a 1 :b 2}] (print env-name) (+ a b))
[{:keys [1 2]}]
3

这可以补救,但宏必须更复杂

于 2014-08-25T19:31:57.903 回答
1

解构可能是这里最好的解决方案,但出于兴趣,Fogus 在他的evalive 项目中有一个 lexical-context 宏。它看起来像这样:

(defmacro lexical-context
  []
  (let [symbols (keys &env)]
    (zipmap (map (fn [sym] `(quote ~sym))
                 symbols)
            symbols)))

(let [a 1]
  (let [b 2]
    (lexical-context))) ; => {a 1, b 2}

它使用宏可用的特殊&env变量,因此您甚至不需要使用不同形式的let.

于 2014-08-26T08:29:35.513 回答