Not so happy with my code

2 views
Skip to first unread message

verec

unread,
Apr 18, 2010, 9:20:50 PM4/18/10
to Clojure
I have two problems with the following code.
First, I have this `tos' business (to-string) because:
user=> (first "abc")
\a
user=> (rest "abc")
(\b \c)
Since I wanted to get strings out of strings, a character or a
collection is no good.

Second, I ended-up having to use a mutable atom `res' because I
couldn't figure out how to do it otherwise.

Any pointer on how to tackle either or both problems welcome.

(ns word-play)

(defn rotate
"Take a collection and left rotates it n steps.
If n is negative, the collection is rotated right.
Executes in O(n) time."
([coll]
(rotate 1 coll))
([n coll]
(let [c (count coll)]
(take c (drop (mod n c) (cycle coll))))))

(defn all-rotations [word]
"all rots"
(take (count (str word)) (cycle (iterate rotate (str word)))))

(defn anagram [word]
"To generate all anagrams of a given string of length n, the most
obvious solution is to consider all n rotations of the string one
by one, and for each such rotation, concatenate its first character
with each of the anagrams of the new string composed of all but its
first character."
(cond
(= (count word) 0) '("")
(= (count word) 1) (list word)
true
(let [all (all-rotations word)
tos #(str (reduce str %))
wst (map tos all)
res (atom "")]
(doseq [w wst]
(let [s (str w)
f (str (first (str s)))
n (str (tos (next (str s))))
j #(str f (tos %))
a (ana3 n)
r (map j a)]
(swap! res concat r)))
@res)))

user=> (anagram "1")
("1")
user=> (anagram "12")
("12" "21")
user=> (anagram "123")
("123" "132" "231" "213" "312" "321")
user=> (anagram "lisp")
("lisp" "lips" "lspi" "lsip" "lpis" "lpsi" "ispl" "islp" "ipls" "ipsl"
"ilsp" "ilps" "spli" "spil" "slip" "slpi" "sipl" "silp" "plis" "plsi"
"pisl" "pils" "psli" "psil")
user=>

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

verec

unread,
Apr 18, 2010, 9:29:02 PM4/18/10
to Clojure
Posted too quickly ... replace "ana3" with "anagram". I cleaned-up the
code before posting, forgetting that the previous version "ana3" was
still in the REPL ... Oh well ...

David Nolen

unread,
Apr 18, 2010, 9:34:45 PM4/18/10
to clo...@googlegroups.com
You don't need an atom, look into loop/recur. Also, this can be done in two lines with the combinatorics clojure-contrib library ;)

user=> (use 'clojure.contrib.combinatorics)
user=> (map #(apply str %) (permutations "lisp"))

Per Vognsen

unread,
Apr 18, 2010, 9:36:40 PM4/18/10
to clo...@googlegroups.com
(use 'clojure.contrib.combinatorics
'clojure.contrib.str-utils)

(defn anagrams [xs]
(map #(str-join "" %) (distinct (permutations xs))))

Of course, the real meat is in the permutations function. The
implementation in the combinatorics library is a bit complicated.
Here's something simpler (and slower):

(defn splittings [xs]
(lazy-seq
(cons [() xs]
(when (seq xs)
(map (fn [[left right]]
[(cons (first xs) left) right])
(splittings (rest xs)))))))

(defn permutations [xs]
(if (seq xs)
(for [perm (permutations (rest xs))
[left right] (splittings perm)]
(concat left [(first xs)] right))
[()]))
Reply all
Reply to author
Forward
0 new messages