Instantly share code, notes, and snippets.

# d11wtq/README.md Last active Aug 29, 2015

Writing int->str (Clojure)

# int->str

This function computes the English string for any given integer supported by Clojure.

## Example

`(int->str 110917) ; "one hundred and ten thousand nine hundred and seventeen"`

## How it works

### Components in a number

The most important trick used is to first break the number down into its components (i.e. its millions, its thousands, hundreds and ones). We do this by successively dividing the number by the size of each component, starting with the largest supported component and carrying over the remainder. The function `solve-components` takes care of this and outputs something of the form:

`(solve-components 110927) ;[[:thousand 110] [:hundred 9] [:ten 2] [:one 7]]`

### Conversion to a string

Now we can write a function to name each component pair. The function `str-of-pair` does this.

`(str-of-pair [:thousand 110]) ; "one hundred and ten thousand"`

For everything except the `:ten`s, `:one`s and `:teen`s this is just a case of saying `(str (int->str value) " " (name component))`. Note the recursion back into `int->str` here in order to avoid saying "110 thousand". For the special cases, we use a lookup table.

### Dealing with teens

There is a special case in English, where `[:ten 1] [:one 7]` isn't "ten seven" but is rather "seventeen". We handle this by eliminating the `:ten` and the `:one` components and replacing them with a `:teen` component (e.g. `[:teen 7]`). The string conversion has a lookup table for `:teen`. The `teenify` function handle the substitution and is performed at the end of `solve-components`.

### Dealing with hundreds

In English, when a number is not a round hundred, we use the conjuction "and" before the remaining number. To handle this, we rebuild our list of components in reverse. If we see a `:hundred` and we've already processed something before it, we inject the `[:str "and"]` component, otherwise we just copy as-is. Remember we're processing in reverse to simplify the need to lookahead. The function `andify` does this substitution and is performed at the end of `solve-components`. Our `str-of-pair` treats `:str` components as verbatim pieces of text.

 (ns numbers.core (require [clojure.string :as string])) (declare int->str) (def sizes "Table of numeric units and sizes." [[:trillion 1000000000000] [:billion 1000000000] [:million 1000000] [:thousand 1000] [:hundred 100] [:ten 10] [:one 1]]) (def names "Table of numeric units and names" {:teen ["eleven" "twelve" "thirteen" "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" "nineteen"] :ten ["ten" "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety"] :one ["one" "two" "three" "four" "five" "six" "seven" "eight" "nine"]}) (defn divide "Performs: x/y = [z,r] where r is the remainder." [x y] (let [remainder (mod x y)] [(/ (- x remainder) y) remainder])) (defn teenify "Substitutes [:ten 1] [:one n] in components for [:teen n]." [components] (let [[[one x] [ten y] & more] (reverse components)] (if (and (= :ten ten) (= 1 y)) (reverse (into more [[:teen x]])) components))) (defn andify "Substitutes [:hundred n] ... for [:hundred n] [:str \"and\"] ..." [components] (reduce (fn [acc [unit n :as pair]] (into acc (or (if (and (not (empty? acc)) (= :hundred unit)) [[:str "and"] pair]) [pair]))) (list) (reverse components))) (defn solve-components "Convert n into a table of its component units." [n] (letfn [(accumulate-units [[n units] [tag size]] (let [[quantity remainder] (divide n size)] [remainder (conj units [tag quantity])]))] (->> (reduce accumulate-units [n (vector)] sizes) (last) (filter (fn [[tag n]] (> n 0))) (teenify) (andify)))) (defn str-of-pair "String value of the individual component pair." [[component v]] (if-let [lookup (names component)] (lookup (dec v)) (case component :str v (str (int->str v) " " (name component))))) (defn int->str "Convert integer n into the English string." [n] (string/join " " (map str-of-pair (solve-components n))))