回文基数 in Clojure

## 問題と他の方の解答

問題: [回文基数 〜 横へな 2014.1.10 参考問題](http://nabetani.sakura.ne.jp/hena/ord17scheherazade/)
他の方の解答: [第17回オフラインリアルタイムどう書くの参考問題](http://qiita.com/Nabetani/items/dabe8ec57e0313229552)

## 私の解答 in Clojure

```clojure
(ns ord17ref
  (:require [clojure.test :refer (deftest are run-tests)]))

(defn- columns [b]
  (fn f [i]
    (if (zero? i) '()
      (cons (mod i b) (f (quot i b))))))

(defn solve [s]
  (let [i (read-string s)
        bs (for [b (range 2 i)
                 :let [cs ((columns b) i)]
                 :when (= cs (reverse cs))]
               b)]
    (if (empty? bs) "-" (apply str (interpose "," bs)))))

(deftest solve-test
  (are [i o] (= (solve i) o)
    "17301" "5,38,100,218,236,5766,17300"    
    "2" "-"    
    "1" "-"    
    "3" "2"    
    "4" "3"    
    "5" "2,4"    
    "6" "5"    
    "10" "3,4,9"    
    "101" "10,100"    
    "1001" "10,25,76,90,142,1000"    
    "10001" "10,24,30,42,80,100,136,10000"    
    "1212" "22,100,201,302,403,605,1211"    
    "123412" "62,100,205,215,30852,61705,123411"    
    "5179" "5178"    
    "4919" "4918"    
    "5791" "5790"    
    "5498" "2748,5497"    
    "453" "150,452"    
    "134" "66,133"    
    "8489" "27,652,8488"    
    "1234" "22,616,1233"    
    "5497" "41,238,5496"    
    "4763" "19,35,432,4762"    
    "3974" "17,27,1986,3973"    
    "3521" "44,55,502,3520"    
    "5513" "20,38,53,148,5512"    
    "8042" "23,29,60,4020,8041"    
    "7442" "37,60,121,3720,7441"    
    "4857" "25,1618,4856"    
    "22843" "49,69,91,141,430,22842"    
    "194823" "84,121,21646,64940,194822"    
    "435697" "160,169,235,626,1822,435696"    
    "142" "3,7,70,141"    
    "886" "5,14,442,885"    
    "3102" "7,65,93,140,281,516,1033,1550,3101"    
    "17326" "11,28,99,105,8662,17325"    
    "32982" "13,72,238,477,716,1433,5496,10993,16490,32981"    
    "36" "5,8,11,17,35"    
    "37" "6,36"    
    "251" "8,250"    
    "252" "5,10,17,20,27,35,41,62,83,125,251"    
    "253" "12,14,22,252"    
    "6643" "2,3,9,81,90,510,948,6642"    
    "5040" "71,79,83,89,104,111,119,125,139,143,167,179,209,239,251,279,314,335,359,419,503,559,629,719,839,1007,1259,1679,2519,5039"    
    "9240" "23,38,62,104,109,119,131,139,153,164,167,209,219,230,263,279,307,329,384,419,439,461,615,659,769,839,923,1154,1319,1539,1847,2309,3079,4619,9239"))
```

## テスト実行結果

```clojure
Clojure 1.6.0
user=> (ns ord17ref)
nil
ord17ref=> (require 'ord17ref :reload-all)
nil
ord17ref=> (run-tests)

Testing ord17ref

Ran 1 tests containing 45 assertions.
0 failures, 0 errors.
{:type :summary, :fail 0, :error 0, :pass 45, :test 1}
```

## 解説

```clojure
(defn- columns [b]
  (fn f [i]
    (if (zero? i) '()
      (cons (mod i b) (f (quot i b))))))
```

`columns` は, 基数 `b` を与えると「整数 `i` を与えると, `i` を基数 `b` で表した場合の, 各桁の数値をシーケンスで返す関数」を返します.

関数 `f` は, 数値 `i` を与えると「`i` を `b` で割った商を, 自身 `f` に与えて得られるシーケンス」の先頭に「`i` を `b` で割った余り」を付加します. `i` が 0 の時は空リストを返します.
これで基数 `b` の場合の各桁を下位から並べたシーケンスが得られます.

利用例です.

```clojure
ord17ref=> ((columns 2) 10)
(0 1 0 1)
ord17ref=> ((columns 3) 10)
(1 0 1)
ord17ref=> ((columns 4) 10)
(2 2)
ord17ref=> ((columns 5) 10)
(0 2)
```

---

```clojure
(defn solve [s]
  (let [i (read-string s)
        bs (for [b (range 2 i)
                 :let [cs ((columns b) i)]
                 :when (= cs (reverse cs))]
               b)]
    (if (empty? bs) "-" (apply str (interpose "," bs)))))
```

文字列で与えられた数字列 `s` を `(read-string ...)` で数値にし, `i` とします.
基数 `b` を 2 から `i` - 1 の範囲で変化させ, その場合の `i` の各桁を下位から並べたシーケンスを `cs` とします.
`cs` と `(reverse cs)` が一致する場合は, 基数 `b` を抽出します.
抽出した基数のシーケンスを `bs` とします.
`bs` が空なら `"-"` を返し, そうでなければ, `(interpose "," ...)` で `","` を間に挟んで,
`(apply str ...)` で文字列化します.

各部分は以下の例のように機能します.

```clojure
ord17ref=> (read-string "17301")
17301

ord17ref=> (for [b (range 2 17301) :let [cs ((columns b) 17301)] :when (= cs (reverse cs))] b)
(5 38 100 218 236 5766 17300)

ord17ref=> (interpose "," '(5 38 100 218 236 5766 17300))
(5 "," 38 "," 100 "," 218 "," 236 "," 5766 "," 17300)

ord17ref=> (apply str '(5 "," 38 "," 100 "," 218 "," 236 "," 5766 "," 17300))
"5,38,100,218,236,5766,17300"
```