4Clojure 59 Juxtaposition
|16-Jul-2012||Posted by Sonia Hamilton under Clojure|
A reminder of what the juxt function does:
Takes a set of functions and returns a fn that is the juxtaposition of those fns. The returned fn takes a variable number of args, and returns a vector containing the result of applying each fn to the args (left-to-right). ((juxt a b c) x) => [(a x) (b x) (c x)]
The problem was fairly easy, it just required the HOF (Higher Order Functions) abstraction of thinking of functions as arguments, like any other (numeric, string, …) argument.
First of all, I need a function that takes a variable number of arguments (0-n) – standard destructuring:
(defn myjuxt [& fs] ...
I don’t know what to do with these functions yet, so I’ll just loop through using for and return each function:
(defn myjuxt [& fs] (for [f fs] f))
And here you can see the returned list of function objects. Not very useful but a start:
(myjuxt + min max) user => (#core_plus #core_min #core_max)
The question gives a good hint here: “return a new function that takes a variable number of arguments”. So do that, using the same destructuring approach for a variable number of arguments:
(defn myjuxt2 [& fs] (fn [& xs] ...
So I want to run each function against these arguments, so I’ll use for again:
(defn myjuxt2 [& fs] (ƒ [& xs] (for [f fs] (f xs)))) ((myjuxt2 + min max) 1 2 3) user=> (ClassCastException Cannot cast clojure.lang.ArraySeq to java.lang.Number
Ouch! But the error message gives a hint – something is seeing an array instead of a number. My three functions want numbers and they’re getting arrays – I need apply to “strip out” the arguments:
(defn myjuxt2 [& fs] (fn [& xs] (for [f fs] (apply f xs)))) ((myjuxt2 + min max) 1 2 3) user=> (6 1 3)
And it works! Looking at the solutions I see that I could’ve used map instead of for, and #() instead of (fn), but they’re minor “golfing” details.