2

我试图想出一些方法让 clojure 哈希图中的值相互引用。从概念上讲,是这样的:

(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

但这并没有真正获得任何好处,因为我仍然必须知道将哪个放入函数中ab另一种尝试:

(def m {:a 1 :b 5 :c (fn [m] (+ (:a m) (:b m)))})
((:c m) m) ;=> 6

好一点,因为我现在已经将函数内化为一个地图,虽然不是专门的这个地图。我可能会尝试用这样的东西来解决这个问题

(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

我认为这是我能做的最好的事情。我错过了更聪明的东西吗?

4

4 回答 4

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}
于 2013-04-13T10:39:20.960 回答
1

正如我在上面的评论中已经说过的,您可以使用 let 形式:

(def m 
  (let [a 1 b 5] 
    {:a a :b b :c (+ a b)}))

如果您使用仅在 m 定义中已知的值,这应该没问题。否则,您最好使用@Michiel 所示的函数参数。

PS顺便说一句,您可以自由使用def中的所有内容,您通常在clojure中使用。此外,有时您可以在其他一些形式中自由使用糖化形式的 let(尽管这个 let 使用与通常的 let 形式不同的机制):

(for [x (...) xs]
     :let [y (+ x 1)]
     ; ...
于 2013-04-12T19:34:18.280 回答
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
于 2013-04-12T19:23:59.873 回答
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}
于 2019-06-21T01:03:27.690 回答