我可以在def中获得这个变量吗?

时间:2010-02-19 20:19:46

标签: clojure

有没有办法模仿像this这样的(def foo {:two 2 :three (inc (:two this))})变量?更好的是(def foo {:two 2 :three (inc ::two)})。我被告知有一个库正是这样做的,但我找不到任何类似的东西。

谢谢!

2 个答案:

答案 0 :(得分:4)

如果您想要某个临时名称,那就是let的用途。

(def foo (let [x {:two 2}]
           (assoc x :three (inc (:two x)))))

我不知道任何你想做的事情的图书馆。每隔一段时间,有人会建议一个“广义箭头”,如->,但有一个神奇的符号,你可以坚持中间表达式,这将被其他东西取代。例如,请参阅herehere。但是这个想法往往被击落,因为它更复杂,而且收效甚微。 let是你的朋友。见Rich的例子:

(let [x []
      x (conj x 1)
      x (into x [2 3])
      x (map inc x)]
...) 

答案 1 :(得分:2)

更新:重新排列并重新设计。build-map和(草图)-m>宏已添加。)

您可以将此特定示例编写为

(def foo (zipmap [:two :three] (iterate inc 2)))

此时发生的最简单的一般解决方案是

user> (-> {} (assoc :two 2) (#(assoc % :three (inc (:two %)))))
{:three 3, :two 2}

它实际上非常灵活,但它确实需要您反复写出assoc

要启用与问题文本类似的语法,您可以使用以下内容:

(defn build-map* [& kvs]
  (reduce (fn [m [k v]]
            (assoc m k (v m)))
          {}
          kvs))

(defmacro build-map [& raw-kvs]
  (assert (even? (count raw-kvs)))
  (let [kvs (map (fn [[k v]] [k `(fn [m#] (let [~'this m#] ~v))])
                 (partition 2 raw-kvs))]
    `(build-map* ~@kvs)))

user> (build-map :two 2 :three (inc (:two this)))
{:three 3, :two 2}

您可以轻松地将其更改为使用用户提供的符号而不是硬编码的this。或者您可以切换到%,这只是匿名函数文字之外的常规符号。也许添加一个显式的初始map参数,称之为-m>(对于地图线程),你可以做

(-m> {} :two 2 :three (inc (:two %)))

得到相同的结果。


另一种时髦的方式(主要是为了好玩):

;;; from Alex Osborne's debug-repl,
;;; see http://gist.github.com/252421
;;; now changed to use &env
(defmacro local-bindings
  "Produces a map of the names of local bindings to their values."
  []
  (let [symbols (map key &env)]
    (zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))

(let [two 2
      three (inc two)]
  (into {} (map (fn [[k v]] [(keyword k) v]) (local-bindings))))
{:two 2, :three 3}

请注意,这也将捕获任何外部let表单引入的绑定...

相关问题