In Clojure, how to destructure all the keys of a map?

久未见 提交于 2019-12-18 07:52:08

问题


In clojure, it is possible to destructure some keys of a map like this:

(let [{:keys [cpp js]} {:cpp 88 :js 90}] 
   (println js); 90
   (println cpp); 88
 )

Is there a way to destructure all the keys of a map?

Maybe something like:

(let [{:all-the-keys} {:cpp 88 :js 90}] 
   (println js); 90
   (println cpp); 88
 )

回答1:


Not really, and it wouldn't be a good idea. Imagine:

(let [{:all-the-keys} m]
  (foo bar))

Are foo and bar globals? Locals? Keys you should extract from m? What should this code do if m sometimes contains a foo key, and foo is also a global function? Sometimes you call the global, and sometimes you call the function stored in m?

Ignoring the technical problems (most of which can probably be overcome), it is really a disaster for readability and predictability. Just be explicit about what keys you want to pull out; if you frequently want to pull out the same ten keys, you can write a simple macro like (with-person p body) that simplifies that common case for you.




回答2:


This question is pretty old so you've probably forgotten about it, but it came up on google when I was trying to do the same thing, so if I post my solution it might help someone else out.

(defmacro let-map [vars & forms]
  `(eval (list 'let (->> ~vars keys
                         (map (fn [sym#] [(-> sym# name symbol) (~vars sym#)]))
                         (apply concat) vec)
               '~(conj forms 'do))))

This basically transforms the map {:cpp 88 :js 90} into the binding form [cpp 88 js 90] then constructs a let binding, along with performing some eval-jitsu to make sure that this happens at run time.

(def test-map {:cpp 88 :js 90})
(let-map test-map
  (println js)
  (println cpp))
;=> 90
;=> 88



回答3:


You could write a macro to do this (effectively creating a mini-DSL), but I don't think it is a very good idea for the following reasons:

  • In order to create the right compile-time literals js and cpp, you would need to destructure the map at compile time. This would be quite limiting in terms of what you could do with it (you would have to specify the keys in advance, and it couldn't use in higher order functions, for example)
  • Macros are generally a bad idea when a simpler method would do the job (see below)

I'd recommend just using a simple doseq in your case to loop over the map:

(let [my-map {:cpp 88 :js 90}]
  (doseq [[k v] my-map]
    (println v)))

Note that:

  • You can use destructuring as above to extract both the key k and value v from each map entry
  • I used doseq rather than for because it is non-lazy and it seems in this example that you are using the loop only for the println side effects.
  • If instead you want a lazy sequence of values (88 90) then for would be appropriate.


来源:https://stackoverflow.com/questions/9345056/in-clojure-how-to-destructure-all-the-keys-of-a-map

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!