1

I am writing a function that, for any given string, replaces any digits within that String with the same number of '.' characters.

Examples:

AT2X -> AT..X

QW3G45 -> QW...G.........

T3Z1 -> T...Z.

I've written the following Clojure function but I am getting an error I don't quite understand:

java.lang.ClassCastException: clojure.lang.LazySeq (in module: Unnamed Module) cannot be case to java.lang.Charsequence

I'm interpreting from the error that I need to force an evaluation of a lazy sequence back into a String (or CharSequence) but I can't figure out where to do so or if this is correct.

(defn dotify
    ;;Replaces digits with the same number of '.'s for use in traditional board formats
    [FEN]
    (let [values (doall (filter isDigit (seq FEN)))]
        (fn [values]
            (let [value (first values)]
                (str/replace FEN value (fn dots [number]
                                           (fn [s times]
                                               (if (> times 0)
                                                   (recur (str s ".") (dec times)))) "" (Character/digit number 10)) value))
        (recur (rest values))) values))

2 Answers 2

2

There is a standard clojure.string/replace function that may handle that case. Its last argument might be not just a string or a pattern but also a function that turns a found fragment into what you want.

Let's prepare such a function first:

(defn replacer [sum-str]
  (let [num (read-string num-str)]
    (apply str (repeat num \.))))

You may try it in this way:

user> (replacer "2")
..
user> (replacer "9")
.........
user> (replacer "22")
......................
user> 

Now pass it into replace as follows:

user> (clojure.string/replace "a2b3c11" #"\d+" replacer)
a..b...c...........
Sign up to request clarification or add additional context in comments.

1 Comment

I would suggest avoiding the names like string & int, using something more like: (let [num (read-string arg-str)] ...)
0

Here's a way to do this using reduce:

(defn dotify [s]
  (->> s
       (reduce (fn [acc elem]
                 (if (Character/isDigit elem)
                   (let [dots (Integer/parseInt (str elem))]
                     (apply conj acc (repeat dots \.)))
                   (conj acc elem)))
               [])
       (apply str)))

(dotify "zx4g1z2h")
=> "zx....g.z..h"

And another version using mapcat:

(defn dotify-mapcat [s]
  (apply str
    (mapcat (fn [c]
              (if (Character/isDigit c)
                (repeat (Integer/parseInt (str c)) \.)
                [c]))
            s)))

There are some issues in your example:

  1. Many of the internal forms are themselves functions, but it looks like you just want their bodies or implementations instead of wrapping them in functions.
  2. It's hard to tell by the indentation/whitespace, but the entire function is just recur-ing, the fn above it is not being used or returned.
  3. One of the arguments to str/replace is a function that returns a function.

It helps to break the problem down into smaller pieces. For one, you know you'll need to examine each character in a string and decide whether to just return it or expand it into a sequence of dots. So you can start with a function:

(defn expand-char [^Character c]
  (if (Character/isDigit c)
    (repeat (Integer/parseInt (str c)) \.)
    [c]))

Then use that function that operates on one character at a time in a higher-order function that operates on the entire string:

(apply str (mapcat expand-char s))
=> "zx....g.z..h"

Note this is also ~5x faster than the examples above because of the ^Character type-hint in expand-char function.

You can do this with str/replace too:

(defn expand-char [s]
  (if (Character/isDigit ^Character (first s))
    (apply str (repeat (Integer/parseInt s) \.))
    s))
(str/replace "zx4g1z2h" #"." expand-char)
=> "zx....g.z..h"

1 Comment

Thank you! These are much more concise examples. Still getting the hang of when to use forms vs functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.