Fork me on GitHub

i am building a hexagonal structure made of strings where each cell is named based on the us alphabet e.g. "a, b, c... but then aa, ab, ac, ad.... ba, bb, bc and so on, depending on the number of cells you want to generate. I am used to doing something like this with nested loops, but wondering if there's a better way to construct an arbitrary amount of strings from a collection (the alphabet) in clojure?


If you are working in JVM Clojure, one fun option would be to base something on Integer.toString(..., radix) described at ...gets you almost all the way there, but would use 0-p, which then you could shift 10 ascii points to a-z


progress so far:

(def alphabet ["a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"])

(defn index-to-label [index]
    (loop [i (+ 1 index)
           acc []]
      (if (zero? i)
        (apply str (reverse acc))
        (recur (quot (dec i) 26)
               (conj acc (nth alphabet (mod (dec i) 26)))))))

(defn infinite-labels []
  (map index-to-label (range)))


Maybe something like this?

(let [alphabet ["a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"]]
    (for [x alphabet
          y alphabet
          :when (not= x y)]
      (str x y))) ;; => ("ab" "ac" "ad" "ae" ,,,)

Bob B15:06:54

or something like:

(def cell-labels
  (let [alphabet    (map (comp str char) (range (int \a) (inc (int \z))))
        make-longer #(for [seed % next alphabet] (str seed next))]
    (apply concat (iterate make-longer alphabet))))

(first cell-labels)
=> "a"
(nth cell-labels 10000)
=> "ntq"
(nth cell-labels 100000)
=> "eqxe"


mega noob question: why does if need its own scope when used with the fn syntax, but not when used with the #() syntax. for example:

(fn [x] (if (= (mod x 2) 0) x nil))
;; vs.
#(if (= (mod % 2) 0) % nil)


because #(...) read expands to something like (fn [%] (...)) edit: whoops


how does that explain it?


you are still missing an extra parens


the extra parens get added by the reader macro


so #(foo %) expands to (fn [%] (foo %))


it happens at the code reader level rather than a normal macro. maybe this is what you're looking for?


I see now, I wrote the wrong thing the first time

🙏 1
Bob B15:06:33

it's also possible to use the reader to make the comparison:

(read-string "#(if (= (mod % 2) 0) % nil)")
; => (fn* [p1__1943#] (if (= (mod p1__1943# 2) 0) p1__1943# nil))
;    (fn [x]          (if (= (mod x         2) 0) x         nil)))


it's the same reason for which you can't return vector or map literals in #(), the form after the # is embedded in the fn body as is