16

I've defined the following 3 functions using the leiningen REPL:

(defn rand-int-range [floor ceiling] (+ floor (rand-int (- ceiling floor))))

(defn mutate-index
  "mutates one index in an array of char and returns the new mutated array"
  [source idx]
  (map
    #(if (= %1 idx)
       (char (+ (int %2) (rand-int-range -3 3)))
       %2)
    (iterate inc 0)
    source))

(defn mutate-string
  [source]
  (str
    (mutate-index
      (.toCharArray source)
      (rand-int (alength (.toCharArray source))))))

When I run (mutate-string "hello"), instead of the REPL printing out the mutated string, it is printing out clojure.lang.LazySeq@xxxxxx where the 'xxxxx' is a random sequence of numbers and letters. I would expect it to instead print something like "hellm" Is this really giving me a string back like I think? If it is, how can I get the REPL to show me that string?

3 Answers 3

19

1) To convert a sequence of characters (which is what mutate-index returns) to a string, use apply str rather than just str. The latter operates on objects, not sequences.

(str [\a \b \c])
=> "[\\a \\b \\c]"

(apply str [\a \b \c])
=> "abc"

2) Strings are seqable, meaning you can use sequence functions like map and filter on them directly without stuff like .toCharArray.

3) Consider using map-indexed or StringBuilder to accomplish what you're trying to do:

(apply str (map-indexed (fn [i c] (if (= 3 i) \X c)) "foobar"))
=> "fooXar"

(str (doto (StringBuilder. "foobar") (.setCharAt 3 \X)))
=> "fooXar"
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Justin, #1 above answered my question. I'm also going to look into #2 and #3 as they seem to be good advice. Obviously I'm a clojure noob, so thanks.
8

That happens, because the map function returns a lazy sequence, which string reprsentation is just the class name and a hash (clojure.lang.LazySeq@xxxxxx).

In order to get the original working you need to first to get the lazy sequence evaluated. By using the (apply str ...) like Justin adviced that should happen and you should get the correct result.

Otherwise normally if you see similar problems due to not evaluating a lazy sequence you should try out function doall, which forces the evalutation of a lazy sequence

Comments

0

Whenever I want to get the string value of a lazy sequence I use clojure.core/pr-str:

user=> (clojure.tools.logging/infof "This is a lazy seq [%s]" (take 5 (range 100)))
INFO  user - This is a lazy seq [clojure.lang.LazySeq@1b554e1]
nil
user=> (clojure.tools.logging/infof "This is a lazy seq [%s]" (pr-str (take 5 (range 100))))
INFO  user - This is a lazy seq [(0 1 2 3 4)]
nil

1 Comment

Grrr. This doesn't add up (but I've seen it work). The docs for (pr-str) say it "pr to a string, returning it" and the docs for (pr) say "Prints the object(s) to the output stream that is the current value of out..." but I don't want it to go to out, I just want the lazy-seq fully realized. Methinks LazySeq may be not as "Simple" as it is advertised.

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.