Just Juxt #1: Fibonacci Sequence (4clojure #26)
Welcome to just juxt! Where it's all juxt
, all the time!
We might as well introduce the juxt
function with its own source:
Clojure 1.9.0
user=> (source juxt)
(defn juxt
"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)]"
{:added "1.1"
:static true}
([f]
(fn
([] [(f)])
([x] [(f x)])
([x y] [(f x y)])
([x y z] [(f x y z)])
([x y z & args] [(apply f x y z args)])))
([f g]
(fn
([] [(f) (g)])
([x] [(f x) (g x)])
([x y] [(f x y) (g x y)])
([x y z] [(f x y z) (g x y z)])
([x y z & args] [(apply f x y z args) (apply g x y z args)])))
([f g h]
(fn
([] [(f) (g) (h)])
([x] [(f x) (g x) (h x)])
([x y] [(f x y) (g x y) (h x y)])
([x y z] [(f x y z) (g x y z) (h x y z)])
([x y z & args] [(apply f x y z args) (apply g x y z args) (apply h x y z args)])))
([f g h & fs]
(let [fs (list* f g h fs)]
(fn
([] (reduce1 #(conj %1 (%2)) [] fs))
([x] (reduce1 #(conj %1 (%2 x)) [] fs))
([x y] (reduce1 #(conj %1 (%2 x y)) [] fs))
([x y z] (reduce1 #(conj %1 (%2 x y z)) [] fs))
([x y z & args] (reduce1 #(conj %1 (apply %2 x y z args)) [] fs))))))
nil
user=>
To build our dataset we will use Mike Fikes' coal-mine, a collection of submissions from the top 1000 users on 4clojure. A grep
yielded over 300 occurrences of juxt
. The very first of which is found in a solution to problem #26:
The good ol' Fibonacci Sequence!
Write a function which returns the first
x
fibonacci numbers.
(ns live.test
(:require [cljs.test :refer-macros [deftest is testing run-tests]]))
(defn fib [x]
(->> [1 1]
(iterate
(juxt last
(partial apply +)))
(take x)
(map first)))
(deftest test-26
(is (= (fib 3) '(1 1 2)))
(is (= (fib 6) '(1 1 2 3 5 8)))
(is (= (fib 8) '(1 1 2 3 5 8 13 21))))
(run-tests)
And there we have it, our first canonical use of juxt
!
Now that we see it passes the tests, let's get our hands dirty and get a better feel for what juxt
is doing. We'll simply hack our function into pieces to get a closer look. (BTW the code on this page is interactive so go ahead and smack it around ;)
(->> [1 1]
(iterate
(juxt last
(partial apply +)))
(take 12)
(map first))
Here it is macroexpanded:
(map first (take 12 (iterate (juxt last (partial apply +)) [1 1])))
Remove (map first)
to see what it does:
(take 12 (iterate (juxt last (partial apply +)) [1 1]))
We could also write it using an anonymous function instead of using partial
(yay even moar shorter!):
(take 12 (iterate (juxt last #(apply + %)) [1 1]))
As we can see, juxt
is acting as our trusty vector-pair generator. The two functions it takes, last
and #(apply + %)
are used to populate them. Each iteration produces:
- The
last
(second) digit; and - The sum of the 2 digits.
That's a fib
.
But that ain't no fib, folks. See y'all tomorrow for another issue of (just juxt!)
!