This question already has an answer here:
Why such implementation of partial in clojure.core
(1 answer)
Closed 6 years ago.
I'm a beginner in clojure trying to understand it at a basic level for now.
I've been experimenting with partial and how it creates closures, and to get a deeper understanding I thought I should take a peek at the source code by doing a (source partial) .
There I get
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"
:static true}
([f] f)
([f arg1]
(fn
([] (f arg1))
([x] (f arg1 x))
([x y] (f arg1 x y))
([x y z] (f arg1 x y z))
([x y z & args] (apply f arg1 x y z args))))
([f arg1 arg2]
(fn
([] (f arg1 arg2))
([x] (f arg1 arg2 x))
([x y] (f arg1 arg2 x y))
([x y z] (f arg1 arg2 x y z))
([x y z & args] (apply f arg1 arg2 x y z args))))
([f arg1 arg2 arg3]
(fn
([] (f arg1 arg2 arg3))
([x] (f arg1 arg2 arg3 x))
([x y] (f arg1 arg2 arg3 x y))
([x y z] (f arg1 arg2 arg3 x y z))
([x y z & args] (apply f arg1 arg2 arg3 x y z args))))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
I find the whole definition to be redundant as I would only write it in a "varargs" fashion, ie the last 2 lines.
Is this a readability feature or am I missing something basic here?
It's not a readability feature but a matter of performance. Stuart Sierra explained it in Clojure Don’ts: Optional Arguments with Varargs:
Variable-arity function calls have to allocate a sequence to hold the arguments, then go through apply. Timothy Baldridge did a quick performance comparison showing that calls to a function with multiple, fixed arities can be much faster than variable-arity (varargs) function calls.
In that benchmark, the varargs version with 1 argument is around an order of magnitude slower than the multi-arity version with 1 arg, and with 3 args the difference goes up to ~ 2 orders of magnitude.
This is not to say that varargs should not be used at all: Sparsely calling a fn with varargs will probably not affect performance, but it could hit you hard when calling it from a tight loop.
I find the whole definition to be redundant as I would only write it
in a "varargs" fashion,
You are quite right. All but the varargs definition are redundant. As #nberger points out, the others are there to improve performance. You will find many such in clojure core: such as map, max, and comp.
Related
I am new to Clojure and attempting to write a program that is supposed to simplify logical expressions. For example:
(or x false) => x
(or true x) => true
(or x y z) => (or x(or y z))
I believe, that I have figured out how to simplify expressions up to two arguments. However, I don't know how to simplify expressions that consist of more than two arguments. I attempted to use the nth function to try and partition the expression, but that seems to complicate things further:
(defn pre-simplify [expression n]
(cond
(= n 0) (nth expression 0)
(= n 1) (nth expression 1)
(= n 2) (nth expression 2)
(= n 3) (nth expression 3)
)
)
This is the code for the Simplify Function:
(defn simplify
([op arg1]
(cond
(and (= arg1 'true) (= op 'not)) false
(and (= arg1 'false) (= op 'not)) true
(= arg1 'true) true
(= arg1 'false) false)
;not not x -> x
)
([op arg1 arg2]
(cond
(seq? arg1) (let [arg1 (simplify op arg1)]))
(cond
(seq? arg2) (let [arg2 (simplify op arg2)]))
(cond
(= op 'or) (
(cond
(and (= arg1 'false) (= arg2 'false)) false
(or (= arg1 'true) (= arg2 'true)) true
(and (= arg1 'false) (and (not= arg2 'false) (not= arg2 'true))) arg2
(and ((not= arg1 'false) (not= arg1 'true)) (= arg2 'false)) arg1
)
)
(= op 'and') (
(cond
(and (= arg1 'true) (= arg2 'true)) true
(or (= arg1 'false) (= arg2 'false)) false
(and (= arg1 'false) (and (not= arg2 'false) (not= arg2 'true))) arg2
(and ((not= arg1 'false) (not= arg1 'true)) (= arg2 'false)) arg1
)
)
)
)
Also, I am thinking of using recursion on the rest of the list after the first two arguments. For example, this is how I am attempting to do it on more than two arguments:
([op arg1 arg2 & rest]
(simplify op (list (op arg1 (op arg2 rest))))))
This looks like homework, so let me just invite you to consider the problem from another angle.
In Clojure,
an and expression is a list (or other sequence) of expressions that starts with the symbol 'and;
an or expression is a list of expressions that starts with the
symbol 'or;
... .
An expression may also be
a symbol for an argument or other local name, or
the literal true or false.
To simplify an expression:
If it's a sequence (use seq? to test this), simplify its
arguments (all the elements of the list but the first - rest will give you those). You can use map to make the recursive call.
Then, for example, if the operator (the first element) is an and symbol,
If there are any false literals among the operands (you can use
some to test this), replace the whole expression with false.
If not, remove any true literals.
These simplifications may open the way for others. For example,
(and) is just true and
(and x) is just x.
Whatever you do, test your function as you develop it, making sure it does what you think it does. I got caught leaving out the operator in one case. And I fell over the (and x) case.
What's also up for grabs is how and how far you are expected to verify that the expression is valid.
What about unknown operators?
Are you expected to verify that symbols refer to something. If so,
how?
Notes
I'm assuming we can ignore side effects: all the expression does is
return a value.
I wouldn't replace (or x y z) with (or x (or y z)). This achieves
nothing and uses an extra token: the opposite of simplification.
Any Clojure value - a number, even a function - acts as logically
true, except false and nil, which act as logically false. Whether
this is relevant to your problem, I don't know.
If you're feeling smart, you may be able to parameterise how and
and or behave in terms of the literal you ignore (true and
false respectively), the one you conclude with, and the one you return
when there are no arguments.
The problem as whole is considered intractable. If you find an efficient solution to it, you'll be world famous!
This question already has an answer here:
Why such implementation of partial in clojure.core
(1 answer)
Closed 6 years ago.
Goal
I'm trying to understand how transducers are implemented.
Confusion
I'm looking at comp, implement here.
In particular, these lines here:
([] (f (g)))
([x] (f (g x)))
([x y] (f (g x y)))
([x y z] (f (g x y z)))
([x y z & args] (f (apply g x y z args)))))
Now, what I don't understand -- why are there five cases? Could this not have been rewritten as:
(args (f (apply g args)))
?
Is there any particular reason why the code was written as above?
Could this not have been rewritten as (args (f (apply g args)))?
Yes, but this has nothing to do with transducers.
comp performs functional composition, whether or not the function
arguments are transducers.
Many core functions, including comp, specialise simple cases for speed.
The general question, as #ClojureMostly notes, is answered here.
I'm currently writing functions with undefined number of arities and I so looked for examples in clojure.core and so on.
This is for example the definition of comp (clojure.core)
(defn comp
"Takes a set of functions and returns a fn that is the composition
of those fns. The returned fn takes a variable number of args,
applies the rightmost of fns to the args, the next
fn (right-to-left) to the result, etc."
{:added "1.0"
:static true}
([] identity)
([f] f)
([f g]
(fn
([] (f (g)))
([x] (f (g x)))
([x y] (f (g x y)))
([x y z] (f (g x y z)))
([x y z & args] (f (apply g x y z args)))))
([f g & fs]
(reduce1 comp (list* f g fs))))
As you can see, for the arities [f g], the code detail the values for 2 and 3 arguments (x y ; x, y, z) even if it could just have directly jumped to [x & args].
Is there any performance reason ? Or is it a convention ?
I suppose that calling apply could impact performance, I don't know.
We generally use at most 3D functions and composition of 2 functions in real lif, maybe it's because of that.
Thanks
Clojure's core library is implemented often in ways that are not particularly idiomatic, specifically for performance reasons. If you are writing a function that is going to be fundamental to most of the lines of code in your program, you could do the same, but in general this is not elegant or particularly advised.
I stumbled across implementation of partial function in cojure.core. It looks like this:
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"
:static true}
([f] f)
([f arg1]
(fn [& args] (apply f arg1 args)))
([f arg1 arg2]
(fn [& args] (apply f arg1 arg2 args)))
([f arg1 arg2 arg3]
(fn [& args] (apply f arg1 arg2 arg3 args)))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
Why it has several parity options if it could have one? Is it just performance optimisation so concat doesn't get called in most cases?
I mean it could look like this otherwise, right?
(defn partial
([f] f)
([f & more]
(fn [& args] (apply f (concat more args))))
)
I also noticed several other functions follow the same pattern.
Yes, it's a performance optimization.
I'ts not just about not calling concat - it's about the fact that & in the argument list requires a collection to be created as well. The clojure core libraries tend to take performance seriously, under the assumption that the basic building blocks of the language will be present in everyone's performance bottleneck.
I am discussing closure with a friend and he thinks (partial + 5) is a closure. But I think a closure is a function closing over a free variable, for example
(let [a 10]
(defn func1 [x] (+ x a))
)
then func1 is a closure. But in this case 5 is not a free variable. So which is the right answer?
partial uses a closure to make the partial function. Check out the code of partial by using (source partial) in repl and you will see that it uses closures.
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"}
([f arg1]
(fn [& args] (apply f arg1 args)))
([f arg1 arg2]
(fn [& args] (apply f arg1 arg2 args)))
([f arg1 arg2 arg3]
(fn [& args] (apply f arg1 arg2 arg3 args)))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
(partial + 5) is an anonymous function or "lambda".
Anonymous functions are often¹ called "closures" but it's an abuse of the term ; see the discussion in "What is the difference between a 'closure' and a 'lambda'?"
[¹] Maybe because in most popular languages that support them, closures and anonymous functions are created using the same language features - which renders them undistinguishable at first glance.