すなぶろ

Pythonistaを目指しているつもりが、Hyのおかげで無事脱線。Clojurian目指すことになりました。

Twitter の片隅で Clojure 愛を叫ぶ→ヘッドハンティングされた

――これは、地球と Land of Lisp を繋ぐ、新たな架け橋の物語

――絶望から這い上がろうとあがく男の目の前に垂らされた一本の蜘蛛の糸

――天にいるのは釈迦か、エイリアンか

――括弧に魅せられた者達の『奇妙な接触』を描くノンフィクションドキュメンタリー

続きを読む

Clojure 入門者による【チャットボットづくり】 Part5

大幅に更新が遅れてしまいました。実は腰痛で入院していまして、無事手術を終えて先日退院してきたところです。

入院中あまりにも暇なのでずっと Programming Clojure (第三版)を読んでいたんですが、 Clojure の奥義をそんな簡単に伝えていいの? と思うくらい濃い内容です。

ネット上の(日本語の)情報が少ない、あるいは古いのが難点だと言われがちな Clojure ですが、そもそも何かを勉強したいと思ったら、どんな言語であれ本を読んだほうがいいと痛感しました。例えば mapfilter, reduce は他の言語でも実装されていることが多いですが、Clojure の場合は戻り値を遅延させたり、即時適用してパフォーマンスを上げたりと、使い方が複数あります。その根底にあるのはいわゆる「Clojure らしさ」とでも言うべきもので、全体を通じて設計思想を学べる本は一度目を通しておいたほうが良さそうです。

ちなみに Programming Clojure の第三版は英語ですが、いまは自動翻訳がそれなりに賢いので、技術用語がわかれば苦労せず読めるはずです。第二版は通称「孔雀本」と呼ばれる日本語訳がありますが、Clojure 1.3 ベースであるため、最近追加された機能には言及していないという弱点があります。とはいえそれほど激しく仕様が変わる言語ではないため、第二版の内容がまったく通用しないということはないと思います。

さて、かくいう私はまだ半分も読んでいませんが、ひとつ言えることがあります。 Clojure 初心者はここに書いてある内容とコードを信用してはいけない、と。 本を読んだ結果、とてもお手本になるようなコードではないということが判明してしまったため、「オブジェクト指向から来た人間が関数型をなんとなくで書くとこうなる」というケーススタディとして、ニヤつきながら眺めることをおすすめします。

というわけで、今回は正規表現形態素解析を使って PatternResponder を実装……する予定でしたが、長くなってしまったので先んじて 形態素解析 を実装します。

続きを読む

Clojure 入門者による【チャットボットづくり】 Part4

前回は respond :random を実装しました。辞書も Map にしようというところまでは決めましたが、それをどう使っていくかに関しては説明していません。

ということで今回は『ランダム辞書の学習』『辞書ファイルの読み書き』をコーディングしていきます。同時にコード量も増えていくことが予想されるので、『ソースファイルの分割』についても。

目次

続きを読む

Clojure 入門者による【チャットボットづくり】 Part3

大切なことを言い忘れていました。このチャットボットの元ネタは、Ruby 向けの書籍『恋するプログラム』で紹介されていた unmo という名前のチャットボットです。今ではあまり聞きませんが、一昔前にチャット専門の AI がちょっとしたブームになった頃、そのトンチンカンな返事を揶揄して『人工知能』ならぬ『人工無能』と呼ばれていました。この MunoアナグラムUnmo となり、雲母(うんも・きらら)という宝石の名前にかかっています。

2005 年に刊行された古い本ですが、思考エンジンをひとつひとつ追加して完成に近づけていく感覚が楽しく、新しい言語を学ぶときはいつもこれを実装することにしています。著者の方が急逝されたのが残念でなりませんが、現在は電子書籍として復刊されているので、この記事でチャットボットに興味を持った方はぜひ手にとってみてください。

というわけで今回は『辞書』および『ランダムレスポンス』を実装していきます。

続きを読む

Clojure 入門者による【チャットボットづくり】 Part2

Coders At Work (原著: Peter Seibel, 翻訳: 青木靖)という本があります。第一線級のハッカー達に「エディタは何を使ってます?」「デバッグはどうしてます?」といった基礎的な質問から、「プログラマ全員が読むべき本はありますか?」といった抽象的な質問まで、Peter Seibelががっつり食いついてインタビューしている濃い本なのですが、Erlang の開発者であるジョー・アームストロングがこんなことを言っていました。

ーー  コードを書く前に多くの時間を考えて過ごすということですが、そのときには実際どんなことをするんですか?
アームストロング  ああ、メモを書きます。ただ考えているわけではありません。紙にいろいろ落書きします。(中略)もう1つ重要なことは、同僚に「君だったらこれをどう解く?」と尋ねることです。同僚のところに行って、「こうやればいいのか、ああやればいいのか迷っている。AかBか選ばなきゃいけない」と言い、そのAとBについて説明している途中で、「ああ、Bだね。ありがとう。助かったよ」というようなことがよくあります。

これを読んで不思議な気持ちになりました。私も質問の前の説明段階で自己解決してしまうことはよくありますし、他の人が勝手に納得して席に戻っていくのも見たことがあります。「あるあるネタ」ではありますが、そういうことはプログラマーとして力をつけていくと、すっかり無くなることだと思っていたのです。世界レベルの言語とフレームワークを作り上げる人にも、私と同じように脳みそが入っているらしいということが、妙にリアルに感じられました。

ーー  あるコンピュータサイエンス学科では教官の部屋にぬいぐるみがあって、教官を患わせる前にそのぬいぐるみに向かって自分の問題を説明しなければならないという決まりになっているそうです。「あの、クマさん、私が取り組んでいるのはこういうことで、このようなアプローチをしているんですが、……そうか! 分かりました」
アームストロング  本当ですか? 私もやってみるべきですね。

ーー  あなたのあの猫に話されるといいですよ。
アームストロング  猫にね。いやまったく! 私より若干年上で非常に頭のいい人と一緒に仕事をしているのですが、私が彼の部屋に行って質問をすると、どの質問に対しても彼は、「プログラムはブラックボックスだ。入力と出力がある。そして入力と出力の間には関数的な関係がある。君の問題の入力は何? 出力は? その2つの間の関数的な関係は?」と聞くのです。そして会話の途中のいずれかの時点で、私は「ああ、君って天才だよ!」と言って部屋を飛び出すことになるのですが、彼のほうは驚いて頭を振りながら、「いったい問題は何だったんだろう。あいつちゃんと説明したためしがない」とつぶやくのです。だから彼は問題を説明して聞かせるクマと一緒ですね。

前回は記事本文を書きながら「コードをリファクタリングせねばならぬ……!」という謎の使命感に燃えておりました。文章化するからにはせめて自分で納得行くコードを載せたいという虚栄心がそうさせたのですが、動機がどうであれエディタの操作を覚えたり、イディオムを検索したのは事実。

そう考えると、私にとってのクマのぬいぐるみはこのブログであり、読者ということになります。Rich Hickey 効果で図らずも別ベクトルの注目を集めた気がする前回のことは忘れて、今回も「なんでこういう仕様にしたんだっけ」と自問自答しながら、ちまちまコードを載せていきます。

Coders at Work プログラミングの技をめぐる探求

Coders at Work プログラミングの技をめぐる探求

続きを読む

Clojure 入門者による【チャットボットづくり】 Part1

最近 Clojure にハマっています。JVM 上で動くバイトコードコンパイル可能な Lisp 族で、 なんか書いてて楽しい です。とりあえず作者である Rich Hickey のアツい一言をご覧ください(参考1参考2)。

※元記事がリンク切れになってしまったので、意訳文章を削除しました。

というわけで、そんな Clojure を使って以前 Python で書いたチャットボット sandmark/unmo を再実装してみました。 勉強用なので間違った説明がある危険があります。ツッコミ歓迎です。 完全なソースコードsandmark/unmo-clojure にあります。

続きを読む

Hy の macroexpand が S 式じゃない→直した

Python へ直接コンパイルできる Lisp 方言 Hy が楽しいです。 しかし Lisp 族のつもりでいると思わぬ落とし穴があったり( let, cons がないなど)するので、 とりあえず macroexpand の結果を S 式にしてみます。

例えば let をマクロで定義してみましょう。 let の正体は lambda というか関数なので、 無名関数を使って実装します。

(defmacro let [values &rest body]
  (setv var-names (list (map first  values))
        var-vals  (list (map second values)))
  `((fn [~@var-names] ~@body) ~@var-vals))

デフォルトの macroexpand-1 の挙動はこんな感じ。

=> (macroexpand-1 '(let ((var 1)) (print var)))
HyExpression([
  HyExpression([
    HySymbol('fn'),
    HyList([
      HySymbol('var')]),
    HyExpression([
      HySymbol('print'),
      HySymbol('var')])]),
  HyInteger(1)])

待って読めない。

もうちょっと Lisper 向けの表現(= S-expression)に近づけてほしいので、 hy コマンドを起動するときに引数をつけてあげます。

  • hy --repl-output-fn=hy.contrib.hy-repr.hy-repr

これで macroexpand してみると、今度はこんな感じに。

=> (macroexpand-1 '(let ((var 1)) (print var)))
'((fn [var] (print var)) 1)

なぜか一行で表示されましたが、それでもこちらのほうが何倍も読みやすいです。

私は Emacs 使いなので hy-mode 経由で hy シェルを起動しているんですが、その場合は hy-shell-interpreter-args (デフォルト "--spy" )を変更すればよい感じです。

(add-hook 'hy-mode-hook
          (lambda ()
            (setq hy-shell-interpreter-args
                  (concat "--repl-output-fn=hy.contrib.hy-repr.hy-repr "
                          hy-shell-interpreter-args))))

Hy がもっと繁栄しますように。