ズンドコキヨシを抽象化したら進捗を煽られた


囲碁で世界チャンピオンを負かすまでになった人工知能.
しかし, 一部の最先端の研究者の間では, ズンドコ節に「キヨシ！」と合いの手を入れるという, より高度な問題に注目が集まっているようです.

[Qiita でズンドコキヨシを検索](http://qiita.com/search?q=%E3%82%BA%E3%83%B3%E3%83%89%E3%82%B3%E3%82%AD%E3%83%A8%E3%82%B7&sort=rel)

出遅れたようですが Clojure でも試してみましょう.

ただし, 問題を抽象化することにより, 別の問題に同じ解法が適用できることが発見されることがあります.
なるべく抽象化して取り組んでみましょう.

## 与えられた要素群から無作為に要素を選び続ける

遅延シーケンスによる無限要素列として実装します.

```clj:
(defn rand-nth-seq [coll]
  ((fn f [] (cons (rand-nth coll) (lazy-seq (f))))))
```

`:a`, `:b`, `:c` のいずれかを無作為に選び続けるシーケンスから, 最初の 10 個の要素を取り出してみます.

```clj:
user=> (take 10 (rand-nth-seq [:a :b :c]))
(:a :c :a :b :a :c :b :c :c :b)
user=> (take 10 (rand-nth-seq [:a :b :c]))
(:b :c :a :c :a :b :b :a :b :b)
user=> (take 10 (rand-nth-seq [:a :b :c]))
(:c :b :c :b :c :b :a :a :a :a)
```

いいでしょう.

```clj:
(defn rand-nth-seq' [coll]
  (let [gen (fn [_] (rand-nth coll))]
    (iterate gen (gen nil))))
```

と書いても良いです.

```clj:
user=> (take 10 (rand-nth-seq' [:a :b :c]))
(:a :c :c :b :b :b :c :a :b :a)
user=> (take 10 (rand-nth-seq' [:a :b :c]))
(:a :b :b :b :a :c :a :b :c :b)
user=> (take 10 (rand-nth-seq' [:a :b :c]))
(:b :b :a :a :a :a :a :b :a :a)
```

## あるシーケンスの部分列が, 別のシーケンスと一致するところまで取得

```clj:
(defn take-until-eq [t [h & r :as s]]
  (cond
    (not h) '()
    (= (take (count t) s) t) t
    :else (cons h (lazy-seq (take-until-eq t r)))))
```

要素がなければ空シーケンスを返します.
シーケンス `s` が比較対象シーケンス `t` と一致していれば, `t` を返します.
そうでなければ `s` の先頭の要素 `h` を先頭とし, 残りの列に対して, 再帰的にこの関数を適用したシーケンスを続けます.

シーケンス `[:c :b :c :a :c :a :b :b :a :c]` の先頭から, 部分列が `[:b :b :a]` と一致するところまでを取得します.

```clj:    
user=> (take-until-eq [:b :b :a] [:c :b :c :a :c :a :b :b :a :c])
(:c :b :c :a :c :a :b :b :a)
```

問題ないようです.

## ズンドコキヨシ

`"ズン"` と `"ドコ"` の二つから無作為に要素を選択し続け, 部分列が `["ズン" "ズン" "ズン" "ズン" "ドコ"]` と一致するところまでを取得します.

```clj:
user=> (take-until-eq ["ズン" "ズン" "ズン" "ズン" "ドコ"]
         (rand-nth-seq ["ズン" "ドコ"]))
("ドコ" "ドコ" "ドコ" "ズン" "ズン" "ドコ" "ドコ" "ドコ" "ドコ" "ズン" "ドコ" "ズン" "ズン" "ドコ" "ドコ" "ドコ" "ドコ" "ズン" "ドコ" "ドコ" "ズン" "ドコ" "ズン" "ドコ" "ドコ" "ズン" "ズン" "ズン" "ズン" "ズン" "ドコ")
```

いいですね.

では, おもむろに合いの手を.

```clj:
(defn kiyoshi []
  (let [p (take-until-eq ["ズン" "ズン" "ズン" "ズン" "ドコ"]
            (rand-nth-seq ["ズン" "ドコ"]))]
    (dorun (map println p))
    (println "キヨシ！")))
```

試験してみます.

```clj:
user=> (kiyoshi)
ドコ
ズン
ドコ
ドコ
ドコ
ズン
ドコ
ズン
ドコ
ズン
ドコ
ドコ
ドコ
ドコ
ズン
ドコ
ドコ
ズン
ドコ
ズン
ドコ
ズン
ドコ
ドコ
ドコ
ズン
ズン
ズン
ズン
ズン
ドコ
キヨシ！
nil
```

良いですね.

## 進捗どうですか？

さて, 賢明な読者の皆さんは, この解法が, 昨年研究者達を悩ませた「進捗を煽られたことを認識する問題」

[Qiita で「進捗どうですか」を検索](http://qiita.com/search?utf8=%E2%9C%93&sort=rel&q=%E9%80%B2%E6%8D%97%E3%81%A9%E3%81%86%E3%81%A7%E3%81%99%E3%81%8B)

の解法と非常に似通っていることに気づかれたことと思います.

`["進捗" "どう" "です" "か"]` の四つから無作為に要素を選択し続け, 部分列が `["進捗" "どう" "です" "か"]` と一致するところまでを取得します.

```clj:
user=> (#(take-until-eq % (rand-nth-seq %)) ["進捗" "どう" "です" "か"])
("進捗" "進捗" "どう" "どう" "進捗" "か" "どう" "です" "です" "どう" "か" "どう" "どう" "か" "か" "どう" "どう" "進捗" "か" "どう" "です" "です" "進捗" "進捗" "進捗" "か" "です" "です" "か" "進捗" "です" "か" "どう" "です" "です" "進捗" "進捗" "です" "どう" "か" "か" "どう" "どう" "どう" "進捗" "です" "です" "か" "進捗" "どう" "進捗" "です" "進捗" "どう" "か" "か" "か" "です" "進捗" "どう" "進捗" "です" "です" "どう" "進捗" "進捗" "進捗" "か" "か" "です" "です" "です" "です" "か" "です" "進捗" "どう" "です" "か")
```

出力を整形します.

```clj:
(defn wap []
  (let [p (#(take-until-eq % (rand-nth-seq %)) ["進捗" "どう" "です" "か"])]
    (dorun (map print p))
    (println "???")
    (println (str (count p) "回で煽られました"))))
```

ではおもむろに

```clj:
user=> (wap)
かかか進捗進捗です進捗ですどうどうか進捗進捗進捗進捗ですですですかどうどうどう進捗進捗進捗ですですかか進捗ですかです進捗進捗どう進捗かどうかですですかですかか進捗進捗です進捗進捗どうどうです進捗ですどうですか進捗かどうですどう進捗どうか進捗か進捗進捗です進捗進捗です進捗です進捗進捗かどう進捗どうどうかどうですです進捗どうか進捗進捗どうですか???
96回で煽られました
nil
```

抽象化は大事ですね.

でも, 賢明な皆様は抽象化にかまけて目の前の具体的な納期を落とされないよう, くれぐれもご注意くださいね.

進捗どうですか?