I want to write the function collect which can combine the sublists into a list, like:
user=> (collect '(a b c d e) 1)
((a)(b)(c)(d)(e))
user=> (collect '(a b c d e) 2)
((a b)(c d)(e))
user=> (collect '(a b c d e) 5)
(a b c d e))
this is my code:
(defn collect [lst num]
(loop [l lst res (atom ())]
(if (<= (count l) num) #res
(recur (drop num l) (swap! res conj (take num (drop num l)))))))
But when I run
user=> (collect '(a b c d e) 1)
I got the error:
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IAtom clojure.core/swap!
why I cannot get the res when I use "swap!" ? Thank you.
It's blowing up in the second pass through the loop.
swap returns the value that was put into the atom, not the atom it's self. So the first pass is updating the atom, and then passing the value it just put into the atom to the second pass through the loop. in the second pass it's trying to use the value as the atom, and getting the exception above.
To "fix" this use a do to update the atom, then pass the atom to the next pass through the loop once it contains the correct value.
user> (defn collect [lst num]
(loop [l lst res (atom ())]
(if (<= (count l) num) #res
(recur (drop num l)
(do (swap! res conj (take num (drop num l)))
res)))))
#'user/collect
user> (collect '(a b c d e) 2)
((e) (c d))
You can also in this case, just remove the atom completely and get exactly the same result (I fixed on ordering problem from your example by using a [] instead of () in the initial value of res):
user> (defn collect [lst num]
(loop [l lst res []]
(if (<= (count l) num) res
(recur (drop num l)
(conj res (take num (drop num l)))))))
#'user/collect
user> (collect '(a b c d e) 2)
[(c d) (e)]
and of course you can also use partition-all as glts mentions above.
;; this would be a correct way to do it
(defn collect [coll n]
(partition-all n coll))
;; this would be a clumsy way to do it
(defn collect
"using a loop (there is not point to do that but at least you can see the logic working as in your example)"
[coll n]
(lazy-seq
(loop [res []
coll coll]
(if (empty? coll)
res
(recur (conj res (take n coll)) (drop n coll))))))
Regarding your error, on the second loop, res is a list-like value, not an atom anymore. That would lead us to :
(defn collect [coll n]
(lazy-seq (loop [res (atom [])
coll coll]
(if (empty? coll)
#res
(recur (do (swap! res conj (take n coll))
;; return the atom instead of the value'
res)
(drop n coll))))))
Note that in order to preserve the order in the solution, I use a vector (litteral []) instead of a list (litteral '()). This is because of the behaviour of conj described here.
Related
So I am trying to solve this problem, and this is the code I have come up with:
First I have a pack function, receives a list and groups same elements into a vector.
(defn pack [lst]
(def a [])
(def vect [])
(cond
(empty? lst)
lst
:else
(loop [i 0]
(def r (get lst i))
(def t (get lst (+ i 1)))
(if (= r t)
(def vect (conj vect r))
)
(if (not= r t)
(and (def vect (conj vect r)) (and (def a (conj a vect)) (def vect [])))
)
(if (= i (- (count lst) 1))
a
(recur (inc i))
)
))
)
for example if I have this vector:
(def tes '[a a a a b c c a a d e e e e])
pack function will return this:
[[a a a a] [b] [c c] [a a] [d] [e e e e]]
Then I tried doing the "encode" part of the problem with this code:
(def v1 [])
(def v2 [])
(conj v2 (conj v1 (count (get (pack tes) 0)) (get (get (pack tes) 0) 0)))
And it returned what I wanted, a vector "v2" with a vector "v1" that has the "encoded" item.
[[4 a]]
So now I try to make the function:
(defn encode [lst]
(loop [index 0 limit (count (pack lst)) v1 [] v2[]]
(if (= index limit)
lst
(conj v2 (conj v1 (count (get (pack tes) index)) (get (get (pack tes) index) index)))
)
(recur (inc index) limit v1 v2)
)
)
(encode tes)
but I get this error:
2021/03/07 00:00:21 got exception from server /usr/local/bin/lein: line 152:
28 Killed "$LEIN_JAVA_CMD" "${BOOTCLASSPATH[#]}" -Dfile.encoding=UTF-8 -Dmaven.wagon.http.ssl.easy=false -Dmaven.wagon.rto=10000 $LEIN_JVM_OPTS
-Dleiningen.original.pwd="$ORIGINAL_PWD" -Dleiningen.script="$0" -classpath "$CLASSPATH" clojure.main -m leiningen.core.main "$#"
2021/03/07 01:42:20 error reading from server EOF
Any way to fix my code or to solve the problem more efficiently but still return a vector?
juxt can be used in the pack function:
(defn pack [xs]
(map (juxt count first) (partition-by identity xs)))
(defn unpack [xs]
(mapcat #(apply repeat %) xs))
Don't use def inside function, because it creates global
variable. Use let instead.
Don't use multiple if in row, there is cond.
Format your code better- for example, put all parentheses on the end together on one line.
Here is more efficient solution:
(defn pack [lst]
(letfn [(pack-help [lst]
(if (empty? lst) '()
(let [elem (first lst)]
(cons (vec (take-while #(= % elem) lst))
(pack-help (drop-while #(= % elem) lst))))))]
(vec (pack-help lst))))
(defn pack-with-count [lst]
(mapv #(vector (count %) (first %))
(pack lst)))
(defn unpack [packed-lst]
(into [] (apply concat packed-lst)))
(pack '[a a a a b c c a a d e e e e])
(pack-with-count '[a a a a b c c a a d e e e e])
(unpack '[[a a a a] [b] [c c] [a a] [d] [e e e e]])
As a rule, whenever you reach for loop/recur, there are some pieces of the standard library which will allow you to get the desired effect using higher-order functions. You avoid needing to implement the wiring and can just concentrate on your intent.
(def tes '[a a a a b c c a a d e e e e])
(partition-by identity tes)
; => ((a a a a) (b) (c c) (a a) (d) (e e e e))
(map (juxt count first) *1)
; => ([4 a] [1 b] [2 c] [2 a] [1 d] [4 e])
(mapcat #(apply repeat %) *1)
; => (a a a a b c c a a d e e e e)
Here *1 is just the REPL shorthand for "previous result" - if you need to compose these into functions, this will be replaced with your argument.
If you really need vectors rather than sequences for the outer collection at each stage, you can wrap with vec (to convert the lazy sequence to a vector), or use mapv instead of map.
Finally - the error message you are getting from lein is a syntax error rather than a logic or code problem. Clojure generally flags an unexpected EOF if there aren't enough closing parens.
(println "because we left them open like this -"
Consider working inside a REPL within an IDE, or if that isn't possible then using a text editor that matches parens for you.
problem formulation
Informally speaking, I want to write a function which, taking as input a function that generates binary factorizations and an element (usually neutral), creates an arbitrary length factorization generator. To be more specific, let us first define the function nfoldr in Clojure.
(defn nfoldr [f e]
(fn rec [n]
(fn [s]
(if (zero? n)
(if (empty? s) e)
(if (seq s)
(if-some [x ((rec (dec n)) (rest s))]
(f (list (first s) x))))))))
Here nil is used with the meaning "undefined output, input not in function's domain". Additionally, let us view the inverse relation of a function f as a set-valued function defining inv(f)(y) = {x | f(x) = y}.
I want to define a function nunfoldr such that inv(nfoldr(f , e)(n)) = nunfoldr(inv(f) , e)(n) when for every element y inv(f)(y) is finite, for each binary function f, element e and natural number n.
Moreover, I want the factorizations to be generated as lazily as possible, in a 2-dimensional sense of laziness. My goal is that, when getting some part of a factorization for the first time, there does not happen (much) computation needed for next parts or next factorizations. Similarly, when getting one factorization for the first time, there does not happen computation needed for next ones, whereas all the previous ones get in effect fully realized.
In an alternative formulation we can use the following longer version of nfoldr, which is equivalent to the shorter one when e is a neutral element.
(defn nfoldr [f e]
(fn [n]
(fn [s]
(if (zero? n)
(if (empty? s) e)
((fn rec [n]
(fn [s]
(if (= 1 n)
(if (and (seq s) (empty? (rest s))) (first s))
(if (seq s)
(if-some [x ((rec (dec n)) (rest s))]
(f (list (first s) x)))))))
n)))))
a special case
This problem is a generalization of the problem of generating partitions described in that question. Let us see how the old problem can be reduced to the current one. We have for every natural number n:
npt(n) = inv(nconcat(n)) = inv(nfoldr(concat2 , ())(n)) = nunfoldr(inv(concat2) , ())(n) = nunfoldr(pt2 , ())(n)
where:
npt(n) generates n-ary partitions
nconcat(n) computes n-ary concatenation
concat2 computes binary concatenation
pt2 generates binary partitions
So the following definitions give a solution to that problem.
(defn generate [step start]
(fn [x] (take-while some? (iterate step (start x)))))
(defn pt2-step [[x y]]
(if (seq y) (list (concat x (list (first y))) (rest y))))
(def pt2-start (partial list ()))
(def pt2 (generate pt2-step pt2-start))
(def npt (nunfoldr pt2 ()))
I will summarize my story of solving this problem, using the old one to create example runs, and conclude with some observations and proposals for extension.
solution 0
At first, I refined/generalized the approach I took for solving the old problem. Here I write my own versions of concat and map mainly for a better presentation and, in the case of concat, for some added laziness. Of course we can use Clojure's versions or mapcat instead.
(defn fproduct [f]
(fn [s]
(lazy-seq
(if (and (seq f) (seq s))
(cons
((first f) (first s))
((fproduct (rest f)) (rest s)))))))
(defn concat' [s]
(lazy-seq
(if (seq s)
(if-let [x (seq (first s))]
(cons (first x) (concat' (cons (rest x) (rest s))))
(concat' (rest s))))))
(defn map' [f]
(fn rec [s]
(lazy-seq
(if (seq s)
(cons (f (first s)) (rec (rest s)))))))
(defn nunfoldr [f e]
(fn rec [n]
(fn [x]
(if (zero? n)
(if (= e x) (list ()) ())
((comp
concat'
(map' (comp
(partial apply map)
(fproduct (list
(partial partial cons)
(rec (dec n))))))
f)
x)))))
In an attempt to get inner laziness we could replace (partial partial cons) with something like (comp (partial partial concat) list). Although this way we get inner LazySeqs, we do not gain any effective laziness because, before consing, most of the computation required for fully realizing the rest part takes place, something that seems unavoidable within this general approach. Based on the longer version of nfoldr, we can also define the following faster version.
(defn nunfoldr [f e]
(fn [n]
(fn [x]
(if (zero? n)
(if (= e x) (list ()) ())
(((fn rec [n]
(fn [x] (println \< x \>)
(if (= 1 n)
(list (list x))
((comp
concat'
(map' (comp
(partial apply map)
(fproduct (list
(partial partial cons)
(rec (dec n))))))
f)
x))))
n)
x)))))
Here I added a println call inside the main recursive function to get some visualization of eagerness. So let us demonstrate the outer laziness and inner eagerness.
user=> (first ((npt 5) (range 3)))
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
(() () () () (0 1 2))
user=> (ffirst ((npt 5) (range 3)))
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
()
solution 1
Then I thought of a more promising approach, using the function:
(defn transpose [s]
(lazy-seq
(if (every? seq s)
(cons
(map first s)
(transpose (map rest s))))))
To get the new solution we replace the previous argument in the map' call with:
(comp
(partial map (partial apply cons))
transpose
(fproduct (list
repeat
(rec (dec n)))))
Trying to get inner laziness we could replace (partial apply cons) with #(cons (first %) (lazy-seq (second %))) but this is not enough. The problem lies in the (every? seq s) test inside transpose, where checking a lazy sequence of factorizations for emptiness (as a stopping condition) results in realizing it.
solution 2
A first way to tackle the previous problem that came to my mind was to use some additional knowledge about the number of n-ary factorizations of an element. This way we can repeat a certain number of times and use only this sequence for the stopping condition of transpose. So we will replace the test inside transpose with (seq (first s)), add an input count to nunfoldr and replace the argument in the map' call with:
(comp
(partial map #(cons (first %) (lazy-seq (second %))))
transpose
(fproduct (list
(partial apply repeat)
(rec (dec n))))
(fn [[x y]] (list (list ((count (dec n)) y) x) y)))
Let us turn to the problem of partitions and define:
(defn npt-count [n]
(comp
(partial apply *)
#(map % (range 1 n))
(partial comp inc)
(partial partial /)
count))
(def npt (nunfoldr pt2 () npt-count))
Now we can demonstrate outer and inner laziness.
user=> (first ((npt 5) (range 3)))
< (0 1 2) >
(< (0 1 2) >
() < (0 1 2) >
() < (0 1 2) >
() < (0 1 2) >
() (0 1 2))
user=> (ffirst ((npt 5) (range 3)))
< (0 1 2) >
()
However, the dependence on additional knowledge and the extra computational cost make this solution unacceptable.
solution 3
Finally, I thought that in some crucial places I should use a kind of lazy sequences "with a non-lazy end", in order to be able to check for emptiness without realizing. An empty such sequence is just a non-lazy empty list and overall they behave somewhat like the lazy-conss of the early days of Clojure. Using the definitions given below we can reach an acceptable solution, which works under the assumption that always at least one of the concat'ed sequences (when there is one) is non-empty, something that holds in particular when every element has at least one binary factorization and we are using the longer version of nunfoldr.
(def lazy? (partial instance? clojure.lang.IPending))
(defn empty-eager? [x] (and (not (lazy? x)) (empty? x)))
(defn transpose [s]
(lazy-seq
(if-not (some empty-eager? s)
(cons
(map first s)
(transpose (map rest s))))))
(defn concat' [s]
(if-not (empty-eager? s)
(lazy-seq
(if-let [x (seq (first s))]
(cons (first x) (concat' (cons (rest x) (rest s))))
(concat' (rest s))))
()))
(defn map' [f]
(fn rec [s]
(if-not (empty-eager? s)
(lazy-seq (cons (f (first s)) (rec (rest s))))
())))
Note that in this approach the input function f should produce lazy sequences of the new kind and the resulting n-ary factorizer will also produce such sequences. To take care of the new input requirement, for the problem of partitions we define:
(defn pt2 [s]
(lazy-seq
(let [start (list () s)]
(cons
start
((fn rec [[x y]]
(if (seq y)
(lazy-seq
(let [step (list (concat x (list (first y))) (rest y))]
(cons step (rec step))))
()))
start)))))
Once again, let us demonstrate outer and inner laziness.
user=> (first ((npt 5) (range 3)))
< (0 1 2) >
< (0 1 2) >
(< (0 1 2) >
() < (0 1 2) >
() < (0 1 2) >
() () (0 1 2))
user=> (ffirst ((npt 5) (range 3)))
< (0 1 2) >
< (0 1 2) >
()
To make the input and output use standard lazy sequences (sacrificing a bit of laziness), we can add:
(defn lazy-end->eager-end [s]
(if (seq s)
(lazy-seq (cons (first s) (lazy-end->eager-end (rest s))))
()))
(defn eager-end->lazy-end [s]
(lazy-seq
(if-not (empty-eager? s)
(cons (first s) (eager-end->lazy-end (rest s))))))
(def nunfoldr
(comp
(partial comp (partial comp eager-end->lazy-end))
(partial apply nunfoldr)
(fproduct (list
(partial comp lazy-end->eager-end)
identity))
list))
observations and extensions
While creating solution 3, I observed that the old mechanism for lazy sequences in Clojure might not be necessarily inferior to the current one. With the transition, we gained the ability to create lazy sequences without any substantial computation taking place but lost the ability to check for emptiness without doing the computation needed to get one more element. Because both of these abilities can be important in some cases, it would be nice if a new mechanism was introduced, which would combine the advantages of the previous ones. Such a mechanism could use again an outer LazySeq thunk, which when forced would return an empty list or a Cons or another LazySeq or a new LazyCons thunk. This new thunk when forced would return a Cons or perhaps another LazyCons. Now empty? would force only LazySeq thunks while first and rest would also force LazyCons. In this setting map could look like this:
(defn map [f s]
(lazy-seq
(if (empty? s) ()
(lazy-cons
(cons (f (first s)) (map f (rest s)))))))
I have also noticed that the approach taken from solution 1 onwards lends itself to further generalization. If inside the argument in the map' call in the longer nunfoldr we replace cons with concat, transpose with some implementation of Cartesian product and repeat with another recursive call, we can then create versions that "split at different places". For example, using the following as the argument we can define a nunfoldm function that "splits in the middle" and corresponds to an easy-to-imagine nfoldm. Note that all "splitting strategies" are equivalent when f is associative.
(comp
(partial map (partial apply concat))
cproduct
(fproduct (let [n-half (quot n 2)]
(list (rec n-half) (rec (- n n-half))))))
Another natural modification would allow for infinite factorizations. To achieve this, if f generated infinite factorizations, nunfoldr(f , e)(n) should generate the factorizations in an order of type ω, so that each one of them could be produced in finite time.
Other possible extensions include dropping the n parameter, creating relational folds (in correspondence with the relational unfolds we consider here) and generically handling algebraic data structures other than sequences as input/output. This book, which I have just discovered, seems to contain valuable relevant information, given in a categorical/relational language.
Finally, to be able to do this kind of programming more conveniently, we could transfer it into a point-free, algebraic setting. This would require constructing considerable "extra machinery", in fact almost making a new language. This paper demonstrates such a language.
I am trying to complete the Hackerrank Maximum Element challenge found here: https://www.hackerrank.com/challenges/maximum-element/problem
My solution produces the correct output, but times out on the final test cases beginning with #17.
Initially, I used a list and loop/recur to get my answer:
(defn get-query []
(map #(Integer/parseInt %) (clojure.string/split (read-line) #" ")))
(defn stack-stepper [query stack]
(condp = (first query)
1 (conj stack (second query))
2 (rest stack)
3 (do (println (apply max stack)) stack)))
(loop [stack '()
queries-left (Integer/parseInt (read-line))]
(if (> queries-left 0)
(recur (stack-stepper (get-query) stack) (dec queries-left))))
After some research and feedback from other channels, I tried a vector instead of a list, and reduce instead of loop/recur, but the results were the same.
(defn get-query []
(map #(Integer/parseInt %) (clojure.string/split (read-line) #" ")))
(defn get-queries []
(loop [queries []
queries-left (Integer/parseInt (read-line))]
(if (= queries-left 0)
queries
(recur (conj queries (get-query)) (dec queries-left)))))
(defn stack-stepper [stack query]
(condp = (first query)
1 (conj stack (second query))
2 (pop stack)
3 (do (println (apply max stack)) stack)))
(reduce stack-stepper [] (get-queries))
I am still new to FP and Clojure and I would really like to understand what I am missing. I greatly appreciate your time and help!
HackerRank problems are often very demanding from a performance point of view.
The obvious thing to try first is using a transient vector so see if that helps. I tried this:
(let [in (clojure.string/split (slurp *in*) #"\s")
tests (first in)
input-data (map #(Integer/parseInt %) (rest in))]
(loop [v (transient [])
d input-data]
(when (seq d)
(condp = (first d)
1 (recur (conj! v (second d)) (drop 2 d))
2 (recur (pop! v) (rest d))
3 (let [pv (persistent! v)] (println (apply max pv)) (recur (transient pv) (rest d)))))))
If failed at the same point as your solution. Clearly they're looking for something cleverer than that.
The obvious bottleneck is the calculation of the max value on the current stack, which gets re-calculated each time. We can instead save the previous max value on the stack, and recover it as the current max value when we pop the stack:
(defn peek! [tvec] (get tvec (dec (count tvec))))
(let [in (clojure.string/split (slurp *in*) #"\s")
tests (first in)
input-data (map #(Integer/parseInt %) (rest in))]
(loop [v (transient [])
m 0
d input-data]
(when (seq d)
(condp = (first d)
1 (let [snd (second d)
max-now (max m snd)]
(recur (conj! v {:val snd :max-prev m}) max-now (drop 2 d)))
2 (let [popped (peek! v)
max (if popped (:max-prev popped) 0)]
(recur (pop! v) max (rest d)))
3 (do
(println m)
(recur v m (rest d)))))))
Which puts me at rank 1 on the leaderboard :)
I have a function that receives a vector and sum all the elements.
(def rec
(fn [numbers acc]
(if (empty? numbers)
acc
(recur (rest numbers) (+ acc (first numbers))))))
(prn (rec [1 2 3] 0))
But instead of calling the function "+" I want to pass the operation as parameter, it means, I want to pass a function as parameter and then call the function.
I tried:
(def rec
(fn [f numbers acc]
(if (empty? numbers)
acc
(recur (rest numbers) (f acc (first numbers))))))
(prn (rec + [4 2 1] 0))
But it does not work, I know there are better ways to sum numbers in a vector, but I'm starting with functional, so it is important to do this kind of exercise.
Thanks in advance.
You need to recur with the same arguments as the parameter vector, in this case:
(recur f (rest numbers) (f acc (first numbers))))))
(btw, it's standard to use defn for defining functions, (defn f[x] ... ) is more concise than (def f (fn [x] ...)))
More ideomatic Clojure would be using reduce here, I think
(defn rec [f numbers acc]
(reduce f acc numbers))
(rec + [1 2 3] 0)
# 6
Factoring
In your
(def rec
(fn [numbers acc]
(if (empty? numbers)
acc
(recur (rest numbers) (+ acc (first numbers))))))
... you can push the accumulator acc beneath the surface of rec:
(defn rec [numbers]
(loop [ns numbers, acc 0]
(if (empty? ns)
acc
(recur (rest ns) (+ acc (first ns))))))
For example,
(rec + [1 3])
; 4
If you want to pass the operation as a parameter, the convention is that calling it with no arguments gives its identity: the value which returns the other argument when it is applied to two.
Thus
(+) ; => 0
(*) ; => 1
So we can write your parameterized rec as
(defn rec [op numbers]
(loop [ns numbers, acc (op)]
(if (empty? ns)
acc
(recur (rest ns) (op acc (first ns))))))
This is almost how reduce works, though not quite as elegantly, IMO.
In clojure, this is valid:
(loop [a 5]
(if (= a 0)
"done"
(recur (dec a))))
However, this is not:
(let [a 5]
(if (= a 0)
"done"
(recur (dec a))))
So I'm wondering: why are loop and let separated, given the fact they both (at least conceptually) introduce lexical bindings? That is, why is loop a recur target while let is not?
EDIT: originally wrote "loop target" which I noticed is incorrect.
Consider the following example:
(defn pascal-step [v n]
(if (pos? n)
(let [l (concat v [0])
r (cons 0 v)]
(recur (map + l r) (dec n)))
v))
This function calculates n+mth line of pascal triangle by given mth line.
Now, imagine, that let is a recur target. In this case I won't be able to recursively call the pascal-step function itself from let binding using recur operator.
Now let's make this example a little bit more complex:
(defn pascal-line [n]
(loop [v [1]
i n]
(if (pos? i)
(let [l (concat v [0])
r (cons 0 v)]
(recur (map + l r) (dec i)))
v)))
Now we're calculating nth line of a pascal triangle. As you can see, I need both loop and let here.
This example is quite simple, so you may suggest removing let binding by using (concat v [0]) and (cons 0 v) directly, but I'm just showing you the concept. There may be a more complex examples where let inside a loop is unavoidable.