Skip to content

Fun in the SundaYS

Let's do a Fun FridaYS post on Sunday.

I wrote the YS one liner to open 40 random Rosetta Code tasks written in Clojure.

$ cd RosettaCodeData
$ vim $(ys -e 'sh-out("find Lang/Clojure"):lines:shuffle
               .mapv(fn([d] sh-out("ls $d"):lines
               .mapv(fn([f] say("$d/$f")))))' |
        grep -v '\-[0-9]' |
        head -40)

Pretty cool, right?

Best Shuffle🔗

Today's task is called Best Shuffle External link .

Shuffle the characters of a string in such a way that as many of the character values are in a different position as possible.

A shuffle that produces a randomized result among the best choices is to be preferred.

A deterministic approach that produces the same sequence every time is acceptable as an alternative.

Display the result as follows:

original string, shuffled string, (score)

The score gives the number of positions whose character value did not change.

Test cases:

abracadabra
seesaw
elk
grrrrrr
up
a

Clojure Solution🔗

(defn score [before after]
  (->> (map = before after)
    (filter true?)
    count))

(defn merge-vecs [init vecs]
  (reduce (fn [counts [index x]]
            (assoc counts x (conj (get counts x []) index)))
    init vecs))

(defn frequency
  "Returns a collection of indices of distinct items"
  [coll]
  (->> (map-indexed vector coll)
    (merge-vecs {})))

(defn group-indices [s]
  (->> (frequency s)
    vals
    (sort-by count)
    reverse))

(defn cycles [coll]
  (let [n (count (first coll))
        cycle (cycle (range n))
        coll (apply concat coll)]
    (->> (map vector coll cycle)
      (merge-vecs []))))

(defn rotate [n coll]
  (let [c (count coll)
        n (rem (+ c n) c)]
    (concat (drop n coll) (take n coll))))

(defn best-shuffle [s]
  (let [ref (cycles (group-indices s))
        prm (apply concat (map (partial rotate 1) ref))
        ref (apply concat ref)]
    (->> (map vector ref prm)
      (sort-by first)
      (map second)
      (map (partial get s))
      (apply str)
      (#(vector s % (score s %))))))

(->> ["abracadabra" "seesaw" "elk" "grrrrrr" "up" "a"]
  (map best-shuffle)
  vec
  println)

Run it:

$ clj -M best-shuffle.clj
[[abracadabra racrababaad 0] [seesaw wssaee 0] [elk kel 0] [grrrrrr rgrrrrr 5] [up pu 0] [a a 1]]

Ported to YS🔗

!YS-v0

defn main(input):
  say: read-string(input)
         .map(best-shuffle):vec

defn best-shuffle(s):
  ref =: s:group-indices:cycles
  prm =: concat.apply(map(partial(rotate 1) ref))
  ref =: concat(ref*)

  map(vector ref prm):
    .sort-by(first _)
    .map(second)
    .map(partial(get s))
    .str(*)
    .call(\([s _ s.score(_)]) _)

defn score(before after):
  map(== before after)
    .filter(true?).#

defn group-indices(s):
  map-indexed(vector s)
    .merge-vecs({}):vals
    .sort-by(count _):reverse

defn cycles(coll):
  n =: coll.0.#
  cycle =: range(n):cycle
  coll =: concat(coll*)
  map(vector coll cycle):
    .merge-vecs([])

defn rotate(n coll):
  c =: coll.#
  n =: (c + n) % c
  concat coll.drop(n): coll.take(n)

defn merge-vecs(vecs init):
  reduce _ init vecs:
    fn(counts [index x]):
      assoc counts x:
        counts.$x.or([]).conj(index)

and run it:

$ ys best-shuffle.ys '["abracadabra" "seesaw" "elk" "grrrrrr" "up" "a"]'
[[abracadabra racrababaad 0] [seesaw wssaee 0] [elk kel 0] [grrrrrr rgrrrrr 5] [up pu 0] [a a 1]]

And published on Rosetta Code External link !

Comments