2
\$\begingroup\$

The follow code should correctly run the test cases at the bottom.

It works correctly. I just don't feel like this is clearest way I could write it.

A lot of the complexity comes from having to return the middle two characters if the length of the string is even.

Here is a link to the Programing Challenge.

(defn convert-to-zero-based-list [index]
  (- index 1))

(defn convert-to-one-based-list [index]
  (+ index 1))

(defn get-middle-with-offset [s]
    (/ (convert-to-one-based-list (count s)) 2))

(defn get-start-of-middle [s]
    (convert-to-zero-based-list (Math/floor (get-middle-with-offset s))))

(defn get-end-of-middle [s]
    (Math/ceil (get-middle-with-offset s)))

(defn get-middle [s]
    (subs s (get-start-of-middle s) (get-end-of-middle s)))

(= (get-middle "a") "a")
(= (get-middle "aa") "aa")
(= (get-middle "aba") "b")
(= (get-middle "abba") "bb")
(= (get-middle "abcba") "c")

An alternative would be to recursively/iteratively trim off the first and last elements of the string until you reached the middle one or two characters.

(defn trim-first-and-last [s]
  (subs s 1 (dec (count s)))
)

(defn get-middle-recur [s]
  (if (<= (count s) 2)
      s
      (get-middle-recur (trim-first-and-last s))
  )
)

(= (get-middle-recur "a") "a")
(= (get-middle-recur "aa") "aa")
(= (get-middle-recur "aba") "b")
(= (get-middle-recur "abba") "bb")
(= (get-middle-recur "abcba") "c")
(= (get-middle-recur "abccba") "cc")
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Have you checked for performance differences between the 2 approaches? I combined your 2nd approach into your question, as it is not really an answer. Perhaps running both approaches with a really long string, say, thousands of characters, would give you more information than those tiny test cases. \$\endgroup\$
    – Phrancis
    Commented Jan 20, 2018 at 5:09
  • \$\begingroup\$ I generally worry about readability more than performance. Writing it recursively helped the readability tremendously, but I think you raise a good issue, because as the string size gets large, the recursive solution gets much slower. At 4000 characters the recursive solution was an order of magnitude slower with 0.3 seconds vs 3 seconds. \$\endgroup\$
    – TMB
    Commented Jan 20, 2018 at 5:30

2 Answers 2

1
\$\begingroup\$

An alternative would be to recursively/iteratively trim off the first and last elements of the string until you reached the middle one or two characters.

(defn trim-first-and-last [s]
  (subs s 1 (dec (count s)))
)

(defn get-middle-recur [s]
  (if (<= (count s) 2)
      s
      (get-middle-recur (trim-first-and-last s))
  )
)

(= (get-middle-recur "a") "a")
(= (get-middle-recur "aa") "aa")
(= (get-middle-recur "aba") "b")
(= (get-middle-recur "abba") "bb")
(= (get-middle-recur "abcba") "c")
(= (get-middle-recur "abccba") "cc")
\$\endgroup\$
0
\$\begingroup\$

Your first variant is indeed very long code.

You basically define a function for every tiny arithmetic operation. This requires you to come up with good names for every of those operations, and naming things well is very hard. Therefore, I'd choose to have fewer named functions.

Two of your function names are convert-to-list, which I find misleading since your code is not really about lists, and not about one-based indexing. It's simply that they add or subtract one.

I don't know how to write idiomatic Clojure since I don't write it regularly. I come from a Java background, and this is how I would do it:

(defn middle [s]
  (let [start (quot (- (count s) 1) 2)
        end   (+ (quot (count s) 2) 1)]
  (subs s start end)))

It's basically the same as in your code. I inlined the convert functions and used integer division instead of floating point division, so that I don't have to use Math/floor and Math/ceil.

And, since I won't need all the helper functions for anything else besides this middle function, I converted them into a single let, so that the definitions are only visible within the middle function.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.