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

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
)

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 (which could 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.

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

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.

Related

Return value from middle of `do` (Clojure)

I have a list of I/O functions to run in a game, but need to collect the value from a function somewhere in the middle of the do
(defn setup-steps [game-state]
(do (io/clear-screen)
(print-welcome-message)
(initial-setup) ;; value to be collected
(io/clear-screen)
(io/print-board game-state)))
Is there a smart way to return the value from somewhere in the middle of a do?
Down the line, I am using the return value of setup-steps to update an atom, like so:
(defn game-loop [game]
(while (:game-in-progress? #game)
;; Here is where I am using the results
(->> (s-io/setup-steps #game) (state/updater game))
(while (:game-in-progress? #game)
(->> (m-io/turn-steps #game) (state/updater game)))
(->> (eg-io/end-game-steps #game) (state/updater game)))
(eg-io/exit-game))
Where
(defn updater
"Updates the game state.
This is the only place where the game atom is modified."
[game update-params]
(swap! game merge update-params))
I'm sure you could write a macro for this, but I don't really understand macros yet.
And maybe I am thinking about this the wrong way... Is it more idiomatic to dow the swap!ing inside setup-steps?
Any reason you can't assign the result in a let and return it at the end of the function?
(defn setup-steps [game-state]
(io/clear-screen)
(print-welcome-message)
(let [v (initial-setup)] ;; value to be collected
(io/clear-screen)
(io/print-board game-state)
v))
EDIT: Got rid of the redundant do that Ryan Asensio mentioned.
Fundamentally the only way to do this is with let, as clartaq shows. But if you find this distasteful there are a number of ways you could wrap this up in a macro to make it prettier to look at and also more clear about what you're doing. Here is the simplest one, which I like to call returning:
(defmacro returning [x & more]
`(let [x# ~x]
(do ~#more)
x#))
(defn foo []
(x)
(y)
(returning (z)
(a)
(b)))
The do in returning is of course superfluous, but I think it's still useful to emphasize that more is evaluated only for side effects.

Conditional "assignment" in functional programming

I am programming something that doesn't have side-effects, but my code is not very readable.
Consider the following piece of code:
(let [csv_data (if header_row (cons header_row data_rows) data_rows)]
)
I'm trying to use csv_data in a block of code. What is a clean way of conditioning on the presence of a header_row? I've looked at if-let, but couldn't see how that could help here.
I have run into similar situations with functional for-loops as well where I'm binding the result to a local variable, and the code looks like a pile of expressions.
Do I really have to create a separate helper function in so many cases?
What am I missing here?
Use the cond->> macro
(let [csv_data (cond->> data_rows
header_row (cons header-row)]
)
It works like the regular ->> macro, but before each threading form a test expression has to be placed that determines whether the threading form will be used.
There is also cond->. Read more about threading macros here: Official threading macros guide
First, don't use underscore, prefer dashes.
Second, there is nothing wrong with a little helper function; after all, this seems to be a requirement for handling your particular data format.
Third, if you can change your data so that you can skip those decisions and have a uniform representation for all corner cases, this is even better. A header row contains a different kind of data (column names?), so you might prefer to keep them separate:
(let [csv {:header header :rows rows}]
...)
Or maybe at some point you could have "headers" and "rows" be of the same type: sequences of rows. Then you can concat them directly.
The ensure-x idiom is a very common way to normalize your data:
(defn ensure-list [data]
(and data (list data)))
For example:
user=> (ensure-list "something")
("something")
user=> (ensure-list ())
(())
user=> (ensure-list nil)
nil
And thus:
(let [csv (concat (ensure-list header) rows)]
...)
i would propose an utility macro. Something like this:
(defmacro update-when [check val-to-update f & params]
`(if-let [x# ~check]
(~f x# ~val-to-update ~#params)
~val-to-update))
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> (:header :data1 :data2)
user> (let [header-row nil
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> [:data1 :data2]
it is quite universal, and lets you fulfill more complex tasks then just simple consing. Like for example you want to reverse some coll if check is trueish, and concat another list...
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows
(fn [h d & params] (apply concat (reverse d) params))
[1 2 3] ['a 'b 'c])]
csv-data))
;;=> (:data2 :data1 1 2 3 a b c)
update
as noticed by #amalloy , this macro should be a function:
(defn update-when [check val-to-update f & params]
(if check
(apply f check val-to-update params)
val-to-update))
After thinking about the "cost" of a one-line helper function in the namespace I've came up with a local function instead:
(let [merge_header_fn (fn [header_row data_rows]
(if header_row
(cons header_row data_rows)
data_rows))
csv_data (merge_header_fn header_row data_rows) ]
...
<use csv_data>
...
)
Unless someone can suggest a more elegant way of handling this, I will keep this as an answer.

use 'for' inside 'let' return a list of hash-map

Sorry for the bad title 'cause I don't know how to describe in 10 words. Here's the detail:
I'd like to loop a file in format like:
a:1 b:2...
I want to loop each line, collect all 'k:v' into a hash-map.
{ a 1, b 2...}
I initialize a hash-map in a 'let' form, then loop all lines with 'for' inside let form.
In each loop step, I use 'assoc' to update the original hash-map.
(let [myhash {}]
(for [line #{"A:1 B:2" "C:3 D:4"}
:let [pairs (clojure.string/split line #"\s")]]
(for [[k v] (map #(clojure.string/split %1 #":") pairs)]
(assoc myhash k (Float. v)))))
But in the end I got a lazy-seq of hash-map, like this:
{ {a 1, b 2...} {x 98 y 99 z 100 ...} }
I know how to 'merge' the result now, but still don't understand why 'for' inside 'let' return
a list of result.
What I'm confused is: does the 'myhash' in the inner 'for' refers to the 'myhash' declared in the 'let' form every time? If I do want a list of hash-map like the output, is this the idiomatic way in Clojure ?
Clojure "for" is a list comprehension, so it creates list. It is NOT a for loop.
Also, you seem to be trying to modify the myhash, but Clojure's datastructures are immutable.
The way I would approach the problem is to try to create a list of pair like (["a" 1] ["b" 2] ..) and the use the (into {} the-list-of-pairs)
If the file format is really as simple as you're describing, then something much more simple should suffice:
(apply hash-map (re-seq #"\w+" (slurp "your-file.txt")))
I think it's more readable if you use the ->> threading macro:
(->> "your-file.txt" slurp (re-seq #"\w+") (apply hash-map))
The slurp function reads an entire file into a string. The re-seq function will just return a sequence of all the words in your file (basically the same as splitting on spaces and colons in this case). Now you have a sequence of alternating key-value pairs, which is exactly what hash-map expects...
I know this doesn't really answer your question, but you did ask about more idiomatic solutions.
I think #dAni is right, and you're confused about some fundamental concepts of Clojure (e.g. the immutable collections). I'd recommend working through some of the exercises on 4Clojure as a fun way to get more familiar with the language. Each time you solve a problem, you can compare your own solution to others' solutions and see other (possibly more idomatic) ways to solve the problem.
Sorry, I didn't read your code very thorougly last night when I was posting my answer. I just realized you actually convert the values to Floats. Here are a few options.
1) partition the sequence of inputs into key/val pairs so that you can map over it. Since you now how a sequence of pairs, you can use into to add them all to a map.
(->> "kvs.txt" slurp (re-seq #"\w") (partition 2)
(map (fn [[k v]] [k (Float. v)])) (into {}))
2) Declare an auxiliary map-values function for maps and use that on the result:
(defn map-values [m f]
(into {} (for [[k v] m] [k (f v)])))
(->> "your-file.txt" slurp (re-seq #"\w+")
(apply hash-map) (map-values #(Float. %)))
3) If you don't mind having symbol keys instead of strings, you can safely use the Clojure reader to convert all your keys and values.
(->> "your-file.txt" slurp (re-seq #"\w+")
(map read-string) (apply hash-map))
Note that this is a safe use of read-string because our call to re-seq would filter out any hazardous input. However, this will give you longs instead of floats since numbers like 1 are long integers in Clojure
Does the myhash in the inner for refer to the myhash declared in the let form every time?
Yes.
The let binds myhash to {}, and it is never rebound. myhash is always {}.
assoc returns a modified map, but does not alter myhash.
So the code can be reduced to
(for [line ["A:1 B:2" "C:3 D:4"]
:let [pairs (clojure.string/split line #"\s")]]
(for [[k v] (map #(clojure.string/split %1 #":") pairs)]
(assoc {} k (Float. v))))
... which produces the same result:
(({"A" 1.0} {"B" 2.0}) ({"C" 3.0} {"D" 4.0}))
If I do want a list of hash-map like the output, is this the idiomatic way in Clojure?
No.
See #DaoWen's answer.

Why in this example calling (f arg) and calling the body of f explicitly yields different results?

First, I have no experience with CS and Clojure is my first language, so pardon if the following problem has a solution, that is immediately apparent for a programmer.
The summary of the question is as follows: one needs to create atoms at will with unknown yet symbols at unknown times. My approach revolves around a) storing temporarily the names of the atoms as strings in an atom itself; b) changing those strings to symbols with a function; c) using a function to add and create new atoms. The problem pertains to step "c": calling the function does not create new atoms, but using its body does create them.
All steps taken in the REPL are below (comments follow code blocks):
user=> (def atom-pool
#_=> (atom ["a1" "a2"]))
#'user/atom-pool
'atom-pool is the atom that stores intermediate to-be atoms as strings.
user=> (defn atom-symbols []
#_=> (mapv symbol (deref atom-pool)))
#'user/atom-symbols
user=> (defmacro populate-atoms []
#_=> (let [qs (vec (remove #(resolve %) (atom-symbols)))]
#_=> `(do ~#(for [s qs]
#_=> `(def ~s (atom #{}))))))
#'user/populate-atoms
'populate-atoms is the macro, that defines those atoms. Note, the purpose of (remove #(resolve %) (atom-symbols)) is to create only yet non-existing atoms. 'atom-symbols reads 'atom-pool and turns its content to symbols.
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(nil nil nil)
Here it is confirmed that there are no 'a1', 'a2', 'a-new' atoms as of yet.
user=> (defn new-atom [a]
#_=> (do
#_=> (swap! atom-pool conj a)
#_=> (populate-atoms)))
#'user/new-atom
'new-atom is the function, that first adds new to-be atom as string to `atom-pool. Then 'populate-atoms creates all the atoms from 'atom-symbols function.
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)
Here we see that 'a1 'a2 were created as clojure.lang.Var$Unbound just by defining a function, why?
user=> (new-atom "a-new")
#'user/a2
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)
Calling (new-atom "a-new") did not create the 'a-new atom!
user=> (do
#_=> (swap! atom-pool conj "a-new")
#_=> (populate-atoms))
#'user/a-new
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 #'user/a-new)
user=>
Here we see that resorting explicitly to 'new-atom's body did create the 'a-new atom. 'a-new is a type of clojure.lang.Atom, but 'a1 and 'a2 were skipped due to already being present in the namespace as clojure.lang.Var$Unbound.
Appreciate any help how to make it work!
EDIT: Note, this is an example. In my project the 'atom-pool is actually a collection of maps (atom with maps). Those maps have keys {:name val}. If a new map is added, then I create a corresponding atom for this map by parsing its :name key.
"The summary of the question is as follows: one needs to create atoms at will with unknown yet symbols at unknown times. "
This sounds like a solution looking for a problem. I would generally suggest you try another way of achieving whatever the actual functionality is without generating vars at runtime, but if you must, you should use intern and leave out the macro stuff.
You cannot solve this with macros since macros are expanded at compile time, meaning that in
(defn new-atom [a]
(do
(swap! atom-pool conj a)
(populate-atoms)))
populate-atoms is expanded only once; when the (defn new-atom ...) form is compiled, but you're attempting to change its expansion when new-atom is called (which necessarily happens later).
#JoostDiepenmaat is right about why populate-atoms is not behaving as expected. You simply cannot do this using macros, and it is generally best to avoid generating vars at runtime. A better solution would be to define your atom-pool as a map of keywords to atoms:
(def atom-pool
(atom {:a1 (atom #{}) :a2 (atom #{})}))
Then you don't need atom-symbols or populate-atoms because you're not dealing with vars at compile-time, but typical data structures at run-time. Your new-atom function could look like this:
(defn new-atom [kw]
(swap! atom-pool assoc kw (atom #{})))
EDIT: If you don't want your new-atom function to override existing atoms which might contain actual data instead of just #{}, you can check first to see if the atom exists in the atom-pool:
(defn new-atom [kw]
(when-not (kw #atom-pool)
(swap! atom-pool assoc kw (atom #{}))))
I've already submitted one answer to this question, and I think that that answer is better, but here is a radically different approach based on eval:
(def atom-pool (atom ["a1" "a2"]))
(defn new-atom! [name]
(load-string (format "(def %s (atom #{}))" name)))
(defn populate-atoms! []
(doseq [x atom-pool]
(new-atom x)))
format builds up a string where %s is substituted with the name you're passing in. load-string reads the resulting string (def "name" (atom #{})) in as a data structure and evals it (this is equivalent to (eval (read-string "(def ...)
Of course, then we're stuck with the problem of only defining atoms that don't already exist. We could change the our new-atom! function to make it so that we only create an atom if it doesn't already exist:
(defn new-atom! [name]
(when-not (resolve (symbol name))
(load-string (format "(def %s (atom #{}))" name name))))
The Clojure community seems to be against using eval in most cases, as it is usually not needed (macros or functions will do what you want in 99% of cases*), and eval can be potentially unsafe, especially if user input is involved -- see Brian Carper's answer to this question.
*After attempting to solve this particular problem using macros, I came to the conclusion that it either cannot be done without relying on eval, or my macro-writing skills just aren't good enough to get the job done with a macro!
At any rate, I still think my other answer is a better solution here -- generally when you're getting way down into the nuts & bolts of writing macros or using eval, there is probably a simpler approach that doesn't involve metaprogramming.

Removing items from a map based on the contents of another map

Still working through Programming Collective Intelligence and using Clojure to write the code. I've got it working, but some parts are really ugly, so I thought I'd ask some of the experts around here to help clean it up.
Let's suppose I have a map that looks like this (bound to "recs"):
{"Superman Returns" 3.902419556891574, "Lady in the Water" 2.8325499182641614,
"Snakes on a Plane" 3.7059737842895792, "The Night Listener" 3.3477895267131017,
"You, Me and Dupree" 2.651006036204627, "Just My Luck" 2.5309807037655645}
and I want to remove those items with keys that are also in the map (bound to "mymovies"):
{"Snakes on a Plane" 4.5, "You, Me and Dupree" 1.0, "Superman Returns" 4.0}
so that I get the map:
{"Lady in the Water" 2.8325499182641614, "The Night Listener" 3.3477895267131017,
"Just My Luck" 2.5309807037655645}
the code that I managed to get to do this looks like:
(apply merge (map #(hash-map (first %) (second %))
(remove #(contains? mymovies (first %))
recs)))
That seems pretty ugly to me. It doesn't seem like it should be necessary to create a map from the value I get back from "remove". Is there a cleaner way to do this?
UPDATE: Joost's answer below sparked another idea. If I turn the keys of the two maps into sets I can use select-keys like this:
(select-keys recs (difference (set (keys recs))
(set (keys mymovies))))
Joost, thanks for turning me on to select-keys. I didn't know about that function before. Now to go rewrite several other sections with this new found knowledge!
(apply dissoc recs (keys mymovies))
The following first builds a seq of keys to keep, then extracts the "submap" for those keys from recs using select-keys. It also takes advantage of the fact that sets are predicates.
(select-keys recs (remove (apply hash-set (keys mymovies)) (keys recs)))
I think ponzao's answer is best for this case, but I wouldn't have thought to apply dissoc. Here are the two solutions I might have come up with: hopefully looking over them will help with similar future problems.
Note that the second solution will fail if your mymovies map contains nil or false values.
(into {}
(for [[k v] recs
:when (not (contains? mymovies k))]
[k v]))
(into {}
(remove (comp mymovies key) recs))