Test case:
(def coll [1 2 2 3 4 4 4 5])
(def p-coll (partition 2 1 coll))
;; ((1 2) (2 2) (2 3) (3 4) (4 4) (4 4) (4 5))
Expected output:
(2 2 4 4 4) => 16
Here is what I am to implement: Start with vector v [0]. Take each pair, if the first element of the pair is equal to the last element of the vector, or if the elements of the pair are equal, add the first item of the pair to v. (And finally reduce v to its sum.) The code below can do if the elements of the pair are equal part, but not the first part. (Thus I get (0 2 4 4). I guess the reason is that the elements are added to v at the very end. My questions:
What is the way to compare an element with the last selected element?
What other idiomatic ways are there to implement what I am trying achieve?
How else can I approach the problem?
(let [s [0]]
(concat s (map #(first %)
(filter #(or (= (first %) (first s)) (= (first %) (second %))) p-coll))))
You are on the right track with partitioning the data here. But there
is a nicer way to do that. You can use (partition-by identity coll)
to group consecutive, same elements.
Then just keep the ones with more than one elements and sum them all up.
E.g.
(reduce
(fn [acc xs]
(+ acc (apply + xs)))
0
(filter
next
(partition-by identity coll)))
Starting out from your initial partition, with p-coll being like you described above (i.e. a list of pairs), and v being the vector [0], you can do the following:
(reduce
(fn [vect [a b]]
(if (or (= a b) (= a (last vect)))
(conj vect a)
vect))
v p-coll)
;; => [0 2 2 4 4 4]
We start from the vector [0], and reduce p-coll by processing its elements one by one. If an element satisfies one of the two conditions you specified, then we conj it onto the initial vector. Otherwise, we leave the vector as is.
Finally, you can use apply + to sum the resulting vector and get your final answer.
Generally, when you need to process a collection (here, p-coll) and some partial answer (here, the vector v) into some sort of final answer (here, the vector [0 2 2 4 4 4]), reduce is the most idiomatic and purely functional approach. After having identified those components, it's just a matter of coming up with the appropriate function to put them together.
Another approach (less idiomatic, but easier to understand from a procedural standpoint) would be to use an atom for the vector v, and keep growing it as you process the list with doseq:
(def v (atom [0]))
(doseq [[a b] p-coll]
(if (or (= a b) (= a (last #v)))
(swap! v conj a)))
(println #v)
;; => [0 2 2 4 4 4]
A solution only with flatten and map:
(defn consecutives [lst]
(flatten (map (fn [[x y z]] (cond (= x y z) [z]
(= y z) [y z]
:else []))
(map #'vector lst (rest lst) (rest (rest lst))))))
Purely tail-recursive solution
which "keeps in memory" previous and previous-previous element.
(defn consecutives
[lst]
(loop [lst lst
acc []
last-val nil
last-last-val nil]
(cond (empty? lst) acc
:else (recur (rest lst)
(if (= (first lst) last-val)
(conj (if (= last-val last-last-val)
acc
(conj acc last-val))
(first lst))
acc)
(first lst)
last-val))))
(consecutives coll)
;; => [2 2 4 4 4]
Related
I need a function that maps a function only on every other element, e.g.
(f inc '(1 2 3 4))
=> '(2 2 4 4)
I came up with:
(defn flipflop [f l]
(loop [k l, b true, r '()]
(if (empty? k)
(reverse r)
(recur (rest k)
(not b)
(conj r (if b
(f (first k))
(first k)))))))
Is there a prettier way to achieve this ?
(map #(% %2)
(cycle [f identity])
coll)
It's a good idea to look at Clojure's higher level functions before using loop and recur.
user=> (defn flipflop
[f coll]
(mapcat #(apply (fn ([a b] [(f a) b])
([a] [(f a)]))
%)
(partition-all 2 coll)))
#'user/flipflop
user=> (flipflop inc [1 2 3 4])
(2 2 4 4)
user=> (flipflop inc [1 2 3 4 5])
(2 2 4 4 6)
user=> (take 11 (flipflop inc (range))) ; demonstrating laziness
(1 1 3 3 5 5 7 7 9 9 11)
this flipflop doesn't need to reverse the output, it is lazy, and I find it much easier to read.
The function uses partition-all to split the list into pairs of two items, and mapcat to join a series of two element sequences from the calls back into a single sequence.
The function uses apply, plus multiple arities, in order to handle the case where the final element of the partitioned collection is a singleton (the input was odd in length).
also, since you want to apply the function to some specific indiced items in the collection (even indices in this case) you could use map-indexed, like this:
(defn flipflop [f coll]
(map-indexed #(if (even? %1) (f %2) %2) coll))
Whereas amalloy's solution is the one, you could simplify your loop - recur solution a bit:
(defn flipflop [f l]
(loop [k l, b true, r []]
(if (empty? k)
r
(recur (rest k)
(not b)
(conj r ((if b f identity) (first k)))))))
This uses couple of common tricks:
If an accumulated list comes out in the wrong order, use a vector
instead.
Where possible, factor out common elements in a conditional.
I have function
(defn goneSeq [inseq uptil]
(loop [counter 0 newSeq [] orginSeq inseq]
(if (== counter uptil)
newSeq
(recur (inc counter) (conj newSeq (first orginSeq)) (rest orginSeq)))))
(defn insert [sorted-seq n]
(loop [currentSeq sorted-seq counter 0]
(cond (empty? currentSeq) (concat sorted-seq (vector n))
(<= n (first currentSeq)) (concat (goneSeq sorted-seq counter) (vector n) currentSeq)
:else (recur (rest currentSeq) (inc counter)))))
that takes in a sorted-sequence and insert the number n at its appropriate position for example: (insert [1 3 4] 2) returns [1 2 3 4].
Now I want to use this function with reduce to sort a given sequence so something like:
(reduce (insert seq n) givenSeq)
What is thr correct way to achieve this?
If the function works for inserting a single value, then this would work:
(reduce insert [] givenSeq)
for example:
user> (reduce insert [] [0 1 2 30.5 0.88 2.2])
(0 0.88 1 2 2.2 30.5)
Also, it should be noted that sort and sort-by are built in and are better than most hand-rolled solutions.
May I suggest some simpler ways to do insert:
A slowish lazy way is
(defn insert [s x]
(let [[fore aft] (split-with #(> x %) s)]
(concat fore (cons x aft))))
A faster eager way is
(defn insert [coll x]
(loop [fore [], coll coll]
(if (and (seq coll) (> x (first coll)))
(recur (conj fore x) (rest coll))
(concat fore (cons x coll)))))
By the way, you had better put your defns in bottom-up order, if possible. Use declare if there is mutual recursion. You had me thinking your solution did not compile.
I am trying to write a recursive sort function that sorts a list from low to high (duh). I am currently getting output, just not the correct output. Here is my code:
(defn sort/predicate [pred loi]
(if (empty? loi)
()
(if (= (count loi) 1)
(cons (first loi) (sort pred (rest loi)))
(if (pred (first loi) (first (rest loi)))
(cons (first loi) (sort pred (rest loi)))
(if (pred (first (rest loi)) (first loi))
(cons (first (rest loi)) (sort pred (cons (first loi) (rest (rest loi)))))
(cons (first loi) (sort pred (rest loi))))))))
Basically, I compare the first two elements in the list and, if the first element is smaller I cons it with the result of comparing the next two elements of the list. If the second element of the list is smaller, I cons the second element with the result of sorting the first two elements of the cons of the first element and everything after the second element (sorry if that's hard to follow). Then, when there is only one element left in the list, I throw it on the end and return it. However, there is a bug along the way somewhere because I should get the following:
>(sort/predicate < '(8 2 5 2 3))
(2 2 3 5 8)
but instead, I get:
>(sort/predicate < '(8 2 5 2 3))
(2 5 2 3 8)
I'm pretty new to clojure, so any help is greatly appreciated. Also, I would like to keep my code roughly the same (I don't want to use a sorting function that already exists). Thanks
I don't think this is a very efficient way to sort, but I tried to stay true to your intention:
(defn my-sort [cmp-fn [x & xs]]
(cond
(nil? x) '()
(empty? xs) (list x)
:else (let [[y & ys :as s] (my-sort cmp-fn xs)]
(if (cmp-fn x y)
(cons x s)
(cons y (my-sort cmp-fn (cons x ys)))))))
;; merge sort implementation - recursive sort without stack consuming
(defn merge-sort
([v comp-fn]
(if (< (count v) 2) v
(let [[left right] (split-at (quot (count v) 2) v)]
(loop [result []
sorted-left (merge-sort left comp-fn)
sorted-right (merge-sort right comp-fn)]
(cond
(empty? sorted-left) (into result sorted-right)
(empty? sorted-right) (into result sorted-left)
:else (if (comp-fn 0 (compare (first sorted-left) (first sorted-right)))
(recur (conj result (first sorted-left)) (rest sorted-left) sorted-right)
(recur (conj result (first sorted-right)) sorted-left (rest sorted-right))))))))
([v] (merge-sort v >)))
clojure.core/sort implement by Java more general.
user=> (sort '(8 2 5 2 3))
(2 2 3 5 8)
user=> (sort > '(8 2 5 2 3))
(8 5 3 2 2)
user=> (source sort)
(defn sort
"Returns a sorted sequence of the items in coll. If no comparator is
supplied, uses compare. comparator must implement
java.util.Comparator. If coll is a Java array, it will be modified.
To avoid this, sort a copy of the array."
{:added "1.0"
:static true}
([coll]
(sort compare coll))
([^java.util.Comparator comp coll]
(if (seq coll)
(let [a (to-array coll)]
(. java.util.Arrays (sort a comp))
(seq a))
())))
nil
user=>
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])
I'm struggling to find a beautiful, idiomatic way to write a function
(defn remove-smaller
[coll partial-order-fn]
___
)
where partial-order-fn takes two arguments and return -1 0 or 1 is they are comparable (resp. smaller, equal, bigger) or nil otherwise.
The result of remove-smaller should be coll, with all items that are smaller than any other item in coll are removed.
Example: If we defined a partial order such as numbers are compared normally, letters too, but a letter and a number are not comparable:
1 < 2 a < t 2 ? a
Then we would have:
(remove-smaller [1 9 a f 3 4 z])
==> [9 z]
(defn partial-compare [x y]
(when (= (type x) (type y))
(compare x y)))
(defn remove-smaller [coll partial-order-fn]
(filter
(fn [x] (every? #(let [p (partial-order-fn x %)]
(or (nil? p) (>= p 0)))
coll))
coll))
(defn -main []
(remove-smaller [1 9 \a \f 3 4 \z] partial-compare))
This outputs (9 \z), which is correct unless you want the return value to be of the same type as coll.
In practice I might just use tom's answer, since no algorithm can guarantee better than O(n^2) worst-case performance and it's easy to read. But if performance matters, choosing an algorithm that is always n^2 isn't good if you can avoid it; the below solution avoids re-iterating over any items which are known not to be maxes, and therefore can be as good as O(n) if the set turns out to actually be totally ordered. (of course, this relies on transitivity of the ordering relation, but since you call this a partial order that's implied)
(defn remove-smaller [cmp coll]
(reduce (fn [maxes x]
(let [[acc keep-x]
,,(reduce (fn [[acc keep-x] [max diff]]
(cond (neg? diff) [(conj acc max) false]
(pos? diff) [acc keep-x]
:else [(conj acc max) keep-x]))
[[] true], (map #(list % (or (cmp x %) 0))
maxes))]
(if keep-x
(conj acc x)
acc)))
(), coll))
(def data [1 9 \a \f 3 4 \z])
(defn my-fn [x y]
(when (= (type x) (type y))
(compare x y)))
(defn remove-smaller [coll partial-order-fn]
(mapv #(->> % (sort partial-order-fn) last) (vals (group-by type data))))
(remove-smaller data my-fn)
;=> [9 \z]
Potentially the order of the remaining items might differ to the input collection, but there is no order between the equality 'partitions'