Clojure function in let binding - function

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.

Related

Where is the argument coming from?

You can notice the v in the lambda in the function body, where is the v coming from, what it is based on?
(define (cached-assoc xs n)
(letrec ([memo (make-vector n #f)]
[acc 0]
[f (lambda(x)
(let ([ans (vector-assoc x memo)])
(if ans
(cdr ans)
(let ([new-ans (assoc x xs)])
(begin
(vector-set! memo acc (cons x new-ans))
(set! acc (if (= (+ acc 1)) 0 (+ acc 1)))
new-ans)))))])
(lambda (v) (f v))))
The whole expression is returning a lambda as a result, and in that lambda there's a formal parameter named v. It doesn't have a value yet, you'll need to call the lambda to bind a value to v and produce a result (assuming the code is working):
((letrec ([memo (make-vector n #f)] ; notice the extra opening `(`, we want to call the returned lambda
[acc 0]
[f (lambda(x)
(let ([ans (vector-assoc x memo)])
(if ans
(cdr ans)
(let ([new-ans (assoc x xs)])
(begin
(vector-set! memo acc (cons x new-ans))
(set! acc (if (= (+ acc 1)) 0 (+ acc 1)))
new-ans)))))])
(lambda (v) (f v)))
10) ; <- the value 10 gets bound to `v`
However, your code isn't right. You are referring to variables named n and xs, but they are not defined anywhere and need a value of their own. The procedure vector-assoc doesn't exist. Also, the lambda at the end is redundant, you could simply return f, there's no need to wrap it in an additional lambda. Finally: you should define the whole expression with a name, it'll make it easier to call it.
I won't go into more details because first you need to fix the function and make it work, and is not clear at all what you want to do - but that should be a separate question.

where does racket lambda argument come from?

I asked a similar question before, I just want to make sure I understand the idea, in the -lambda(x)- line 4, what is the x, where it is coming from?
(define (cached-assoc xs n)
(letrec ([memo (make-vector n #f)]
[acc 0]
[f (lambda(x)
(let ([ans (vector-assoc x memo)])
(if ans
(cdr ans)
(let ([new-ans (assoc x xs)])
(begin
(vector-set! memo acc (cons x new-ans))
(set! acc (if (= (+ acc 1)) 0 (+ acc 1)))
new-ans)))))])
f))
Your cached-assoc procedure returns f, a function which has x as an unbound parameter. It doesn’t have a value yet, the value will be whatever you pass to it when you finally invoke it:
((cached-assoc xs n) 10)
In the above example x will be bound to 10, and xs and n will be whatever you defined for them before calling cached-assoc. I believe the source of the confusion is the fact that cached-assoc is returning a lambda, once you understand this it'll be clear.

Strange reduce behavior in Clojure

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))))

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))

How to make a Clojure function take a variable number of parameters?

I'm learning Clojure and I'm trying to define a function that take a variable number of parameters (a variadic function) and sum them up (yep, just like the + procedure). However, I don´t know how to implement such function
Everything I can do is:
(defn sum [n1, n2] (+ n1 n2))
Of course this function takes two parameteres and two parameters only. Please teach me how to make it accept (and process) an undefined number of parameters.
In general, non-commutative case you can use apply:
(defn sum [& args] (apply + args))
Since addition is commutative, something like this should work too:
(defn sum [& args] (reduce + args))
& causes args to be bound to the remainder of the argument list (in this case the whole list, as there's nothing to the left of &).
Obviously defining sum like that doesn't make sense, since instead of:
(sum a b c d e ...)
you can just write:
(+ a b c d e ....)
Yehoanathan mentions arity overloading but does not provide a direct example. Here's what he's talking about:
(defn special-sum
([] (+ 10 10))
([x] (+ 10 x))
([x y] (+ x y)))
(special-sum) => 20
(special-sum 50) => 60
(special-sum 50 25) => 75
(defn my-sum
([] 0) ; no parameter
([x] x) ; one parameter
([x y] (+ x y)) ; two parameters
([x y & more] ; more than two parameters
(reduce + (my-sum x y) more))
)
defn is a macro that makes defining functions a little simpler.
Clojure supports arity overloading in a single function object,
self-reference, and variable-arity functions using &
From http://clojure.org/functional_programming
(defn sum [& args]
(print "sum of" args ":" (apply + args)))
This takes any number of arguments and add them up.