16 Jul 2012
4clojure problem 59 Juxtapostion - here’s how I approached the problem (warts and all) in the hope that it helps others who are learning 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.
comments powered by Disqus