I have looked around already and I can't see to find a way to do this.
I simply want to print a tree out, level by level.
So '[a [b c]] would print out:
a
b c
Provided I understood you correctly, this code should work:
(defn bf
[root]
(if (vector? root)
(let [roots (filterv (complement vector?) root)
children (filterv vector? root)]
(if-not (empty? children)
(into [roots] (mapcat bf children))
[roots]))
root))
It's not lazy, it returns a vector of vectors containing the nodes, e.g.
(bf ['a ['b 'c] 'd ['e 'f]]) => [[a d] [b c] [e f]]
Printing out:
(let [xs (bf ['a ['b 'c] 'd ['e 'f]])]
(doseq [line (map str/join xs)] ; you need to require [clojure.string :as str]
(println line)))
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.
New to Clojure.
Input - ["a" ["b" "c"] "d"]
Expected output - ["a" "b" "c" "d"]
What I'm trying to do - create an empty vector ('result'), then do two doseq's on the input collection to fill 'result' up, finally return the filled up 'result'. However the function returns an empty vector. What am I doing wrong?
(flat ["a" ["b" "c"] "d"])
(defn flat [arr]
(let [result []]
(doseq [element arr]
(if (coll? element) (doseq [element2 element] (conj result element2))
(conj result element))) result))
As others have pointed out, you can't mutate result. If you really did want to implement your function with mutation you'd need an atom, which you can mutate with swap!
(defn flat [arr]
(let [result (atom [])]
(doseq [element arr]
(if (coll? element) (doseq [element2 element] (swap! result conj element2))
(swap! result conj element)))
#result))
Notice however, that this only gives you a single level of flattening, which you can accomplish simply with
(apply concat <your seq>)
A simple, recursive, multilevel flatten is:
(defn flat [x] (if (coll? x) (mapcat flat x) [x]))
#!/usr/bin/env boot
(def inp ["a" "b" ["c" "d" "e" ["f" "g" "h"] "i"] "j" "k"])
(defn flat
([xs] (flat xs []))
([xs acc]
(if (empty? xs) acc
(if (vector? (first xs))
(flat (rest xs) (flat (first xs) acc))
(recur (rest xs) (conj acc (first xs)))))))
(println (flat inp)) ;[a b c d e f g h i j k]
The basic idea is to check if first element is a list, if so recurse that list (flat (first xs) acc) adding each element to the accumulator and then proceed with rest of the list giving (flat (rest xs) (flat (first xs) acc)). Else just recur individual elements.
We can use other constructs like let, cond as well.
If you want a fast version of flatten, see clojure.core.reducers/flatten.
(require '[clojure.core.reducers :as r])
(defn rflatten [coll] (into [] (r/flatten coll)))
I have these functions:
(def i (atom {})) ;incremented/calculated file stats
(defn updatei [n fic fos]
(swap! i conj {(keyword n)[fic fos]}))
(defn calcfo [fo fi fis]
(if-let [n (get #i fo)] ;find a previous record?
(let [fic (inc (first n)), fos (+ fis (second n))] ;increment the stats
(updatei fo fic fos))
(let [fic 1, fos fis] ;if not then: first calc recorded
(updatei fo fic fos))))
How could I write (updatei fo fic fos) once, instead of having it listed twice in the function? Is there a secret or-let I am unaware of?
-Hypothetical code-
(defn calcfo [fo fi fis]
(if-let [n (get #i fo)] ;find a previous record?
(let [fic (inc (first n)), fos (+ fis (second n))] ;increment the stats
(or-let [fic 1, fos fis] ;if not then: first calc recorded
(updatei fo fic fos)))))
Or am I thinking of this too imperatively versus functionally?
EDIT:
I decided this made the most sense to me:
(defn calcfo [fo fis]
(apply updatei fo
(if-let [[rfc rfos] (get #i fo)] ;find a previous record?
[(inc rfc) (+ rfos fis)] ;increment the stats
[1 fis]) ;if not then: first calc recorded
))
Thanks for the great answers!
A rearrangement might help
(defn calcfo [fo fi fis]
(apply updatei fo
(if-let [n (get #i fo)]
[(inc (first n)), (+ fis (second n))]
[1, fis] )))
What about using an if and then destructuring? Here's an approach:
(defn calcfo [fo fi fis]
(let [n (get #i fo) ;find a previous record?
[fic fos] (if n
[(-> n first inc) (-> n second (+ fis))] ;increment the stats
[1 fis])] ;if not then: first calc recorded
(updatei fo fic fos)))
The argument fi doesn't seem to be being used so maybe you could remove it from the argument list.
(defn calcfo [fo fis] ,,,)
The usage of first and second could also be avoided with the use of destructuring when binding n in the let form:
(defn calcfo [fo fis]
(let [[x y & _] (get #i fo)
[fic fos] (if x [(inc x) (+ fis y)] [1 fis])]
(updatei fo fic fos)))
I think you would sidestep the whole problem and make your code better if you rewrote updatei, something like:
(defn- safe+ [a b]
(if a (if b (+ a b) a) b))
(defn updatei [n fic fos]
(swap! i update-in [(keyword n)] #(vector (safe+ fic (first %)) (safe+ fos (second %)))))
There may be a better way to write that code, but the basic idea is to use update-in to either store the new values (if nothing was stored for that key before), or combine them with what is already there.
I have a seq, (def coll '([:a 20] [:b 30] [:c 50] [:d 90]))
I want to iterate through the seq, and modify only the first element that matches a predicate.
The predicate (def pred (fn [[a b]] (> b 30)))
(f pred (fn [[a b]] [a (+ b 2)]) coll) => ([:a 20] [:b 30] [:c 52] [:d 90])
f is the fn I want, which takes a pred, and a fn to apply to the first elem which matches the pred. All the rest of the elems are not modified and returned in the seq.
What is the idiomatic way to do the above?
One possible way is to split the collection with split-with, apply the function f to the first element of the second collection returned by split-with, and concat the elements together again.
(defn apply-to-first [pred f coll]
(let [[h t] (split-with (complement pred) coll)]
(concat h (list (f (first t))) (rest t))))
Note that the pred function in your example should probably look like this:
(def pred #(> (second %) 30))
As with most problems, there is a number of ways to solve it. This is but one of them.
If you're running Clojure 1.5, give this a try:
(reduce
(fn [acc [a b]]
(if (pred b)
(reduced (concat (:res acc) [[a (+ b 2)]] (rest (:coll acc))))
(assoc acc
:res (conj (:res acc) [a b])
:coll (rest (:coll acc)))))
{:coll coll :res []}
coll)
;; ([:a 20] [:b 30] [:c 52] [:d 90])
The key in this algorithm is the use of the reduced (note the 'd') function - it essentially tells reduce to halt the iteration and return the result. From its doc string:
-------------------------
clojure.core/reduced
([x])
Wraps x in a way such that a reduce will terminate with the value x
The code is a bit terse, but it should give you the basic idea.
Hope this helps.
This function is not hard to write recursively "from scratch". Not only is this a good learning exercise, it also produces the best solution: it is as lazy as possible, and does the absolute minimum amount of computation. So far, only one answer to this question is lazy, and that one calls pred twice on all the items before the update occurs: once in the take-while, and once in the drop-while, parts of split-with.
(defn update-first [pred f coll]
(lazy-seq
(when-let [coll (seq coll)]
(if (pred (first coll))
(cons (f (first coll))
(rest coll))
(cons (first coll)
(update-first pred f (rest coll)))))))
To keep it straightforward: find first element, find its index and use assoc to "update" the element at index:
(let [e (first (filter pred coll))
ind (.indexOf coll e)]
(assoc (vec coll) ind ((fn [[a b]] [a (+ b 2)]) e) ))
Dominic's note about pred applies:
(def pred #(> (second %) 30))
I have a sequence s and a list of indexes into this sequence indexes. How do I retain only the items given via the indexes?
Simple example:
(filter-by-index '(a b c d e f g) '(0 2 3 4)) ; => (a c d e)
My usecase:
(filter-by-index '(c c# d d# e f f# g g# a a# b) '(0 2 4 5 7 9 11)) ; => (c d e f g a b)
You can use keep-indexed:
(defn filter-by-index [coll idxs]
(keep-indexed #(when ((set idxs) %1) %2)
coll))
Another version using explicit recur and lazy-seq:
(defn filter-by-index [coll idxs]
(lazy-seq
(when-let [idx (first idxs)]
(if (zero? idx)
(cons (first coll)
(filter-by-index (rest coll) (rest (map dec idxs))))
(filter-by-index (drop idx coll)
(map #(- % idx) idxs))))))
make a list of vectors containing the items combined with the indexes,
(def with-indexes (map #(vector %1 %2 ) ['a 'b 'c 'd 'e 'f] (range)))
#'clojure.core/with-indexes
with-indexes
([a 0] [b 1] [c 2] [d 3] [e 4] [f 5])
filter this list
lojure.core=> (def filtered (filter #(#{1 3 5 7} (second % )) with-indexes))
#'clojure.core/filtered
clojure.core=> filtered
([b 1] [d 3] [f 5])
then remove the indexes.
clojure.core=> (map first filtered)
(b d f)
then we thread it together with the "thread last" macro
(defn filter-by-index [coll idxs]
(->> coll
(map #(vector %1 %2)(range))
(filter #(idxs (first %)))
(map second)))
clojure.core=> (filter-by-index ['a 'b 'c 'd 'e 'f 'g] #{2 3 1 6})
(b c d g)
The moral of the story is, break it into small independent parts, test them, then compose them into a working function.
The easiest solution is to use map:
(defn filter-by-index [coll idx]
(map (partial nth coll) idx))
I like Jonas's answer, but neither version will work well for an infinite sequence of indices: the first tries to create an infinite set, and the latter runs into a stack overflow by layering too many unrealized lazy sequences on top of each other. To avoid both problems you have to do slightly more manual work:
(defn filter-by-index [coll idxs]
((fn helper [coll idxs offset]
(lazy-seq
(when-let [idx (first idxs)]
(if (= idx offset)
(cons (first coll)
(helper (rest coll) (rest idxs) (inc offset)))
(helper (rest coll) idxs (inc offset))))))
coll idxs 0))
With this version, both coll and idxs can be infinite and you will still have no problems:
user> (nth (filter-by-index (range) (iterate #(+ 2 %) 0)) 1e6)
2000000
Edit: not trying to single out Jonas's answer: none of the other solutions work for infinite index sequences, which is why I felt a solution that does is needed.
I had a similar use case and came up with another easy solution. This one expects vectors.
I've changed the function name to match other similar clojure functions.
(defn select-indices [coll indices]
(reverse (vals (select-keys coll indices))))
(defn filter-by-index [seq idxs]
(let [idxs (into #{} idxs)]
(reduce (fn [h [char idx]]
(if (contains? idxs idx)
(conj h char) h))
[] (partition 2 (interleave seq (iterate inc 0))))))
(filter-by-index [\a \b \c \d \e \f \g] [0 2 3 4])
=>[\a \c \d \e]
=> (defn filter-by-index [src indexes]
(reduce (fn [a i] (conj a (nth src i))) [] indexes))
=> (filter-by-index '(a b c d e f g) '(0 2 3 4))
[a c d e]
I know this is not what was asked, but after reading these answers, I realized in my own personal use case, what I actually wanted was basically filtering by a mask.
So here was my take. Hopefully this will help someone else.
(defn filter-by-mask [coll mask]
(filter some? (map #(if %1 %2) mask coll)))
(defn make-errors-mask [coll]
(map #(nil? (:error %)) coll))
Usage
(let [v [{} {:error 3} {:ok 2} {:error 4 :yea 7}]
data ["one" "two" "three" "four"]
mask (make-errors-mask v)]
(filter-by-mask data mask))
; ==> ("one" "three")