Picking "Random" elements from a vector - clojure

I am looking to pick out random (that is pseudorandom) elements from a vector. The function would have an input, call it r, that would select the number of elements to be selected. Also, the vector, call it v, would also have to be an input. This is something I have never attempted and don't know where to begin in the process.
Assumptions going into the construction would be r is less than the number of elements in v. Duplicate elements selected from v would also not be an issue. To be clear the elements will be strictly numbers in fact they will be floating points, and I would like to retain that structure upon selection.
I have tried something along the lines of
(take 2 (iterate rand-nth [2 3 4 5 6 7]))
but return the vector and a random element from the list, i.e.
([2 3 4 5 6 7] 7)
Some similar posts from java include: How to choose a random element in this array only once across all declared objects in main?
Take n random elements from a List<E>?

You want repeatedly not iterate here
(repeatedly 2 #(rand-nth [2 3 4 5 6 7]))

A function to return r random elements from vector v in a vector is ...
(defn choose-random [r v]
(mapv v (repeatedly r #(rand-int (count v)))))

If you want to have distinct vector elements in the return vector you could use :
(defn random-take-n [n v]
(let [indices (->> (range (count v))
(shuffle)
(take n))]
(mapv v indexes)))

Related

How to replace occurrence of an element with multiple elements in a sequence?

So, I have a list of items. I want to replace each occurrence of an item based on a criteria with a set of elements in it's place.
Ideally, map can let you convert a list of N elements into another list of N elements but here the length will increase as we insert more elements at index where we had other individual elements replaced by a list of elements in place.
As proposed by #Lee you can do it using mapcat.
E.g:
(mapcat #(if (even? %) [% %] [%]) (range 10))
will result into:
=> (0 0 1 2 2 3 4 4 5 6 6 7 8 8 9)
Rather than map, you can use reduce, starting with an empty accumulator collection [].
(reduce #(conj %1 (dec %2) %2)
[]
[1 3 5 7])
So here, starting with a collection of odd numbers [1 3 5 7], we are adding extra even numbers into the sequence. Output is:
[0 1 2 3 4 5 6 7]

Append to clojure vector from within loop

I have:
(defn keep?
(def sum [])
(loop [i 0]
(when (< i 10)
(conj sum 10)
(recur (inc i))))
sum
)
This just gives me and empty vector even though I am conj-ing 10 onto sum. Is this because it is not in-scope within the Loop? How would I achieve the same outcome. (btw, this example is deliberately simplified)
Thanks
conj does not modify its argument. In fact, without resorting to evil reflection tricks, nothing will modify a vector, it's an immutable data structure. This is one of the fundamental principles of Clojure.
In functional programming, instead of modifying data, we replace it with another immutable value. So you need to use the return value of conj, or it is effectively a noop.
(defn keep?
[]
(loop [i 0 sum []]
(if (< i 10)
(recur (inc i) (conj sum 10))
sum)))
Also, the second arg to defn must always be a vector.
conj is not destructive, it does not alter that collection, returns a new collection with the designated state (reference).
To achieve the desired result, you may:
Define sum in a loop-form, like i is defined, instead of using def
recur (inc i) (conj sum 10) to rebind sum to a new one on every iteration, so that state is built up to what you expect
Once the condition in when is not met, just return sum from your loop and it will bubble up to become the return value of this function. Uh hang on, when does not have an "else-branch", a possible alternative is if
Like so:
(defn keep? []
(loop [i 0
sum []]
(if (< i 10)
(recur (inc i)
(conj sum 10))
sum)))
Just to supplement the other answers, I almost never use the loop function. Here are a few ways to do it using the for function:
; return a lazy sequence
(for [i (range 10) ]
i)
;=> (0 1 2 3 4 5 6 7 8 9)
; return a concrete vector
(vec
(for [i (range 10) ]
i))
;=> [0 1 2 3 4 5 6 7 8 9]
; 'into' is very nice for converting one collection into another
(into #{}
(for [i (range 10) ]
i))
;=> #{0 7 1 4 6 3 2 9 5 8} ; hash-set is unique but unordered

Split a vector into vector of vectors in clojure instead of vector of lists

The clojure documentation of split-at states that it takes a collection of elements and returns a vector of two lists, each containing elements greater or smaller than a given index:
(split-at 2 [1 2 3 4 5])
[(1 2) (3 4 5)]
What I want is this:
(split-at' 2 [1 2 3 4 5])
[[1 2] [3 4 5]]
This is a collection cut into two collections that keep the order of the elements (like vectors), preferably without performance penalties.
What is the usual way to do this and are there any performance optimized ways to do it?
If you're working exclusively with vectors, one option would be to use subvec.
(defn split-at' [idx v]
[(subvec v 0 idx) (subvec v idx)])
(split-at' 2 [1 2 3 4 5])
;; => [[1 2] [3 4 5]]
As regards to performance, the docs on subvec state:
This operation is O(1) and very fast, as
the resulting vector shares structure with the original and no
trimming is done.
Why not extend the core function with "vec" function ?
So based on split-at definition:
(defn split-at
"Returns a vector of [(take n coll) (drop n coll)]"
{:added "1.0"
:static true}
[n coll]
[(take n coll) (drop n coll)])
We can add vec to each element of the vector result
(defn split-at-vec
[n coll]
[(vec (take n coll)) (vec (drop n coll))])
Releated to "performance penalties" i think that when you transform your lazy seqs in favor of vector then you loose the lazy performance.

Finding the "middle element" from a vector in Clojure

Simple newbie question in Clojure...
If I have an odd number of elements in a Clojure vector, how can I extract the "middle" value? I've been looking at this for a while and can't work out how to do it!
Some examples:
(middle-value [0]) should return [0]
(middle-value [0 1 2]) should return [1]
(middle-value [0 1 :abc 3 4]) should return [:abc]
(middle-value [0 1 2 "test" 4 5 6]) should return ["test"]
How about calculating the middle index and accessing by it?
(defn middle-value [vect]
(when-not (empty? vect)
(vect (quot (count vect) 2))))
A somewhat inefficient but fun implementation (works with sequence abstraction instead of concrete vector):
(defn middle [[fst & rst]]
(if-not rst fst
(recur (butlast rst))))
Returns nil in case of even amount of elements.
Less fun but more efficient:
(nth v (quot (count v) 2))
where v is the vector.
Get the number of items in the vector, halve it, floor the result and get the item at that index. Assuming a vector v:
(get v (floor (/ (count v) 2)))
Unfortunately floor isn't in clojure.core, you'll need to pull in another library for that or go directly to java.lang.Math.floor.
This code, of course, doesn't do anything about even-counted vectors, but I'm assuming you can handle those already.

Idiomatic sequence slice in Clojure

In Python, there is a convenient way of taking parts of a list called "slicing":
a = [1,2,3,4,5,6,7,8,9,10] # ≡ a = range(1,10)
a[:3] # get first 3 elements
a[3:] # get all elements except the first 3
a[:-3] # get all elements except the last 3
a[-3:] # get last 3 elements
a[3:7] # get 4 elements starting from 3rd (≡ from 3rd to 7th exclusive)
a[3:-3] # get all elements except the first 3 and the last 3
Playing with clojure.repl/doc in Clojure, I found equivalents for all of them but I'm not sure they are idiomatic.
(def a (take 10 (iterate inc 1)))
(take 3 a)
(drop 3 a)
(take (- (count a) 3) a)
(drop (- (count a) 3) a)
(drop 3 (take 7 a))
(drop 3 (take (- (count a) 3) a))
My question is: how to slice sequences in Clojure? In other words, what is the correct way to return different parts of a sequence?
You can simplify all the ones using count by using take-last or drop-last instead:
(def a (take 10 (iterate inc 1)))
(take 3 a) ; get first 3 elements
(drop 3 a) ; get all elements except the first 3
(drop-last 3 a) ; get all elements except the last 3
(take-last 3 a) ; get last 3 elements
(drop 3 (take 7 a)) ; get 4 elements starting from 3
(drop 3 (drop-last 3 a)) ; get all elements except the first and the last 3
And as suggested in the comments below, you can use the ->> macro to "thread" several operation together. For example, the last two lines could also be written like this:
(->> a (take 7) (drop 3)) ; get 4 elements starting from 3
(->> a (drop-last 3) (drop 3)) ; get all elements except the first and the last 3
I think the two methods are both very readable if you are only applying two operations to a list, but when you have a long string like take, map, filter, drop, first then using the ->> macro can make the code much easier to read and probably even easier for to write.
Python's notion of a sequence is very different from Clojure's.
In Python,
a sequence is a finite ordered set indexed by
non-negative numbers; and
a list is a mutable sequence which you can add slices to or remove
slices from.
In Clojure,
a sequence is an interface supporting first, rest, and
cons;
a list is an immutable sequential collection with cons (or
rest) adding (or removing) first elements (returning lists so
modified, anyway).
The nearest thing in Clojure to a Python list is a vector. As Adam Sznajder suggests, you can slice it using subvec, though you can't add or remove slices as you can in Python.
subvec is a fast constant-time operation, whereas drop makes you pay for the number of elements bypassed (take makes you pay for the elements you traverse, but these are the ones you are interested in).
Your examples become ...
(def a (vec (range 1 (inc 10))))
(subvec a 0 3)
; [1 2 3]
(subvec a 3)
; [4 5 6 7 8 9 10]
(subvec a 0 (- (count a) 3))
; [1 2 3 4 5 6 7]
(subvec a (- (count a) 3))
; [8 9 10]
(subvec a 3 (+ 3 4))
; [4 5 6 7]
(subvec a 3 (- (count a) 3))
; [4 5 6 7]
There is a function subvec. Unfortunately, it only works with vectors so you would have to transform your sequence:
http://clojuredocs.org/clojure_core/clojure.core/subvec
Slicing a sequence is a bit of a "code smell" - a sequence in general is designed for sequential access of items.
If you are going to do a lot of slicing / concatenation, there are much better data structures available, in particular checkout the RRB-Tree vector implementation:
https://github.com/clojure/core.rrb-vector
This supports very efficient subvec and catvec operations.