My first working (and trivial) Clojure program

I would like to share with you the joy of being able to finally make something working in Clojure.
This is a very simple program solving a very simple problem usually described in any IT University: palindrome detection.
My solution is not the most elegant and short one but it gave me the possibility to practice with Vectors.
Obviously any suggestion is more than welcome, thank you!

(defn is-letter [letter]
  (and (> (int letter) (int \A)) (< (int letter) (int \z))))

(defn check-palindrome
  ([word] (let [length (count (seq word))
                letter-vector (vec (filter #(is-letter %1) (seq (.toUpperCase word))))]
                (check-palindrome  letter-vector 0 (- (count letter-vector) 1) )
          )
  )
  ([letters start end ]  (cond
                           (< end start) (Exception. "The string is NOT a palindrome")
                           (not (= (letters start) (letters end))) (Exception. "The word is NOT a palindrome")
                           (or (= start end) (= 1 (- end start))) (println "The string is a palindrome")

                     :else  (recur letters (inc start) (dec end)))
   )
 )
 
(check-palindrome "Murder, for a jar of red rum")

14 thoughts on My first working (and trivial) Clojure program

  1. Hi, I think you can do palindrome check in shorter way, like this:

    (defn check-palindrome [string-to-check]
    (let [clean-sequence
    (filter #(Character/isLetter %)
    (seq (.toLowerCase string-to-check)))]
    (= clean-sequence (reverse clean-sequence))))

  2. Congrats on your first Clojure fn! For the sake of alternatives:


    (defn letter?

    (Character/isLetter c))

    (defn palindrome?
    [s]
    (let [s (filter letter? (.toLowerCase s))]
    (= s (reverse s))))

    It doesn’t handle nil gracefully, but oh well.

  3. Fantastic! Welcome to the fold. ;) I love Clojure and I hope you will too.

    Perhaps even cleaner riff off of Wojtek Hryniewski’s solution:


    (defn palindrome? [string]
    (let [letters (filter #(Character/isLetter %) (.toLowerCase string))]
    (= letters (reverse letters))))

    Notice that you don’t need to explicitly convert anything to a seq. Simply applying filter to a String is good enough.

    Another option if you want to delve a little further into Java land which is totally supported by the Clojure community would be:


    (defn palindrome? [string]
    (let [filtered-string (.replaceAll (.toLowerCase string) "[^A-Za-z]" "")]
    (= (seq filtered-string) (reverse filtered-string))))

    The only advantage I’d see for going this route would be because it avoids the use of the anymous function that’s needed for filtering using Character/isLetter. But it’s a little less readable, IMO.

    A few comments on your implementation:

    1. It’s idiomatic in most Clojure code I’ve read for predicates to be functions ending in the `?` character. This is in contrast to Java where you’d ordinarily define a predicate with `isLetter` because of their lack of support for `?` characters in the name of a method.

    2. You’re function is doing an awful lot of stuff. You’re throwing Exceptions in non-exceptional cases. You’re relying on side-effects to decide whether or not the predicate passes. Exceptions should almost certainly not be used in this case, except maybe in the case where you wanted tighter type checks on your function, but there are better ways to get that, like pre-conditions.

    For instance:


    (defn palindrome? [string]
    {:pre [(string? string)]}
    (let [characters (filter #(Character/isLetter %) (.toLowerCase string))]
    (= characters (reverse characters))))

    If your concern was being able to see when you call the function whether or not it passed, you needn’t worry. Executing the function at the REPL will return the value the function becomes given it’s inputs, which will be true or false. Executing from the command line will print the last value calculated by the program.

    3. Your use of vec is a little discouraged, as the only reason to even use a specific kind of seq is either for its performance characteristics (i.e. faster to add to the front of a list vs. the back of a vector) or its duplicate behavior (i.e. dups allowed in vectors vs. dups not allowed in sets). When possible, sequences should just be thought of as seqs and never as the concrete types that a sequence can represent.

    4. Nice use of recursion! Just be aware that many things that can be solved recursively can also be solved using map/filter/reduce algorithms and tend to read much cleaner using them.

    • Thank you very much Tim,
      your comment is worth a Uni lesson :)
      The reason why I ended up with this clutter solution is because I always remember the way we used to solve this problem in Pascal at Uni and I wanted to replicate it in Clojure. Indeed I’m tempted to solve the binary search in Clojure to see how difficult it is.
      I fully agree with your point 4. I’m already starting to think in Clojure in terms of map/reduce/filter but in this iterative scenario I couldn’t find use of them.
      And as per the exception, you are right. After all “palindrome” should be a predicate itself and it should return only true/false. That was a very “conceptual” mistake I’ve done :(

      Thanks a lot for your help,
      Nico

    • …indeed.. I’m loving it!
      I feel like when I’ve driven for the first time in London on the opposite side I was used to in Italy.
      It means doing something you’re used to do naturally but in a completely new fashion!

    • A few points…

      (1) I agree with previous commentators, that it’s idiomatic to end a predicate with a question mark
      (2) The passed in string is intentionally not lowercased before comparison, as that would mean deciding for the user that checks will be done in a case insensitive manner
      (3) I deferred nil checking to the caller of the code. This may or may not have been the best idea.

      (defn palindrome? [s]
      (= (.toString (.reverse (StringBuilder. strData))) strData))

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>