Strange reduce behavior in Clojure - function

EDIT: This was not a problem with reduce or the function being reduced. I shadowed the clojure.core/range function.
I have a function
(defn- roundfn [[xi ci bi oi :as state] r]
(let [[xn cn bn] (newstate [xi ci bi] 0)
exfn (word<-x xn)]
[xn cn bn
(into oi
[(exfn [6 3 6 1])
(exfn [4 1 4 7])
(exfn [2 7 2 5])
(exfn [0 5 0 3])])]))
where x1,x2, and x4 are themselves vectors. x3 is a value.
When I reduce this function like
(reduce roundfn [[][] 0 []] (range 3))
or
(reduce roundfn [[][] 0 []] (vec (range 3)))
I'm receiving IndexOutOfBoundsException clojure.lang.PersistentVector.arrayFor (PersistentVector.java:107)
When I reduce this function like
(reduce roundfn [[][] 0 []] [0 1 2])
it works as expected

(Working off of this version of the source -- link to the current revision of the file mentioned in a comment on the question.)
Firstly, running your code produces the exception at my REPL in all the cases you listed, including the literal [0 1 2] case. It would be astounding if this were not the case. (NB. I use rabbit-round in place of roundfn, since that's the name of the function quoted in the question text as found in the source on GitHub.)
The problem's source is as follows (based on the link to the full source given in a comment on the question):
The function listed as roundfn in the question text is called rabbit-round in the source. The problematic call is (reduce rabbit-round [[] [] 0 []] [0 1 2]).
The reduce than calls rabbit-round with the initial arguments; therein a call to roundfn takes place (roundfn being a separate function in the original source): (roundfn [[] [] 0] 0). It is here that the exception gets thrown.
roundfn is a composition of two functions; already the first turns out to be the source of the problem: (update-counter-system [[] [] 0] 0) throws.
There's a further reduce call in there using counter-system-round as the reduction function. The exception is thrown when the latter is first applied: (counter-system-round [[] 0] 0).
Looking at counter-system-round we see that it attempts to compute (nth c round) in the first line. At this point, c is bound to [] (an empty vector) and round will be 0. Thus this call amounts to (nth [] 0) and correctly throws IndexOutOfBoundsException.

I was shadowing the clojure.core\range function with my [lower upper :as range] destructuring
Below is the version that didn't work
(generate-keystream [_ {:keys [ss] :as initmap} [lower upper :as range]]
(let [ns (conj ss [])]
(reduce rabbit-round ns (range 3))))
The following works as expected (note the change to :as rng instead of :as range)
(generate-keystream [_ {:keys [ss] :as initmap} [lower upper :as rng]]
(let [ns (conj ss [])]
(reduce rabbit-round ns (range 3))))
This explains why (range 3) and (vec (range 3)) both didn't work, but [0 1 2] did. The first two were evaluating to ([0 0] 3) and (vec ([0 0] 3)) in my original code, while [0 1 2] was evaluating to [0 1 2] and succeeding.
Lesson learned. Don't shadow functions
As a side note, this works too...
(generate-keystream [_ {:keys [ss] :as initmap} [lower upper :as range]]
(let [ns (conj ss [])]
(reduce rabbit-round ns (clojure.core/range 3))))

Related

How to reset a counter in Re-frame (ClojureScript)

This must be one of those silly/complex things that everybody founds when learning a new framework. So I have this function:
(defn display-questions-list
[]
(let [counter (atom 1)]
[:div
(doall (for [question #(rf/subscribe [:questions])]
^{:key (swap! counter inc)} [question-item (assoc question :counter #counter)])])))
The #counter atom doesn't hold any important data, it's just a "visual" counter to display the number in the list. When the page is loaded for first time, all works fine, if there are five questions the list displays (1..5), the issue is that when a question is created/edited/deleted the subscription:
#(rf/subscribe [:questions])
is called again and then of course the list is displayed but now from 6 to 11. So I need a way to reset the #counter.
You should not be using an atom for this purpose. Your code should look more like this:
(ns tst.demo.core
(:use tupelo.test)
(:require [tupelo.core :as t]))
(defn display-questions-list
[]
[:div
(let [questions #(rf/subscribe [:questions])]
(doall (for [[idx question] (t/indexed questions)]
^{:key idx}
[question-item (assoc question :counter idx) ])))])
The tupelo.core/indexed function from the Tupelo library simply prepends a zero-based index value to each item in the collection:
(t/indexed [:a :b :c :d :e]) =>
([0 :a]
[1 :b]
[2 :c]
[3 :d]
[4 :e])
The source code is pretty simple:
(defn zip-lazy
"Usage: (zip-lazy coll1 coll2 ...)
(zip-lazy xs ys zs) => [ [x0 y0 z0]
[x1 y1 z1]
[x2 y2 z2]
... ]
Returns a lazy result. Will truncate to the length of the shortest collection.
A convenience wrapper for `(map vector coll1 coll2 ...)`. "
[& colls]
(assert #(every? sequential? colls))
(apply map vector colls))
(defn indexed
"Given one or more collections, returns a sequence of indexed tuples from the collections:
(indexed xs ys zs) -> [ [0 x0 y0 z0]
[1 x1 y1 z1]
[2 x2 y2 z2]
... ] "
[& colls]
(apply zip-lazy (range) colls))
Update
Actually, the main goal of the :key metadata is to provide a stable ID value for each item in the list. Since the items may be in different orders, using the list index value is actually a React antipattern. Using a unique ID either from within the data element (i.e. a user id, etc) or just the hashcode provides a unique reference value. So, in practice your code would be better written as this:
(defn display-questions-list
[]
[:div
(doall (for [question #(rf/subscribe [:questions])]
^{:key (hash question)}
[question-item (assoc question :counter idx)]))])
Some hashcode samples:
(hash 1) => 1392991556
(hash :a) => -2123407586
(hash {:a 1, :b [2 3 4]}) => 383153859

d3 JavaScript translation to ClojureScript

I want to translate the following JavaScript into ClojureScript:
var myScale = d3.scaleLinear()
.domain([0, 10])
.range([0, 600]);
After creating this function you ought to be able to call it with a number:
myScale(3); // returns 180
My attempt is as follows:
(:require ["d3-scale" :refer (scaleLinear)])
(let [f (-> scaleLinear
(. (domain (clj->js [0 10])))
(. (range (clj->js [0 600]))))]
(f 3))
Gives:
react-dom.development.js:16545 Uncaught TypeError: module$node_modules$d3_scale$dist$d3_scale.scaleLinear.domain is not a function
scaleLinear is a function that can be called from d3. It is not something that ought to be constructed. So this is a better answer:
(-> d3
(.scaleLinear)
(.domain #js [min-y max-y])
(.range #js [1 0]))
I had trouble with the requires, so here are the relevant ones:
["d3" :as d3]
["d3-scale" :refer (scaleLinear)]
This is using shadow-cljs rather than cljsjs.
The trouble with requiring "d3" is that you pull the whole library in. So
here's another approach:
(-> (scaleLinear)
(.domain #js [min-y max-y])
(.range #js [1 0]))
It seems that whenever you are requiring something you can use it directly as it was required, and forget the interop dot.
Also (:require ["d3" :as d3]) means that you pull d3 into the local scope. js/d3 is the global scope, so just use d3 if that's what you have required. i.e. js is another 'interop' thing you can forget about.
On the other hand domain and range were not required, so interop rules need to be observed.
In conclusion - the main problem solved is that scaleLinear is a function and as such actually needs to be called!
This question was asked at the beginning of an exploration into working with d3 through React Vis - see https://medium.com/#cjmurphy/using-react-vis-from-clojurescript-787d02281f7c
With some trial and error I did get it to work. These are all equivalent:
(-> (new scaleLinear)
(.domain (clj->js [0 10]))
(.range (clj->js [0 600])))
(-> (new scaleLinear)
(. (domain (clj->js [0 10])))
(. (range (clj->js [0 600]))))
(-> (scaleLinear.)
(.domain #js [0 10])
(.range #js [0 600]))

Wrong arity of simple function in clojure

I've started to learn clojure. In my book there is following exercise:
Write a function, mapset, that works like map except the return value is a set:
(mapset inc [1 1 2 2])
; => #{2 3}
I've started with something like this:
(defn mapset
[vect]
(set vect))
The result is error
"Wrong number of args (2) passed to: core/mapset"
I tried [& args] as well.
So, the question is: how can I solve such problem?
Take a closer look at your call to mapset:
(mapset inc [1 1 2 2])
Since code is data, this "call" is just a list of three elements:
The symbol mapset
The symbol inc
The vector [1 1 2 2]
When you evaluate this code, Clojure will see that it is a list and proceed to evaluate each of the items in that list (once it determines that it isn't a special form or macro), so it will then have a new list of three elements:
The function to which the symbol core/mapset was bound
The function to which the symbol clojure.core/inc was bound
The vector [1 1 2 2]
Finally, Clojure will call the first element of the list with the rest of the elements as arguments. In this case, there are two arguments in the rest of the list, but in your function definition, you only accounted for one:
(defn mapset
[vect]
(set vect))
To remedy this, you could implement mapset as follows:
(defn mapset
[f vect]
(set (map f vect)))
Now, when you call (mapset inc [1 1 2 2]), the argument f will be found to the function clojure.core/inc, and the argument vect will be bound to the vector [1 1 2 2].
Your definition of mapset takes a single argument vect
At a minimum you need to take 2 arguments, a function and a sequence
(defn mapset [f xs] (set (map f xs)))`
But it is interesting to think about this as the composition of 2 functions also:
(def mapset (comp set map))

Clojure function in let binding

If I have a function that evaluates to a function
(defn func1 [c1 c2]
(fn [x1 x2]
...do some stuff with c1 c2 x1))
that I use elsewhere in a map or reduce, is it better to use inline
(defn func2 [x y z]
(reduce (func1 x y) z (range 20)))
or to let bind it first
(defn func2 [x y z]
(let [ffunc (func1 x y)]
(reduce ffunc z (range 20))))
In the first case I would be worried that a new function over x and y is generated each step through the reduce.
The evaluation of the function call (func1 x y) is done once in each case.
The rule for evaluating a function call in Clojure consists of evaluating all the expressions that are provided as its arguments and then invoking the function with those values.
If you define the following higher order function:
(defn plus []
(println "calling plus")
+)
And then call reduce in the following way:
(reduce (plus) [0 1 2 3])
A single calling plus is printed, showing the function plus is invoked only once.
The same thing happens when using the let form:
(let [f (plus)]
(reduce f [0 1 2 3]))
Hope it helps.

How to build a vector via a call to reduce

I'm trying to figure why this particular function isn't working as expected. I suspect from the error message that it has something to do with the way I'm creating the empty vector for the accumulator.
I have a simple function that returns a sequence of 2-element vectors:
(defn zip-with-index
"Returns a sequence in which each element is of the
form [i c] where i is the index of the element and c
is the element at that index."
[coll]
(map-indexed (fn [i c] [i c]) coll))
That works fine. The problem comes when I try to use it in another function
(defn indexes-satisfying
"Returns a vector containing all indexes of coll that satisfy
the predicate p."
[p coll]
(defn accum-if-satisfies [acc zipped]
(let [idx (first zipped)
elem (second zipped)]
(if (p elem)
(conj acc idx)
(acc))))
(reduce accum-if-satisfies (vector) (zip-with-index coll)))
It compiles, but when I attempt to use it I get an error:
user=> (indexes-satisfying (partial > 3) [1 3 5 7])
ArityException Wrong number of args (0) passed to: PersistentVector
clojure.lang.AFn.throwArity (AFn.java:437)
I can't figure out what's going wrong here. Also if there is a more 'Clojure-like' way of doing what I'm trying to do, I'm interested in hearing about that also.
The problem is probably on the else clause of accum-if-satisfies, should be just acc not (acc).
You could use filter and then map instead of reduce. Like that:
(map #(first %)
(filter #(p (second %))
(zip-with-index coll)))
You could also call map-indexed with vector instead of (fn [i c] [i c]).
The whole code would look like that:
(defn indexes-satisfying
[p coll]
(map #(first %)
(filter #(p (second %))
(map-indexed vector coll))))
As for a more Clojure-like way, you could use
(defn indexes-satisfying [pred coll]
(filterv #(pred (nth coll %))
(range (count coll))))
Use filter instead of filterv to return a lazy seq rather than a vector.
Also, you should not use defn to define inner functions; it will instead define a global function in the namespace where the inner function is defined and have subtle side effects besides that. Use letfn instead:
(defn outer [& args]
(letfn [(inner [& inner-args] ...)]
(inner ...)))
One more way to do it would be:
(defn indexes-satisfying [p coll]
(keep-indexed #(if (p %2) % nil) coll))