Can someone explain meaning and difference of keep function from simple map(array, fun)
var array = [1, 2, 3, 4];
var even = function (x) { return x % 2 === 0; };
array.map(even);
Description of keep function in Closure docs
Look at this example from the docs you linked:
user> (keep #(if (odd? %) %) (range 10))
(1 3 5 7 9)
user> (map #(if (odd? %) %) (range 10))
(nil 1 nil 3 nil 5 nil 7 nil 9)
As you can see keep does not include nil values in the resulting list - map does.
Related
I am struggling to find a function that will find a happy-medium between the get function and nth. I have been doing a lot of research on these sequence type functions, does anyone know a work around for this or know a function that performs as such?
I need nth's ability to grab sublists:
=> (nth '(1 (2 3) 4 5) 1)
(2 3)
=> (get 1 '(1 (2 3) 4 5))
nil
And I need get's ability to return "nil" when out of range:
=> (get -1 '(1 (2 3) 4 5))
nil
=> (nth '(1 (2 3) 4 5) -1)
Execution error (IndexOutOfBoundsException) at user/eval149 (REPL:1).
null
I need this code for a recursive sub-seq function:
(defn sub-seq [coll i j]
(nth coll i)
(if (< i (+ i j))
(sub-seq coll (inc' i) j)
)
)
(The sub-seq function is supposed to return 'j' elements starting at position 'i'.)
Here are some sample outputs for what I am trying to write:
=> (sub-seq '(1 2 (3 4) (5 (6 7))) 1 2))
(2 (3 4))
=> (sub-seq '(1 2 3 4 5 6 7) 2 4)
(3 4 5 6)
I finally got my function to work, thank you all for your help:
(defn sub-seq [coll i j]
(conj
(list*
(nth coll i nil)
(if (> j 1)
(sub-seq coll (+ i 1) (- j 1))))))
nth takes an optional third argument not-found. You can use it to provide a default value if your index is out of bounds:
user=> (nth '(1 (2 3) 4 5) -1)
Execution error (IndexOutOfBoundsException) at user/eval1 (REPL:1).
null
user=> (nth '(1 (2 3) 4 5) -1 nil)
nil
If you had a vector, you could use subvec directly:
(let [s [1 2 3 4 5 6 7]]
(subvec s 2 6))
If you have a sequence then you could write:
(defn subsequence [coll start n]
(->> coll
(drop start)
(take n)))
(subsequence '(1 2 (3 4) (5 (6 7))) 1 2)
=> (2 (3 4))
(subsequence '(1 2 3 4 5 6 7) 2 4)
=> (3 4 5 6)
Side note: When writing a Clojure program, often you can solve your problem more simply with sequence processing. Sometimes recursive algorithms are necessary, but often you can get by with Clojure's rich set of functions that operate on sequences.
You are misunderstanding get. It works on associative collections like maps and vectors. Consider:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(dotest
(let [data-list '(1 (2 3) 4 5)
data-vec (vec data-list) ]
(spyx (nth data-list 1))
(spyx (nth data-vec 1))
(spyx (get data-list 1))
(spyx (get data-vec 1))
))
with result
(nth data-list 1) => (2 3)
(nth data-vec 1) => (2 3)
(get data-list 1) => nil
(get data-vec 1) => (2 3)
Since a Clojure list is not associative like a map, list should not be used at all with lists. Ideally, get would throw an exception when passed a list argument to indicate it doesn't work with them.
For your specific goal, maybe just do something like:
(take j
(drop i coll))
For additional documentation please review here.
I know this is a recurring question (here, here, and more), and I know that the problem is related to creating lazy sequencies, but I can't see why it fails.
The problem: I had written a (not very nice) quicksort algorithm to sort strings that uses loop/recur. But applied to 10000 elements, I get a StackOverflowError:
(defn qsort [list]
(loop [[current & todo :as all] [list] sorted []]
(cond
(nil? current) sorted
(or (nil? (seq current)) (= (count current) 1)) (recur todo (concat sorted current))
:else (let [[pivot & rest] current
pred #(> (compare pivot %) 0)
lt (filter pred rest)
gte (remove pred rest)
work (list* lt [pivot] gte todo)]
(recur work sorted)))))
I used in this way:
(defn tlfnum [] (str/join (repeatedly 10 #(rand-int 10))))
(defn tlfbook [n] (repeatedly n #(tlfnum)))
(time (count (qsort (tlfbook 10000))))
And this is part of the stack trace:
[clojure.lang.LazySeq seq "LazySeq.java" 49]
[clojure.lang.RT seq "RT.java" 521]
[clojure.core$seq__4357 invokeStatic "core.clj" 137]
[clojure.core$concat$fn__4446 invoke "core.clj" 706]
[clojure.lang.LazySeq sval "LazySeq.java" 40]
[clojure.lang.LazySeq seq "LazySeq.java" 49]
[clojure.lang.RT seq "RT.java" 521]
[clojure.core$seq__4357 invokeStatic "core.clj" 137]]}
As far as I know, loop/recur performs tail call optimization, so no stack is used (is, in fact, an iterative process written using recursive syntax).
Reading other answers, and because of the stack trace, I see there's a problem with concat and adding a doall before concat solves the stack overflow problem. But... why?
Here's part of the code for the two-arity version of concat.
(defn concat [x y]
(lazy-seq
(let [s (seq x)]
,,,))
)
Notice that it uses two other functions, lazy-seq, and seq. lazy-seq is a bit like a lambda, it wraps some code without executing it yet. The code inside the lazy-seq block has to result in some kind of sequence value. When you call any sequence operation on the lazy-seq, then it will first evaluate the code ("realize" the lazy seq), and then perform the operation on the result.
(def lz (lazy-seq
(println "Realizing!")
'(1 2 3)))
(first lz)
;; prints "realizing"
;; => 1
Now try this:
(defn lazy-conj [xs x]
(lazy-seq
(println "Realizing" x)
(conj (seq xs) x)))
Notice that it's similar to concat, it calls seq on its first argument, and returns a lazy-seq
(def up-to-hundred
(reduce lazy-conj () (range 100)))
(first up-to-hundred)
;; prints "Realizing 99"
;; prints "Realizing 98"
;; prints "Realizing 97"
;; ...
;; => 99
Even though you asked for only the first element, it still ended up realizing the whole sequence. That's because realizing the outer "layer" results in calling seq on the next "layer", which realizes another lazy-seq, which again calls seq, etc. So it's a chain reaction that realizes everything, and each step consumes a stack frame.
(def up-to-ten-thousand
(reduce lazy-conj () (range 10000)))
(first up-to-ten-thousand)
;;=> java.lang.StackOverflowError
You get the same problem when stacking concat calls. That's why for instance (reduce concat ,,,) is always a smell, instead you can use (apply concat ,,,) or (into () cat ,,,).
Other lazy operators like filter and map can exhibit the exact same problem. If you really have a lot of transformation steps over a sequence consider using transducers instead.
;; without transducers: many intermediate lazy seqs and deep call stacks
(->> my-seq
(map foo)
(filter bar)
(map baz)
,,,)
;; with transducers: seq processed in a single pass
(sequence (comp
(map foo)
(filter bar)
(map baz))
my-seq)
Arne had a good answer (and, in fact, I'd never noticed cat before!). If you want a simpler solution, you can use the glue function from the Tupelo library:
Gluing Together Like Collections
The concat function can sometimes have rather surprising results:
(concat {:a 1} {:b 2} {:c 3} )
;=> ( [:a 1] [:b 2] [:c 3] )
In this example, the user probably meant to merge the 3 maps into one. Instead, the three maps were mysteriously converted into length-2 vectors, which were then nested inside another sequence.
The conj function can also surprise the user:
(conj [1 2] [3 4] )
;=> [1 2 [3 4] ]
Here the user probably wanted to get [1 2 3 4] back, but instead got a nested vector by mistake.
Instead of having to wonder if the items to be combined will be merged, nested, or converted into another data type, we provide the glue function to always combine like collections together into a result collection of the same type:
; Glue together like collections:
(is (= (glue [ 1 2] '(3 4) [ 5 6] ) [ 1 2 3 4 5 6 ] )) ; all sequential (vectors & lists)
(is (= (glue {:a 1} {:b 2} {:c 3} ) {:a 1 :c 3 :b 2} )) ; all maps
(is (= (glue #{1 2} #{3 4} #{6 5} ) #{ 1 2 6 5 3 4 } )) ; all sets
(is (= (glue "I" " like " \a " nap!" ) "I like a nap!" )) ; all text (strings & chars)
; If you want to convert to a sorted set or map, just put an empty one first:
(is (= (glue (sorted-map) {:a 1} {:b 2} {:c 3}) {:a 1 :b 2 :c 3} ))
(is (= (glue (sorted-set) #{1 2} #{3 4} #{6 5}) #{ 1 2 3 4 5 6 } ))
An Exception will be thrown if the collections to be 'glued' are not all of the same type. The allowable input types are:
all sequential: any mix of lists & vectors (vector result)
all maps (sorted or not)
all sets (sorted or not)
all text: any mix of strings & characters (string result)
I put glue into your code instead of concat and still got a StackOverflowError. So, I also replaced the lazy filter and remove with eager versions keep-if and drop-if to get this result:
(defn qsort [list]
(loop [[current & todo :as all] [list] sorted []]
(cond
(nil? current) sorted
(or (nil? (seq current)) (= (count current) 1))
(recur todo (glue sorted current))
:else (let [[pivot & rest] current
pred #(> (compare pivot %) 0)
lt (keep-if pred rest)
gte (drop-if pred rest)
work (list* lt [pivot] gte todo)]
(recur work sorted)))))
(defn tlfnum [] (str/join (repeatedly 10 #(rand-int 10))))
(defn tlfbook [n] (repeatedly n #(tlfnum)))
(def result
(time (count (qsort (tlfbook 10000)))))
-------------------------------------
Clojure 1.8.0 Java 1.8.0_111
-------------------------------------
"Elapsed time: 1377.321118 msecs"
result => 10000
((fn foo [x] (when (> x 0) (conj (foo (dec x)) x))) 5)
For this code, the result is [5 4 3 2 1]
Why isn't is [1,2,3,4,5]?
I see we do conf from result of recursive foo call with a value.
For I thought it should be 1 2 3 4 5?
Need help to understand this.
Thanks.
From the documentation of conj:
clojure.core/conj
([coll x] [coll x & xs])
conj[oin]. Returns a new collection with the xs
'added'. (conj nil item) returns (item). The 'addition' may
happen at different 'places' depending on the concrete type.
The termination condition of your function yields nil, because the test is a when. So the deepest conj call will be:
(conj nil 1)
(1) <-- a list
The next one:
(conj (conj nil 1) 2)
(2 1)
So your result will be in decreasing order because conj appends at the front for lists. If you want it in increasing order, start with an empty vector like this:
((fn foo [x] (if (> x 0) (conj (foo (dec x)) x) [])) 5)
[1 2 3 4 5]
The recursive call expands to
(conj (conj (conj (conj (conj nil 1) 2) 3) 4) 5)
;(5 4 3 2 1)
The implicit nil returned by (foo 0) puns to ().
So, I'm trying to transform each element of a vector x,in this way: x[i]--> 1-(1/x[i])
(defn change[x]
(fn [i]
(assoc x i (- 1 (/ 1 (get x i))))
)
(range 0 (* (count x) 1))
)
I'm using assoc to replace each element of the vector, I'm supposed to get a vector with the changes, but instead I'm getting a list.
For example
user> (change [21 32 23 34])
(0 1 2 3)
But I should get a vector :v
The code for the function you provided doesn't use the local anonymous function, and can be refactored greatly.
This is your original function with comments.
(defn change[x]
;; start unused anonymous
(fn [i]
(assoc x i (- 1 (/ 1 (get x i)))))
;; end unused anonymous
;; start/end gen list of ints
(range 0 (* (count x) 1)))
This is probably what you mean
(defn change [coll]
(mapv #(- 1 (/ 1 %)) coll))
And this is the output
user> (change [21 32 23 34])
;=> [20/21 31/32 22/23 33/34]
What your code does
Your original code (reformatted)
(defn change [x]
(fn [i] (assoc x i (- 1 (/ 1 (get x i)))))
(range 0 (* (count x) 1)))
evaluates and discards a function value then
returns the range.
So you can omit the fn form and reduce it to
(defn change [x]
(range 0 (* (count x) 1)))
which in turn reduces to
(defn change [x]
(range (count x)))
So, for example,
(change [:whatever :I :choose :to :put :here])
;(0 1 2 3 4 5)
I have a recursive function that spits out a hash every time it is called.
The first time it loops around the hash is:
{1 "mary", 2 "dean"}
next round spits out
{23 "ava", 4 "scout"}
and the final round returns
{3 "bina", 16 "bob"}
My function will always return the last round of data, {3 "gina", 16 "bob"}.
I'd like to, instead of spitting out the last piece of data, store them all in one giant hash so that I can compare them. After I compare them, the function should return "ava", because that is the value associated with the highest key. What's the best way to go about this?
One general pattern is to add a state argument to your recursive function and provide an arity of the funciton that adds the initial state value. Then in the base case you can do postprocessing of the state value.
Here is an example that builds a map of inputs before the first odd number to a random value:
user> (defn example
([input] (example input {})) ;; one argument recurs with default value
([input state] ;; two arg case passes the state.
(if (odd? (first input))
state
(recur (rest input)
(assoc state (first input) (rand-int 10))))))
#'user/example
And one that selects the highest value form the resulting state:
user> (example [2 4 8 9])
{8 0, 4 1, 2 4}
user> (defn example
([input] (example input {}))
([input state]
(if (odd? (first input))
(first (sort-by val state))
(recur (rest input)
(assoc state (first input) (rand-int 10))))))
#'user/example
user> (example [2 4 8 9])
[8 6]
If you have a side-effective function that spits out a sequence of values, model it as a sequence. If the sequence terminates, presumably the function then returns an invalid value.
The function
(defn ensequence [f! valid?]
(take-while valid? (repeatedly f!)))
... returns the sequence of values produced by side-effective function f!, terminating whenever the valid? test fails.
For instance,
(ensequence #(rand-int 10) #(not= 5 %))
... returns a sequence of random values in (range 10), stopping just before the first 5:
(6 9)
... for instance (your milage will vary).
To show how ensequence works in your case, we use an inverse function that turns a sequence into a function that returns its successive elements, and nil thereafter:
(defn oracle! [coll]
(let [s (atom coll)]
(fn [] (let [x (first #s)] (swap! s rest) x))))
For example,
(repeatedly 10 (oracle! (range 5)))
;(0 1 2 3 4 nil nil nil nil nil)
For your data
(def data [{1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"}])
The function
(oracle! data)
... successively returns its elements, followed by nils:
(repeatedly 10 (oracle! data))
;({1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"}
nil nil nil nil nil nil nil)
We can use ensequence on this, however it was generated, to recover the original sequence:
(ensequence (oracle! data) identity)
;({1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"})
Since nil is false and never valid here, identity is a good validity test.
Now that we have the sequence, we can do whatever we like with it. In your case, we just
concatenate the maps into a big sequence of map-entries;
find the entry with maximum key; and
take its val.
Thus:
(val (apply max-key key (reduce concat data)))
;"ava"
We should use (ensequence (oracle! data) identity) instead of the equivalent data, but it doesn't make any difference.