Use of :let modifier in Clojure - clojure

I wonder what is the sense of a :let modifier when using the for loop in Clojure?

:let lets you define named values, in the same sense that the let special form lets you do it:
(for [i (range 10)
:let [x (* i 2)]]
x) ;;=> (0 2 4 6 8 10 12 14 16 18)
is equivalent to:
(for [i (range 10)]
(let [x (* i 2)]
x)) ;;=> (0 2 4 6 8 10 12 14 16 18)
except when used in combination with :when (or :while):
(for [i (range 10)
:let [x (* i 2)]
:when (> i 5)]
x) ;;=> (12 14 16 18)
(for [i (range 10)]
(let [x (* i 2)]
(when (> i 5) x))) ;;=> (nil nil nil nil nil nil 12 14 16 18)

You can use :let to create bindings inside a list comprehension like let.
To quote an example of the clojure documentation:
user=> (for [x [0 1 2 3 4 5]
:let [y (* x 3)]
:when (even? y)]
y)
(0 6 12)
The trick is that you can now use y in the :while and :when modifiers, instead of writing something like
user=> (for [x [0 1 2 3 4 5]
:when (even? (* x 3))]
(* x 3))

Related

clojure split collection in chunks of increasing size

Hi I am a clojure newbie,
I am trying to create a function that splits a collection into chunks of increasing size something along the lines of
(apply #(#(take %) (range 1 n)) col)
where n is the number of chunks
example of expected output:
with n = 4 and col = (range 1 4)
(1) (2 3) (4)
with n = 7 and col = (range 1 7)
(1) (2 3) (4 5 6) (7)
You can use something like this:
(defn partition-inc
"Partition xs at increasing steps of n"
[n xs]
(lazy-seq
(when (seq xs)
(cons (take n xs)
(partition-inc (inc n) (drop n xs))))))
; (println (take 5 (partition-inc 1 (range))))
; → ((0) (1 2) (3 4 5) (6 7 8 9) (10 11 12 13 14))
Or if you want to have more influence, you could alternatively provide
a sequence for the sizes (behaves the same as above, if passed (iterate inc 1) for sizes:
(defn partition-sizes
"Partition xs into chunks given by sizes"
[sizes xs]
(lazy-seq
(when (and (seq sizes) (seq xs))
(let [n (first sizes)]
(cons (take n xs) (partition-sizes (rest sizes) (drop n xs)))))))
; (println (take 5 (partition-sizes (range 1 10 2) (range))))
; → ((0) (1 2 3) (4 5 6 7 8) (9 10 11 12 13 14 15) (16 17 18 19 20 21 22 23 24))
An eager solution would look like
(defn partition-inc [coll]
(loop [rt [], c (seq coll), n 1]
(if (seq c)
(recur (conj rt (take n c)) (drop n c) (inc n))
rt)))
another way would be to employ some clojure sequences functions:
(->> (reductions (fn [[_ x] n] (split-at n x))
[[] (range 1 8)]
(iterate inc 1))
(map first)
rest
(take-while seq))
;;=> ((1) (2 3) (4 5 6) (7))
Yet another approach...
(defn growing-chunks [src]
(->> (range)
(reductions #(drop %2 %1) src)
(take-while seq)
(map-indexed take)
rest))
(growing-chunks [:a :b :c :d :e :f :g :h :i])
;; => ((:a) (:b :c) (:d :e :f) (:g :h :i))

Partition a seq by a "windowing" predicate in Clojure

I would like to "chunk" a seq into subseqs the same as partition-by, except that the function is not applied to each individual element, but to a range of elements.
So, for example:
(gather (fn [a b] (> (- b a) 2))
[1 4 5 8 9 10 15 20 21])
would result in:
[[1] [4 5] [8 9 10] [15] [20 21]]
Likewise:
(defn f [a b] (> (- b a) 2))
(gather f [1 2 3 4]) ;; => [[1 2 3] [4]]
(gather f [1 2 3 4 5 6 7 8 9]) ;; => [[1 2 3] [4 5 6] [7 8 9]]
The idea is that I apply the start of the list and the next element to the function, and if the function returns true we partition the current head of the list up to that point into a new partition.
I've written this:
(defn gather
[pred? lst]
(loop [acc [] cur [] l lst]
(let [a (first cur)
b (first l)
nxt (conj cur b)
rst (rest l)]
(cond
(empty? l) (conj acc cur)
(empty? cur) (recur acc nxt rst)
((complement pred?) a b) (recur acc nxt rst)
:else (recur (conj acc cur) [b] rst)))))
and it works, but I know there's a simpler way. My question is:
Is there a built in function to do this where this function would be unnecessary? If not, is there a more idiomatic (or simpler) solution that I have overlooked? Something combining reduce and take-while?
Thanks.
Original interpretation of question
We (all) seemed to have misinterpreted your question as wanting to start a new partition whenever the predicate held for consecutive elements.
Yet another, lazy, built on partition-by
(defn partition-between [pred? coll]
(let [switch (reductions not= true (map pred? coll (rest coll)))]
(map (partial map first) (partition-by second (map list coll switch)))))
(partition-between (fn [a b] (> (- b a) 2)) [1 4 5 8 9 10 15 20 21])
;=> ((1) (4 5) (8 9 10) (15) (20 21))
Actual Question
The actual question asks us to start a new partition whenever pred? holds for the beginning of the current partition and the current element. For this we can just rip off partition-by with a few tweaks to its source.
(defn gather [pred? coll]
(lazy-seq
(when-let [s (seq coll)]
(let [fst (first s)
run (cons fst (take-while #((complement pred?) fst %) (next s)))]
(cons run (gather pred? (seq (drop (count run) s))))))))
(gather (fn [a b] (> (- b a) 2)) [1 4 5 8 9 10 15 20 21])
;=> ((1) (4 5) (8 9 10) (15) (20 21))
(gather (fn [a b] (> (- b a) 2)) [1 2 3 4])
;=> ((1 2 3) (4))
(gather (fn [a b] (> (- b a) 2)) [1 2 3 4 5 6 7 8 9])
;=> ((1 2 3) (4 5 6) (7 8 9))
Since you need to have the information from previous or next elements than the one you are currently deciding on, a partition of pairs with a reduce could do the trick in this case.
This is what I came up with after some iterations:
(defn gather [pred s]
(->> (partition 2 1 (repeat nil) s) ; partition the sequence and if necessary
; fill the last partition with nils
(reduce (fn [acc [x :as s]]
(let [n (dec (count acc))
acc (update-in acc [n] conj x)]
(if (apply pred s)
(conj acc [])
acc)))
[[]])))
(gather (fn [a b] (when (and a b) (> (- b a) 2)))
[1 4 5 8 9 10 15 20 21])
;= [[1] [4 5] [8 9 10] [15] [20 21]]
The basic idea is to make partitions of the number of elements the predicate function takes, filling the last partition with nils if necessary. The function then reduces each partition by determining if the predicate is met, if so then the first element in the partition is added to the current group and a new group is created. Since the last partition could have been filled with nulls, the predicate has to be modified.
Tow possible improvements to this function would be to let the user:
Define the value to fill the last partition, so the reducing function could check if any of the elements in the partition is this value.
Specify the arity of the predicate, thus allowing to determine the grouping taking into account the current and the next n elements.
I wrote this some time ago in useful:
(defn partition-between [split? coll]
(lazy-seq
(when-let [[x & more] (seq coll)]
(lazy-loop [items [x], coll more]
(if-let [[x & more] (seq coll)]
(if (split? [(peek items) x])
(cons items (lazy-recur [x] more))
(lazy-recur (conj items x) more))
[items])))))
It uses lazy-loop, which is just a way to write lazy-seq expressions that look like loop/recur, but I hope it's fairly clear.
I linked to a historical version of the function, because later I realized there's a more general function that you can use to implement partition-between, or partition-by, or indeed lots of other sequential functions. These days the implementation is much shorter, but it's less obvious what's going on if you're not familiar with the more general function I called glue:
(defn partition-between [split? coll]
(glue conj []
(fn [v x]
(not (split? [(peek v) x])))
(constantly false)
coll))
Note that both of these solutions are lazy, which at the time I'm writing this is not true of any of the other solutions in this thread.
Here is one way, with steps split up. It can be narrowed down to fewer statements.
(def l [1 4 5 8 9 10 15 20 21])
(defn reduce_fn [f x y]
(cond
(f (last (last x)) y) (conj x [y])
:else (conj (vec (butlast x)) (conj (last x) y)) )
)
(def reduce_fn1 (partial reduce_fn #(> (- %2 %1) 2)))
(reduce reduce_fn1 [[(first l)]] (rest l))
keep-indexed is a wonderful function. Given a function f and a vector lst,
(keep-indexed (fn [idx it] (if (apply f it) idx))
(partition 2 1 lst)))
(0 2 5 6)
this returns the indices after which you want to split. Let's increment them and tack a 0 at the front:
(cons 0 (map inc (.....)))
(0 1 3 6 7)
Partition these to get ranges:
(partition 2 1 nil (....))
((0 1) (1 3) (3 6) (6 7) (7))
Now use these to generate subvecs:
(map (partial apply subvec lst) ....)
([1] [4 5] [8 9 10] [15] [20 21])
Putting it all together:
(defn gather
[f lst]
(let [indices (cons 0 (map inc
(keep-indexed (fn [idx it]
(if (apply f it) idx))
(partition 2 1 lst))))]
(map (partial apply subvec (vec lst))
(partition 2 1 nil indices))))
(gather #(> (- %2 %) 2) '(1 4 5 8 9 10 15 20 21))
([1] [4 5] [8 9 10] [15] [20 21])

Clojure - how to do reductions function but drop state?

If I use the reductions function like so:
(reductions + [1 2 3 4 5])
Then I get
(1 3 6 10 15)
Which is great - but I'd like to apply a binary function in the same way without the state being carried forward - something like
(magic-hof + [1 2 3 4 5])
leads to
(1 3 5 7 9)
ie it returns the operation applied to the first pair, then steps 1 to the next pair.
Can someone tell me the higher-order function I'm looking for? (Something like reductions)
This is my (non-working) go at it:
(defn thisfunc [a b] [(+ a b) b])
(reduce thisfunc [1 2 3 4 5])
You can do it with map:
(map f coll (rest coll))
And if you want a function:
(defn map-pairwise [f coll]
(map f coll (rest coll)))
And if you really need the first element to remain untouched (thanx to juan.facorro's comment):
(defn magic-hof [f [x & xs :as s]]
(cons x (map f s xs)))
partition will group your seq:
user> (->> [1 2 3 4 5] (partition 2 1) (map #(apply + %)) (cons 1))
(1 3 5 7 9)
So, you want to apply a function to subsequent pairs of elements?
(defn pairwise-apply
[f sq]
(when (seq sq)
(->> (map f sq (next sq))
(cons (first sq)))))
Let's try it:
(pairwise-apply + (range 1 6))
;; => (1 3 5 7 9)
This is sufficient:
(#(map + (cons 0 %) %) [1 2 3 4 5])
;; => (1 3 5 7 9)

Building tables in clojure

If I wanted to build a table in Clojure of vector duplicates, I'd write:
(take 2 (repeat [1 2 3]))
But how would I expand this notion of a table function to build something like:
Input 1: [a^2 2 6 2] where a^2 is some input function, 2 is min value, 6 is max value, and 2 is step size.
Output 1: [4,16,36]
Input 2: [b^2 10 -5 -2]
Output 2: [100 64 36 16 4 0 4 16]
This outputs a 4x3 matrix
Input 3: [(+ (* 10 i) j) [1 4] [1 3]]
where (+ (* 10 i) j) is 10i+j (some given input function), [1 4] is the min and max of i, and [1 3] is the min and max of j.
Output 3: [[11 12 13] [21 22 23] [31 32 33] [41 42 43]]
You want to use for in a nested fashion:
(for [i (range 1 (inc 4))]
(for [j (range 1 (inc 3))]
(+ (* 10 i) j)))
;; '((11 12 13) (21 22 23) (31 32 33) (41 42 43))
EDIT: expanded with an example implementation
For example:
(defn build-seq [f lower upper step]
(for [i (range lower (+ upper step) step)]
(f i)))
(build-seq #(* % %) 2 6 2)
;; '(4 16 36)
(defn build-table [f [ilower iupper] [jlower jupper]]
(for [i (range ilower (inc iupper))]
(for [j (range jlower (inc jupper))]
(f i j))))
(build-table #(+ (* 10 %) %2) [1 4] [1 3])
;; '((11 12 13) (21 22 23) (31 32 33) (41 42 43))
Your three input/output samples do not display a consistent signature for one variable and two ; furthermore, the step argument seems to be optional. I'm skeptical about the existence of a nice API that would retain the samples' syntax, but I can try something different (even if I do believe the simple embedded for forms are a better solution):
(defn flexible-range [{:keys [lower upper step] :or {lower 0}}]
(let [[upper step] (cond
(and upper step) [(+ upper step) step]
step (if (pos? step)
[Double/POSITIVE_INFINITY step]
[Double/NEGATIVE-INFINITY step])
upper (if (< lower upper)
[(inc upper) 1]
[(dec upper) -1])
:else [Double/POSITIVE_INFINITY 1])]
(range lower upper step)))
(defn build-table
([f [& params]]
(for [i (flexible-range params)]
(f i)))
([f [& iparams] [& jparams]]
(for [i (flexible-range iparams)]
(for [j (flexible-range jparams)]
(f i j)))))
(build-table #(* % %) [:lower 2 :upper 6 :step 2])
;; '(4 16 36)
(build-table #(+ (* 10 %) %2) [:lower 1 :upper 4]
[:lower 1 :upper 3])
;; '((11 12 13) (21 22 23) (31 32 33) (41 42 43))
I think you can easily solve it with map and range
(defn applier
[f ini max step]
(map f (range ini (+ max step) step)))
(applier #(* % %) 2 6 2)
=> (4 16 36)
This fn can resolve your third example
(defn your-fn [[ra1 ra2] [rb1 rb2] the-fn]
(vec (map (fn [i] (vec (map (fn [j] (the-fn i j)) (range rb1 (inc rb2))))) (range ra1 (inc ra2))))
)
(your-fn [1 4] [1 3] (fn [i j] (+ (* 10 i) j)))
=> [[11 12 13] [21 22 23] [31 32 33] [41 42 43]]
But i'd need a few more specification details (or more use cases) to make this behavior generic, maybe you can explain a little more your problem. I think the 1st-2nd and the 3rd examples don't take the same type of parameters and meaning, (step vs seq ). So the #Guillermo-Winkler solves part the problem and my-fn will cover the last example

Changing map behaviour in Clojure

I need to modify map function behavior to provide mapping not with minimum collection size but with maximum and use zero for missing elements.
Standard behavior:
(map + [1 2 3] [4 5 6 7 8]) => [5 7 9]
Needed behavior:
(map + [1 2 3] [4 5 6 7 8]) => [5 7 9 7 8]
I wrote function to do this, but it seems not very extensible with varargs.
(defn map-ext [f coll1 coll2]
(let [mx (max (count coll1) (count coll2))]
(map f
(concat coll1 (repeat (- mx (count coll1)) 0))
(concat coll2 (repeat (- mx (count coll2)) 0)))))
Is there a better way to do this?
Your method is concise, but inefficient (it calls count). A more efficient solution, which does not require the entirety of its input sequences to be stored in memory follows:
(defn map-pad [f pad & colls]
(lazy-seq
(let [seqs (map seq colls)]
(when (some identity seqs)
(cons (apply f (map #(or (first %) pad) seqs))
(apply map-pad f pad (map rest seqs)))))))
Used like this:
user=> (map-pad + 0 [] [1] [1 1] (range 1 10))
(3 3 3 4 5 6 7 8 9)
Edit: Generalized map-pad to arbitrary arity.
Another lazy variant, usable with an arbitrary number of input sequences:
(defn map-ext [f ext & seqs]
(lazy-seq
(if (some seq seqs)
(cons (apply f (map #(if (seq %) (first %) ext) seqs))
(apply map-ext f ext (map rest seqs)))
())))
Usage:
user> (map-ext + 0 [1 2 3] [4 5 6 7 8])
(5 7 9 7 8)
user> (map-ext + 0 [1 2 3] [4 5 6 7 8] [3 4])
(8 11 9 7 8)
If you just want it to work for any number of collections, try:
(defn map-ext [f & colls]
(let [mx (apply max (map count colls))]
(apply map f (map #(concat % (repeat (- mx (count %)) 0)) colls))))
Clojure> (map-ext + [1 2] [1 2 3] [1 2 3 4])
(3 6 6 4)
I suspect there may be better solutions though (as Trevor Caira suggests, this solution isn't lazy due to the calls to count).
How about that:
(defn map-ext [f x & xs]
(let [colls (cons x xs)
res (apply map f colls)
next (filter not-empty (map #(drop (count res) %) colls))]
(if (empty? next) res
(lazy-seq (concat res (apply map-ext f next))))))
user> (map-ext + [1 2 3] [4] [5 6] [7 8 9 10])
(17 16 12 10)
Along the lines of #LeNsTR's solution, but simpler and faster:
(defn map-ext [f & colls]
(lazy-seq
(let [colls (filter seq colls)
firsts (map first colls)
rests (map rest colls)]
(when (seq colls)
(cons (apply f firsts) (apply map-ext f rests))))))
(map-ext + [1 2 3] [4] [5 6] [7 8 9 10])
;(17 16 12 10)
I've just noticed Michał Marczyk's accepted solution, which is superior: it deals properly with asymmetric mapping functions such as -.
We can make Michał Marczyk's answer neater by using the convention - which many core functions follow - that you get a default or identity value by calling the function with no arguments. For examples:
(+) ;=> 0
(concat) ;=> ()
The code becomes
(defn map-ext [f & seqs]
(lazy-seq
(when (some seq seqs)
(cons (apply f (map #(if (seq %) (first %) (f)) seqs))
(apply map-ext f (map rest seqs)))
)))
(map-ext + [1 2 3] [4 5 6 7 8] [3 4])
;(8 11 9 7 8)
I've made the minimum changes. It could be speeded up a bit.
We may need a function that will inject such a default value into a function that lacks it:
(defn with-default [f default]
(fn
([] default)
([& args] (apply f args))))
((with-default + 6)) ;=> 6
((with-default + 6) 7 8) ;=> 15
This could be speeded up or even turned into a macro.