How do you get the keys from a transient map in clojure? - clojure

I am trying to get the seq of all of the keys in a transient map:
(keys {3 4 5 6 7 8}) gives (3 5 7)
as I expect but:
(keys (transient {3 4 5 6 7 8}))
gives
#<CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.PersistentArrayMap$TransientArrayMap (NO_SOURCE_FILE:346)>
on the same note, how do I process using a transient map?
(map identity {3 4 5 6})
gives
([3 4] [5 6])
but
(map identity (transient {3 4 5 6}))
gives
#<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.PersistentArrayMap$TransientArrayMap>

This isn't really possible because of the interaction between laziness and mutability. (keys m) always returns a lazy sequence backed by the immutable m data structure, computing elements as necessary. But if m is a transient, it might change at any time, which would ruin the lazy key-sequence. You're really meant to not do anything too fancy with transients; and because it's so cheap to create transient or persistent versions of a data structure, it's not too onerous to go back and forth a few times if you really want to do something fancy.

I don't think there's a way to work on transients like you work on persistent structures. You need to create a persistent structure from your transients with persistence!
user> (map identity (persistent! (transient {3 4 5 6})))
([3 4] [5 6])
You can learn more about transients from Clojure docs.

Related

Undo overwrite of built-in function in Clojure

I managed to redefine the built-in function vector by mistake.
More specifically, this is what I did:
(def vector [1 2 3 4 5 6])
And this is what I intended to do:
(def my-vector (vector 1 2 3 4 5 6))
Is there some way to "undo" that mistake, without restarting the REPL?
I.e., reverting vector back to its default definition.
(def vector #'clojure.core/vector)

Clojurescript - map from list of subvecs

I'm trying to create a map from a list of 2-element Subvecs.
This works fine in Clojure:
(into {} (list (subvec [1 2 3] 1)))
>> {2 3}
But fails in ClojureScript, with the following error:
No protocol method IMapEntry.-key defined for type number: 2
Replacing (subvec [1 2 3] 1) with [2 3] makes it work in both languages.
I'm new to ClojureScript, and can't find where this behaviour is documented. Is this a bug? And how would you suggest going around it efficiently?
Thanks!
I think it's an omission. Subvectors should be indistinguishable from ordinary vectors, and therefore Subvec should have an implementation of IMapEntry added to it, like the one in PersistentVector.

Clojure : is there a more idiomatic way to work on nested vectors?

I want to cap samples that I generate from Poisson's distributions.
Original data is like
[[2 12] [3 14]] (samples)
Here, [2 12] correspond to samples of distributions [P1 P2], [3 14] as well.
I want to cap P1 and P2 with max values, let's say for instance
[4 12] (max-values)
With these parameters, I want so to output (I want to keep vectors)
[[2 12] [3 12]]
This is pretty easy but I do not know if my way is very idiomatic :
(defn cap-poisson-samples
"Cap poisson samples to meet the expactations
if required"
[data max-values]
(mapv
(fn [x]
(mapv (fn [u v] (if (> u v) v u)) x max-values))
data))
Someone told me that it's better to avoid nested map in the past but I do not know if it's true.
I know prewalk exists but it's not possible to pass two inputs (like my second mapv).
i could also use a for but it's heavier.
Generally speaking, I'm quite lost when I work with two vectors I have to process on same indexes. I searched in clojure;core but I did not find any.
So I generally use for or mapv depending on the fact that the index is important or not.
Thanks

What's the one-level sequence flattening function in Clojure?

What's the one-level sequence flattening function in Clojure? I am using apply concat for now, but I wonder if there is a built-in function for that, either in standard library or clojure-contrib.
My general first choice is apply concat. Also, don't overlook (for [subcoll coll, item subcoll] item) -- depending on the broader context, this may result in clearer code.
There's no standard function. apply concat is a good solution in many cases. Or you can equivalently use mapcat seq.
The problem with apply concat is that it fails when there is anything other than a collection/sequential is at the first level:
(apply concat [1 [2 3] [4 [5]]])
=> IllegalArgumentException Don't know how to create ISeq from: java.lang.Long...
Hence you may want to do something like:
(defn flatten-one-level [coll]
(mapcat #(if (sequential? %) % [%]) coll))
(flatten-one-level [1 [2 3] [4 [5]]])
=> (1 2 3 4 [5])
As a more general point, the lack of a built-in function should not usually stop you from defining your own :-)
i use apply concat too - i don't think there's anything else in the core.
flatten is multiple levels (and is defined via a tree-walk, not in terms of repeated single level expansion)
see also Clojure: Semi-Flattening a nested Sequence which has a flatten-1 from clojure mvc (and which is much more complex than i expected).
update to clarify laziness:
user=> (take 3 (apply concat (for [i (range 1e6)] (do (print i) [i]))))
012345678910111213141516171819202122232425262728293031(0 1 2)
you can see that it evaluates the argument 32 times - this is chunking for efficiency, and is otherwise lazy (it doesn't evaluate the whole list). for a discussion of chunking see comments at end of http://isti.bitbucket.org/2012/04/01/pipes-clojure-choco-1.html

Inconsistency with Clojure's sequences?

Clojure:
1:13 user=> (first (conj '(1 2 3) 4))
4
1:14 user=> (first (conj [1 2 3] 4))
1
; . . .
1:17 user=> (first (conj (seq [1 2 3]) 4))
4
I understand what is going on, but should this work differently?
Documentation for conj (from clojure.org):
conj[oin]. Returns a new collection with the xs
'added'. (conj nil item) returns (item). The 'addition' may
happen at different 'places' depending on the concrete type.
It's more efficient to "add" elements to the end of a vector, while it's more efficient to do so at the beginning of lists. conj uses whatever is the most efficient for the data structure you give it.
In the examples you give, '(1 2 3) and (seq [1 2 3]) both implement ISeq (see documentation for seq?), while [1 2 3] doesn't.
Clojure's conj ultimately calls the cons method (not to be confused with the cons function - this method is internal clojure code) on the underlying data structure; for vectors (PersistentVector), cons adds elements to the end, while for lists they're added to the front (the cons method for PersistentLists returns a new list with the new element as its head, and the existing list as its tail).
If you look at Clojure Data Structures
you'll see that conj works differently with lists and vectors.
conj puts the added item at the front of the list and at the end of a vector.
I also suggest looking at Clojure API conj
which has some nice examples. ClojureDocs overall has some very nice examples for most Clojure commands.