我正在尝试用一些方法让clojure hashmap中的值互相引用。从概念上讲,这样的事情:
(def m {:a 1 :b 5 :c (+ (:a m) (:b m))} ;Implies (= (:c m) 6)
这当然不起作用,因为我循环引用m
。我可以做点像
(def m {:a 1 :b 5 :c (fn [a b] (+ a b))})
((:c m) (:a m) (:b m)) ;=> 6
但这并没有真正获得任何好处,因为我仍然需要知道将哪个a
和b
放入函数中。另一种尝试:
(def m {:a 1 :b 5 :c (fn [m] (+ (:a m) (:b m)))})
((:c m) m) ;=> 6
这有点好,因为我现在已将该功能内化到 a 地图,但并非专门此地图。我可能会尝试用这样的东西解决这个问题
(defn new-get [k m]
(let [v-or-fn (get m k)]
(if (fn? v-or-fn) (v-or-fn m) v-or-fn)))
(def m {:a 1 :b 5 :c (fn [m] (+ (:a m) (:b m)))})
(new-get :a m) ;=> 1
(new-get :b m) ;=> 5
(new-get :c m) ;=> 6
我认为这是我能做的最好的事情。我错过了一些更聪明的东西吗?
答案 0 :(得分:2)
无法帮助自己编写一个宏:
(defmacro defmap [name m]
(let [mm (into [] (map (fn [[k v]] `[~k (fn [~name] ~v)]) m))]
`(def ~name
(loop [result# {} mp# (seq ~mm)]
(if (seq mp#)
(let [[k# v#] (first mp#)]
(recur (assoc result# k# (v# result#)) (rest mp#)))
result#)))))
(defmap m [[:a 1]
[:b 5]
[:c (+ (:a m) (:b m))]])
;; m is {:a 1 :b 5 :c 6}
答案 1 :(得分:1)
正如我在上面的评论中所说,你可以使用let form:
(def m
(let [a 1 b 5]
{:a a :b b :c (+ a b)}))
如果您使用的只是m定义中的值,那么这应该没问题。否则,最好将函数参数用作@Michiel shown。
P.S。顺便说一句,你可以自由地使用def里面的所有内容,你通常会在clojure中使用它。此外,有时你可以自由地在其他形式的内部使用含糖形式(虽然这会使用与通常不同的机制形式):
(for [x (...) xs]
:let [y (+ x 1)]
; ...
答案 2 :(得分:0)
由于c
是派生值,因此a
和b
的函数可能更好,通过定义生成此映射的函数:
(defn my-map-fn [a b]
{:a a :b b :c (+ a b)})
(def my-map (my-map-fn 1 2))
(:c my-map) ;;=> 3
答案 3 :(得分:0)
这是我的看法:
(defmacro let-map [& bindings]
(let [symbol-keys (->> bindings (partition 2) (map first))]
`(let [~@bindings]
(into {} ~(mapv (fn [k] [(keyword k) k]) symbol-keys)))))
;; if you view it as similar to let, when it's more complicated:
(let-map
a 1
b 5
c (+ a b)) ; => {:a 1, :b 5, :c 6}
;; if you see it as an augmented hash-map, when it's simple enough:
(let-map a 1, b 5, c (+ a b)) ; => {:a 1, :b 5, :c 6}