I am trying to solve a clojure problem where I implement my own comp function.
I have the following expression that works how I expect:
(reduce #(apply %2 [%1]) [1 2 3 4] [rest reverse])
This gives an output of
(4 3 2)
I have tried abstracting this into a function like this:
(((fn [& funcs]
(fn [& args]
(reduce #(apply %2 [%1]) args funcs)
)) rest reverse) [1 2 3 4])
But I get the following error when I run it:
CompilerException java.lang.ClassCastException: clojure.lang.ArraySeq
cannot be cast to java.lang.Number,
compiling:(/Users/paulcowan/projects/scratch/src/scratch/core.clj:1:1)
To me the only difference that I can see is how that funcs and args are different types than the vectors that I created in the first example.
Why does reduce and apply behave differently in the second example?
Simply:
(defn my-comp [& fns]
(fn [x] (reduce #(%2 %1) x fns)))
giving
((my-comp rest reverse) [1 2 3 4])
;(4 3 2)
As it should, my-comp returns an identity function for an empty argument list.
But it expects all functions, including the first applied,
to take a single argument.
To get round (2), adapt it as follows:
(defn my-comp [& fns]
(if (empty? fns)
identity
(let [[f & fs] fns]
(fn [& args] (reduce #(%2 %1) (apply f args) fs)))))
Apart from (1), this merely rephrases Mark's answer.
For fun ...
We could define my-comp in terms of the standard comp:
(defn my-comp [& fns] (apply comp (reverse fns)))
But it probably makes more sense the other way round, since comp has to reverse its argument list in general:
(defn comp [& fns] (apply my-comp (reverse fns)))
We could even define an argument reverser
(defn rev-args [f] (fn [& args] (apply f (reverse args))))
... which turns a function into one that does the same thing to a reversed argument list.
Then
(def comp (rev-args my-comp))
or vice-versa:
(def my-comp (rev-args comp))
First of all, I don't get any error messages (nor correct result):
user> (((fn [& funcs]
(fn [& args]
(reduce #(apply %2 [%1]) args funcs))) rest reverse) [1 2 3 4])
;; => ()
Difference between the two examples is that in the first one you pass value [1 2 3 4] into reduce, while in the second one you pass [[1 2 3 4]] (because args is meant to keep all arguments of the function as one vector.
This will work:
user> (((fn [& funcs]
(fn [args]
(reduce #(apply %2 [%1]) args funcs))) rest reverse) [1 2 3 4])
;; => (4 3 2)
However, to get a function for functional composition that will be able to take any number of arguments, you should write something like this:
user> (defn my-comp [& fncs]
(fn [& args]
(reduce #(%2 %1) ; you can omit apply here, as %2 is already function
; and %1 is always one value, as noisesmith noticed
(apply (first fncs) args)
(rest fncs))))
;; => #'user/my-comp
user> (def my-fnc (my-comp rest reverse))
;; => #'user/my-fnc
user> (my-fnc [1 2 3 4])
;; => (4 3 2)
It will work fine, because only first function should have ability to take many arguments, as others will be applied to value returned by previously called function.
Related
I am currently struggling with an assignment to create an anonymous function, in order to fulfil the following test cases:
Test case 1:
(= [3 2 1] ((__ rest reverse) [1 2 3 4]))
Test case 2:
(= 5 ((__ (partial + 3) second) [1 2 3 4]))
Test case 3:
(= true ((__ zero? #(mod % 8) +) 3 5 7 9))
Test case 4:
(= "HELLO" ((__ #(.toUpperCase %) #(apply str %) take) 5 "hello world"))
I came up with the solution:
(fn [& fs]
(fn [& items] (reduce #(%2 %1)
(flatten items)
(reverse fs))))
My idea was to create a list of the functions bound to the outer function, and then to apply a reducer on this function list, beginning with array "items".
As this works fine for chaining single arity functions in test cases 1 and 2, I have no idea how to modify the inner Lambda-function, in order to deal with multi-arity functions:
(apply + ___ ) ;; first function argument of test case 3
(take 5 ___ ) ;; first function argument of test case 4
Is there still a way to get around this problem?
Many thanks!
Source:
4Clojure - Problem 58
Addendum: I came across a "funky" solution using:
(fn [& fs] (reduce (fn [f g] #(f (apply g %&))) fs))
I don't fully understand this approach, to be honest...
Addendum 2: There was a similar discussion on this topic 7 years ago:
Clojure: Implementing the comp function
There I found the following solution:
(fn [& xs]
(fn [& ys]
(reduce #(%2 %1)
(apply (last xs) ys) (rest (reverse xs)))))
However, I still do not understand how we are able to kick off the reducer on the expression (apply (last xs) ys) , which represents the left-most function in the function chain.
In test case 1, that would translate to (apply rest [1 2 3 4]), which is wrong.
This is very similar to how comp is implemented in clojure.core.
(defn my-comp
([f] f)
([f g]
(fn
([] (f (g)))
([x] (f (g x)))
([x y] (f (g x y)))
([x y & args] (f (apply g x y args)))))
([f g & fs]
(reduce my-comp (list* f g fs))))
The key to understanding higher order function like comp is to think about what needs to happen when we compose functions.
What is the simplest case ? (comp f) Comp only receiving a single function, so we just return that function, there is no composition yet. How about second most simple case: Comp receiving two functions, like (comp f g), now we need to return another function which when called, does the composition, like (f (g)). But this returned function needs to support zero or more arguments, so we make it variadic. Why does it need to support zero or more arguments ? Because of function g, the inner most function can have zero or more arguments.
For example: what does (comp dec inc) return ?
It returns this fn:
(fn
([] (dec (inc)))
([x] (dec (inc x)))
([x y] (dec (inc x y)))
([x y & args] (dec (apply inc x y args)))))
It assumes that inc (the inner most function which gets executed first) could receive zero or more args. But in reality inc only supports one argument, so you would get the arity exception if you called this function with more than one argument like this ((comp dec inc) 1 2), but calling it with single argument would work, because the inner most function inc has a single arity, ((comp dec inc) 10). I hope I am clear here, why this returned function needs to be variadic.
Now for the next step, what if we compose three or more functions ? This is simple now, because the bread and butter was already implemented with two argument function that my-comp supports. So we just call this 2 argument function while we reduce through a list of supplied functions. Each step returns a new function which wraps the input function.
The first two test cases have the rest params: [[1 2 3 4]], not [1 2 3 4].
So it's not (apply rest [1 2 3 4]) but (apply rest [[1 2 3 4]]) or (rest [1 2 3 4]).
To drill it home:
(rest-ex [& rst]
rst
)
(rst 1 2 3) ;;=> [1 2 3]
(rst [1 2] 3) ;;=> [[1 2] 3]
(rst [1 2 3]) ;;=> [[1 2 3]]
Using apply:
; rest example one
(apply + [1 2 3]) ;;=> 6
; rest example two
(apply conj [[1 2] 3]) ;;=> [1 2 3]
; rest example three
(apply reverse [[1 2 3]]) ;;=> (3 2 1)
For both your funky solution and comp itself, it's like taking a car (the first function), beefing it up with a turbo, installing speakers (the following function). The car, w/ the turbo and amazing sound system, is available for the next group of friends to use (the apply turns it from a one-seat stock car to having as many "seats" as you want). In your case, the reducer function uses apply w/ a rest parameter, so it's like offering the option for more doors w/ each function added (but it chooses one door anyway).
The first two test cases are simple, and reduce isn't needed but can be used.
;; [[1 2 3 4]]
;; [rest reverse]
((fn [& fs] (reduce (fn [f g] #(f (apply g %&))) fs)) rest reverse) ;; is functionally equivalent to
((fn [& fs] #((first fs) (apply (second fs) %&))) rest reverse)
#(rest (apply reverse %&))
;; So
(((fn [& fs] (reduce (fn [f g] #(f (apply g %&))) fs)) rest reverse) [1 2 3 4]) ;; (3 2 1)
(((fn [& fs] #((first fs) (apply (second fs) %&))) rest reverse) [1 2 3 4]) ;; (3 2)
(#(rest (apply reverse %&)) [1 2 3 4]) ;;=> (3 2 1)
The third test case, on the second round of reduce, after it's started, looks like:
;; [3 5 7 9]
;; [zero? #(mod % 8) +]
;; ^ ^ The reducer function runs against these two f's
;; Which turns the original:
(fn [& fs] (reduce (fn [f g] #(f (apply g %&))) fs))
;; into an equivalent:
(reduce #(zero? (apply (fn [v] (mod v 8)) [g])) [+])
;; which ultimately results in (wow!):
((fn [& args] (zero? (apply (fn [v] (mod v 8)) [(apply + args)]))) 3 5 7 9)
Pay careful attention to the %& in the reducer function. that's why I wrapped (apply + args) in a vector.
While going through this, I realized what I intuited from my use of reduce is a tiny bit more involved than I realized--esp. w/ function composition, rest params, and apply at play.
It's not that simple, but it's understandable.
I have faux-curry in Programming Clojure book.
user=> (defn faux-curry [& args] (apply partial partial args))
#'user/faux-curry
user=> (def add-3 ((faux-curry +) 3))
#'user/add-3
user=> (add-3 10)
13
However, it's hard to see why two partials are needed in the body.
In Coluredocs.org, I see (apply f args) as an example of an apply function. How to interpret (apply partial partial args)?
I think the easiest way to understand this is to expand out each part.
(partial + 2) returns a function
(fn [& xs] (apply + 2 xs))
calling the resulting function calls + with 1 and all the subsequent args passed to partial.
(apply + 2 [3 4 5]) is the same as (+ 2 3 4 5)
(fn [& xs] ...) says "take a sequence of arguments and call them xs"
which is like (fn [x1 x2 x3...] ...)
(partial partial) returns a function
(fn [& xs] (apply partial xs))
which has exactly the same behavior as partial because the function it produces just directly calls partial with all the arguments.
(apply partial partial args)
is the same as
(partial partial arg1 arg2...)
returns
(fn [& xs] (apply partial arg1 arg2... xs))
(apply partial partial [+ 1]) produces
(fn [& xs] (apply partial + 1 xs)).
Let's call this result g.
(g 2) => (apply partial + 1 [2]) => (fn [& xs] (apply + 1 2 xs)). Let's call this result h. (h 4 5) => (apply + 1 2 [4 5]) => (+ 1 2 4 5)
But if you were to leave out one of the partials:
(apply partial [+ 1]) produces
(fn [& xs] (apply + 1 xs)))
calling this resultant function will not return a function, but the result of summing the arguments with 1.
So having observed these behaviors, let's attempt to describe
(apply partial partial args):
"Create a function that creates a function that applies some function with the supplied arguments at creation time, and the supplied arguments at call time."
Hence faux-curry is a function that creates a function that creates a function o_O
What is the idiomatic way to apply transducers to an atom's value?
This seems to do the job, but I'm unsure of correctness (and style ^^).
(let [xf1 (map inc)
xf2 (map #(+ % 2))
xf #(vec (eduction (comp xf2 xf1) %))
a (atom [1 2 3])]
(swap! a xf))
;=> [4 5 6]
(let [xf1 (map inc)
xf2 (map #(* % 2))
foo #(into [] (comp xf2 xf1) %)
a (atom [1 2 3])]
(swap! a foo))
;; => [3 5 7]
There are two things you need to take note.
comp in transducers works the opposite order as normal applications. That is, xf2 is applied prior to xf1. For each element, it is doubled then incremented.
eduction returns a sequence, so it's not the same type as your original value in the atom.
I'm running an example in Clojure Programming by Chas Emerick et al.:
(defn make-user
[& [uid]]
{:user-id (or uid (str (java.util.UUID/randomUUID)))})
output:
=> (make-user "T-800")
{:user-id "T-800"}
Now I remove the square brackets around uid in line 2:
(defn make-user
[& uid]
{:user-id (or uid (str (java.util.UUID/randomUUID)))})
The evaluation result becomes:
=> (make-user "T-800")
{:user-id ("T-800")}
How to understand this difference?
In an argument declaration, putting arguments next to & causes them to get wraped into a list.
(defn return [x y & more]
[x y more])
(return 1 2 3 4 5 6)
;=> [1 2 (3 4 5 6)]
This wraping is useful to manipulate an undetermined number of args in a function:
(defn plus [& more] (apply + more))
(plus 3 4)
;=> 7
(plus 3 4 5 6)
;=> 18
But if we know there will only be one element as the optional argument and want to directly access it, we could use first to extract it:
(defn make-user-1 [& uid] {:user-id (first uid)})
(make-user-1 "T-800")
;=> {:user-id "T-800"}
Or, we could simply unwrap the argument element by using destructuring!
(defn make-user-2 [& [uid]] {:user-id uid})
(make-user-2 "T-800")
;=> {:user-id "T-800"}
So, here are some arg declaration and their given output:
(defn f [arg] arg) ;=> arg
(defn f [& arg] arg) ;=> (arg)
(defn f [& [arg]] arg) ;=> arg
The more you wrap args in the arg declaration, the more those unwrap in the body.
[& uid]:
& means putting all arguments together as a list named uid.
So, uid here is of type list
[& [uid]]:
putting all arguments together as [uid], which is a list (not because [] but it is after &).
In addition, do a destructuring of this list by grabbing the first element as uid
So uid here is not a list but just one element.
4Clojure Problem 58 is stated as:
Write a function which allows you to create function compositions. The parameter list should take a variable number of functions, and create a function applies them from right-to-left.
(= [3 2 1] ((__ rest reverse) [1 2 3 4]))
(= 5 ((__ (partial + 3) second) [1 2 3 4]))
(= true ((__ zero? #(mod % 8) +) 3 5 7 9))
(= "HELLO" ((__ #(.toUpperCase %) #(apply str %) take) 5 "hello world"))
Here __ should be replaced by the solution.
In this problem the function comp should not be employed.
A solution I found is:
(fn [& xs]
(fn [& ys]
(reduce #(%2 %1)
(apply (last xs) ys) (rest (reverse xs)))))
It works. But I don't really understand how the reduce works here. How does it represent (apply f_1 (apply f_2 ...(apply f_n-1 (apply f_n args))...)?
Let's try modifying that solution in 3 stages. Stay with each for a while and see if you get it. Stop if and when you do lest I confuse you more.
First, let's have more descriptive names
(defn my-comp [& fns]
(fn [& args]
(reduce (fn [result-so-far next-fn] (next-fn result-so-far))
(apply (last fns) args) (rest (reverse fns)))))
then factor up some
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)
remaining-fns (rest ordered-fns)]
(reduce
(fn [result-so-far next-fn] (next-fn result-so-far))
first-result
remaining-fns))))
next replace reduce with a loop which does the same
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)]
(loop [result-so-far first-result, remaining-fns (rest ordered-fns)]
(if (empty? remaining-fns)
result-so-far
(let [next-fn (first remaining-fns)]
(recur (next-fn result-so-far), (rest remaining-fns))))))))
My solution was:
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
Lets try that for:
((
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
#(.toUpperCase %)
#(apply str %)
take)
5 "hello world"))
fs is a list of the functions:
#(.toUpperCase %)
#(apply str %)
take
The first time through the reduce, we set
f <--- #(.toUpperCase %)
g <--- #(apply str %)
We create an anonymous function, and assign this to the reduce function's accumulator.
#(f (apply g %&)) <---- uppercase the result of apply str
Next time through the reduce, we set
f <--- uppercase the result of apply str
g <--- take
Again we create a new anonymous function, and assign this to the reduce function's accumulator.
#(f (apply g %&)) <---- uppercase composed with apply str composed with take
fs is now empty, so this anonymous function is returned from reduce.
This function is passed 5 and "hello world"
The anonymous function then:
Does take 5 "hello world" to become (\h \e \l \l \o)
Does apply str to become "hello"
Does toUppercase to become "HELLO"
Here's an elegent (in my opinion) definition of comp:
(defn comp [& fs]
(reduce (fn [result f]
(fn [& args]
(result (apply f args))))
identity
fs))
The nested anonymous functions might make it hard to read at first, so let's try to address that by pulling them out and giving them a name.
(defn chain [f g]
(fn [& args]
(f (apply g args))))
This function chain is just like comp except that it only accepts two arguments.
((chain inc inc) 1) ;=> 3
((chain rest reverse) [1 2 3 4]) ;=> (3 2 1)
((chain inc inc inc) 1) ;=> ArityException
The definition of comp atop chain is very simple and helps isolate what reduce is bringing to the show.
(defn comp [& fs]
(reduce chain identity fs))
It chains together the first two functions, the result of which is a function. It then chains that function with the next, and so on.
So using your last example:
((comp #(.toUpperCase %) #(apply str %) take) 5 "hello world") ;=> "HELLO"
The equivalent only using chain (no reduce) is:
((chain identity
(chain (chain #(.toUpperCase %)
#(apply str %))
take))
5 "hello world")
;=> "HELLO"
At a fundamental level, reduce is about iteration. Here's what an implementation in an imperative style might look like (ignoring the possibility of multiple arities, as Clojure's version supports):
def reduce(f, init, seq):
result = init
for item in seq:
result = f(result, item)
return result
It's just capturing the pattern of iterating over a sequence and accumulating a result. I think reduce has a sort of mystique around it which can actually make it much harder to understand than it needs to be, but if you just break it down you'll definitely get it (and probably be surprised how often you find it useful).
Here is my solution:
(defn my-comp
([] identity)
([f] f)
([f & r]
(fn [& args]
(f (apply (apply my-comp r) args)))))
I like A. Webb's solution better, though it does not behave exactly like comp because it does not return identity when called without any arguments. Simply adding a zero-arity body would fix that issue though.
Consider this example:
(def c (comp f1 ... fn-1 fn))
(c p1 p2 ... pm)
When c is called:
first comp's rightmost parameter fn is applied to the p* parameters ;
then fn-1 is applied to the result of the previous step ;
(...)
then f1 is applied to the result of the previous step, and its result is returned
Your sample solution does exactly the same.
first the rightmost parameter (last xs) is applied to the ys parameters:
(apply (last xs) ys)
the remaining parameters are reversed to be fed to reduce:
(rest (reverse xs))
reduce takes the provided initial result and list of functions and iteratively applies the functions to the result:
(reduce #(%2 %1) ..init.. ..functions..)