Just Juxt #2: Palindrome Detector (4clojure #27)
4clojure 27 - Palindrome Detector
Write a function which returns true
if the given sequence is a palindrome.
Hint: "racecar"
does not equal '(\r \a \c \e \c \a \r)
.
Unit tests
We'll start by setting up a placeholder function and our test suite in a KLIPSE snippet. This code is live and editable, so go ahead and fill it in to make the tests pass. Or not... Not trying to tell you what to do or anything...
(ns live.test
(:require [cljs.test :refer-macros [deftest is testing run-tests]]))
(defn palindrome? [s]
)
(deftest test-27
(is (false? (palindrome? '(1 2 3 4 5))))
(is (true? (palindrome? "racecar")))
(is (true? (palindrome? [:foo :bar :foo])))
(is (true? (palindrome? '(1 1 3 3 1 1))))
(is (false? (palindrome? '(:a :b :c)))))
(run-tests)
First off, here's the standard, boring way to do it:
(#(= (seq %) (reverse %)) '(1 2 3 4 5))
(#(= (seq %) (reverse %)) '(1 2 3 2 1))
But friends don't let friends pass up a juxt
-ortunity...
Are you ready for this?
Remember, juxt
is our universal vector constructor. So, what do we want in our vector?
Let's back up a tiny bit - were it not for that pesky string ("racecar"
), we would only have to compare a sequence with its reverse:
(defn palindrome? [s]
(= s (reverse s)))
(run-tests)
You see, the string gets turned into a sequence of characters when reverse
is called on it:
(reverse "racecar")
So to perform a proper comparison we need to call seq
on the forward version too.
That's why this is a perfect
juxt
-ortunity!
Any time we need to produce a vector of items each processed by separate functions, we could be thinking
juxt
. When this use-case is understood, the function will become a potentially formidable item in our utility-belt. It seems evident to us, asjuxt
-ers, that encouraging its establishment as an idiomatic Clojure design pattern will lead to code that more clearly demonstrates our intentions. That's what language expressivity is all about, and I believe it's one of Clojure's biggest strengths. And indeed, modeling the problem as a vector of items processed by different functions seems entirely fitting in this case:
(seq "racecar")
(reverse "racecar")
Here we go, time to juxt
it up:
((juxt seq reverse) "racecar")
Isn't it beautiful?
From here we can easily compare them with (apply =)
:
(defn palindrome? [s]
(apply = ((juxt seq reverse) s)))
(run-tests)
Now that's what I call love at first juxt
. See you all tomorrow for more!