Как ссылаться на ключ в карте, в которой он существует? - PullRequest
0 голосов
/ 21 марта 2020

У меня есть карта примерно такая:

{:a "some" :b (str :a " stuff")}

Я пытаюсь сделать так, чтобы значение: b карты было «некоторыми вещами», но приведенный выше пример не работает , Я попытался обернуть его в def;

(def foo {:a "some" :b (str (:a foo) " stuff")})

Но это тоже не работает. Как мне сделать эту работу?

Ответы [ 3 ]

2 голосов
/ 21 марта 2020

Вы должны использовать let здесь (либо определить карту, либо значение a). Вы не можете получить доступ к карте, пока она строится.

0 голосов
/ 23 марта 2020

также, вы можете использовать макросы для этого случая (для развлечения и образования). Как вы можете создать простой макрос anaphori c, чтобы сделать трюк:

(defmacro make-map-ana [& keyvals]
  `(-> {}
       ~@(map (fn [[k v]] `((fn [~'it] (assoc ~'it ~k ~v))))
              (partition-all 2 keyvals))))

, как вы его используете:

user> (make-map-ana :a "some"
                    :b (str (:a it) " stuff"))
;;=> {:a "some", :b "some stuff"}

, который расширяется до кода замыкания следующим образом:

(-> {}
    ((fn [it] (assoc it :a "some")))
    ((fn [it] (assoc it :b (str (:a it) " stuff")))))

, поэтому он становится отражающим, передавая контекст (а именно it) вниз по течению.

(make-map-ana :a :fun
              :b it
              :c it)
;;=> {:a :fun, :b {:a :fun}, :c {:a :fun, :b {:a :fun}}}

еще один способ написать этот макрос - использовать переопределение в let bindings:

(defmacro make-map-ana [& keyvals]
  `(let [~'it {}
         ~@(mapcat (fn [[k v]] ['it `(assoc ~'it ~k ~v)])
                   (partition-all 2 keyvals))]
     ~'it))

теперь (make-map-ana :a "some" :b (str (:a it) " stuff")) расширился бы до следующего:

(let [it {}
      it (assoc it :a "some")
      it (assoc it :b (str (:a it) " stuff"))]
  it)

, также получая нужный вам результат

0 голосов
/ 21 марта 2020

Ну, как сказал cfrick, я не думаю, что вы можете получить доступ к карте во время ее построения, как вы написали во втором примере.

Вы можете создать простую функцию, подобную приведенной ниже.

(defn merge-a-b [map] (assoc map :b (str (:a map) (:b map))))

Это также полезно, если вы заканчиваете вектором этих карт, например:

(def x2 [{:a "some", :b " stuff"} {:a "some-other", :b " stuff"}])

Вы можете просто использовать встроенную карту в Clojure и запускать ее так:

(map merge-a-b x2)
({:a "some", :b "some:a stuff"} {:a "some-other", :b "some-other stuff"})
...