シンギュラリティと怠惰
この記事は VOYAGE GROUP techlog / Advent Calendar 2016 の記事として書いています。ダラダラした記事になっているのでお暇な時にどうぞ。
今年の個人的な流行語を挙げると「今夜勝ちたい」と「シンギュラリティ」になるでしょうか。前者は今年1年間、ストリートファイター5を毎夜練習しては負け、練習しては負けを繰り返した気持ちが強く現れている良いワードです。弊社はゲームサークルがあり、日夜練習に励んでいるので興味がある方は連絡ください。後者はなんか今年のワードな気がしたのですが、以下を見ると 2014 年から盛り上がり続けているようですね。
このもし来るかもしれないシンギュラリティ後の世界、人工知能が人間を上回った世界とされていますが、これによりロボットに支配されちゃうー、とか仕事がなくなっちゃうー、とか色々想像されていますね。
ところでこれは生筋子(450gほど)を塩水につけて、菜箸でほぐしているところです。
人間のやることがなくなっても時間と自由がある限り、生筋子を菜箸でほぐすとか色々おもしろいことに挑戦できるようになったりするのではないかと僕は楽観的です。あ、でもやっぱりごろごろしてるだけの怠惰な生活を送るかも……。
さて、僕は人工知能はプログラムを書けるのかというものに興味があります。プログラムを書いてくれるならだらだらゲームをして過ごせるので
プログラムを自動生成する方法で自分が考えつくのは、大量のコードを集めてそれらを何らかのブロックで分割し、期待した結果がでるようにブロックを並べ替えできるよう機械学習していくことです。なのでそのようなものを作ります。下の記事でも Python コード片を集めて、それっぽく動きそうなコードを生成しています。
次は筋子の筋を取り除くために塩水で洗っていきます。手でかき混ぜて浮いてきたゴミをすくって捨てる、を数回繰り返します。
洗った後です。きれいになりましたね。ちょいちょい筋が残っているのは取り除くのがだるくなったからです。
それっぽいコード生成する太郎は残念ながら自分で仕事を見つけてくれないので、いったんお仕事を与えましょう。今回は数列のソートをしてもらうことにしました。ちなみに機械にソートさせることすら放棄した記事はこちらです。
ソートをするための簡単な DSL を組んで、コード辺の組み合わせを選んでいく強化学習を目指します。強化学習というとあれです。人工知能が Atari 2600 の Pong を攻略する動画を見たことある人もいるのではないでしょうか。ざっくり言うと、ある状態で、ある操作をした結果の報酬をもらい、たくさん報酬がもらえるようにどういう操作をすればよいのか学習していくというものです。僕もたくさん報酬がもらえるように日々頑張っています。(゚∀゚)人(゚∀゚)ナカーマ
醤油・酒・みりん・昆布を入れて沸騰させた様子です。つけ汁になります。
DSL を組むと言いましたがやっぱり面倒なのでやめましょう。操作できることをもう少し単純化して、数値の入れ替えを繰り返すことでソートを目指すようにしました。コード片の入れ替えではなく、213
の 1 番目と 2 番目を入れ替えるというような操作の学習になります。もはやコードを書かせるという目的はなくなりましたが仕事ができれば良いのです。
実際にコードをアウトプットすることの利点は、人間が理解できる(かもしれない)アウトプットにすることにより、人間による調整の余地があるところかなと思います。まあ人の手を介さずになんかいい感じにちゃんと動けばええのや。
実際はレジスターなどを作ってもう少し複雑な操作をできるようにした結果、人間には理解できないような操作が有効に働くのではみたいな、ほのかな期待があったのですが面倒なのでやめましょう。
おっ、もしかしてこれはいくらですかね?そうですね。美味しいです。
今回は Q-leaning という手法を使います。詳細は以下が分かりやすいです。
Q-learning で必要なものは以下の 3 つです。
- 状態
- 操作
- 報酬
状態は今回の場合、0123456789
のような数列になります。その状態下でどう操作(入れ替え)をしたらどういう状態になったか、その結果の報酬はどうだったかということを知り、操作に対する期待値を学んでいきます。
報酬はどうやって設定すればよいでしょうか。ちゃんとソートできたら 1, それ以外は 0 にしましょう。良い報酬を与えるとよく働いてくれるかもしれませんが良い報酬を考えるのも面倒ですね。
学習の時、次にどういう操作をしようかというのを決める方法に ε-greedy というものがあるのでこれを使います。これは通常時は期待値の高い操作を選んでいき、ある確率でランダムに操作を選ぶ方法になります。たまに横道にそれるともっといい方法があったみたいなのを生み出します。
おっとここでカニが登場だー!昨日はカニ鍋をしました。欲望に忠実ですね。
今回は要素数が 7 の数列を並べ替えます。
学習をせずにランダムに入れ替えていくと、10271 回で並び替えられました。頑張ったね。5 回学習すると、15 回の並び替えでうまくいきました。本当は入力値を何回も変えてその結果の平均を見たりするべきですが面倒さがあります。
入れ替えの様子です。実行したコードはこちらになります。
input: 6,4,5,2,1,3,0 ... ----------- step 10, SwapPosAAndPosB(6,4) [4, 3, 1, 0, 6, 2, 5] -> [4, 3, 1, 0, 5, 2, 6] max_q: 0.000000 ----------- step 11, SwapPosAAndPosB(3,2) [4, 3, 1, 0, 5, 2, 6] -> [4, 3, 0, 1, 5, 2, 6] max_q: 0.512000 ----------- step 12, SwapPosAAndPosB(5,0) [4, 3, 0, 1, 5, 2, 6] -> [2, 3, 0, 1, 5, 4, 6] max_q: 0.640000 ----------- step 13, SwapPosAAndPosB(2,0) [2, 3, 0, 1, 5, 4, 6] -> [0, 3, 2, 1, 5, 4, 6] max_q: 0.800000 ----------- step 14, SwapPosAAndPosB(5,4) [0, 3, 2, 1, 5, 4, 6] -> [0, 3, 2, 1, 4, 5, 6] max_q: 1.000000
知ってる状態までランダムに入れ替えまくって、知ってる状態になったらどんどんゴールを目指していくという感じですね。
100 回学習では 4 回の入れ替えです。よくやった。
step 0, SwapPosAAndPosB(6,0) [6, 4, 5, 2, 1, 3, 0] -> [0, 4, 5, 2, 1, 3, 6] max_q: 0.512000 ----------- step 1, SwapPosAAndPosB(4,1) [0, 4, 5, 2, 1, 3, 6] -> [0, 1, 5, 2, 4, 3, 6] max_q: 0.640000 ----------- step 2, SwapPosAAndPosB(5,2) [0, 1, 5, 2, 4, 3, 6] -> [0, 1, 3, 2, 4, 5, 6] max_q: 0.800000 ----------- step 3, SwapPosAAndPosB(3,2) [0, 1, 3, 2, 4, 5, 6] -> [0, 1, 2, 3, 4, 5, 6] max_q: 1.000000 -----------
早くなってよかったみたいなこと言ってますが、学習により知識が増えた結果、知ってる状態に早く到達できるようになっただけです。早くソートしてほしいなら、入れ替えステップ数に応じて報酬を減らしたり増やしたりする必要がありそうです。問題が複雑になると報酬についてもやはり色々考えないといけなくて面倒ですね。
強化学習をベースにしたやり方で良いなぁと思うのは、入力に偏りがある場合それに最適化されたソート方法が学習されるかもしれないというところです。便利やーと思ったのですが、今回やったのと似たような研究があるよと同僚に教えられたのでままならない。
これは曇っていますが、カニの出汁をよく吸った雑炊といくらですよ。
要素数 7 桁で頑張ってましたが、10 桁にしたら学習に異常な時間がかかるようになりました。最初の知識が少ない状態で、ランダム入れ替えによるゴール到達が難しくなったからだと思います。学習のコードは雑に書いたような気がしますが、遅くなった理由はプログラマーの腕がヘボだからだったというだけではありません。たぶん……
Pong を人工知能にクリアさせる場合、どれくらいの状態があるのか考えるだけでお腹いっぱいです。ここでニューラルネットワークが出てきます。Q-learning にニューラルネットワークを組み込んだ DQN (Deep Q Network)というものがあり、先のリンク先に詳しく書かれています。DQN で頑張るところまでやりたかったですが、いくらや鍋を作るのに夢中になって面倒になりました。
来年は怠惰にならないよう頑張ります。年始は人工知能と共に暮す生活を夢見たいです。
次の VOYAGE GROUP Advent Calendar の記事に期待!
一本集中〜↑↑
VOYAGE GROUP Advent Calendar 2015 の 22 日目のエントリです。
小学生時代での成績表に「落ち着きがない」と書かれ何も変わらないまま大人になった自分には、長年の課題として集中力がないというのがあります。サンタさんには集中力をくださいと願っているのにいつも届かないです(クリスマス要素クリア)。僕は常に進捗が最高なのですが、たまに「今日はいつもより進捗が最高じゃないな?」とか思ったり、それが続くと効率的に動けているかとか集中力のこととかについて考えます。今回はその集中を続けるためのポモドーロテクニックという枠組みで必要なタイマーを作った話をします。
集中力の意味を弊社自慢の辞書横断検索サービス「コトバンク」で引いてみると(VOYAGE GROUP 要素クリア)
とのことです。今回は集中力がない状態というのを、ある物事に注意を向けにくい状態ということにします。集中できなくなってしまう要因としては色々あります。Twitter や Slack の通知の割り込みが入ってきてしまうので作業中にその対応をすることになってしまったり、辛いものを食べてなんだかとてもお腹が痛かったり、火事ですぐに作業を中止しないといけなかったり、文章中にそれほど重要ではないリンクや修飾語や(カッコ書き)があったりする、などが物事を進めることを邪魔します。集中力がない場合はやらないといけない・やりたいと思うような気になることが他にもあって、そっちに手をつけてしまうわけです。一本に集中したい。
ポモドーロテクニック
ポモドーロテクニックは集中するためのシンプルな枠組みです。
- 終わらせたいタスクを選ぶ
- 25 分、タスクに取り組む (それ以外はしない)
- 短い休憩
- 2 と 3 を 4 回繰り返したら 20 分の休憩
というシンプルな枠組みです。この枠組みがもたらす効果としては「他のことに気をやらなくていいので思考がシンプルになる」「割り込みを無視できる(強い意志が必要)」とか色々あるとは思うのですが、僕は「必ずタスクに取り組んでいるので前に進む」というのと「25 分は取り組もうという気にさせる取っ掛かり」の2つが大きいと考えています。僕の好きなコラムに「射撃しつつ前進」というジョエル・スポルスキが書いたものがあります。このコラムには余計なことに足をとらわれずに物事を「前進し続けること」とそのためにまずは「ただ始めること」が大切というようなことが書かれています。ポモドーロテクニックはこのコラムにある「前進し続ける」ことと「ただ始めること」が実現できて、お気に入りのテクニックです。
このポモドーロテクニックをするためにはタイマーが必要です。物理的なタイマーからスマホやデスクトップで動くソフトウェアまで様々なものがあり、何を使用しても問題ありません。今回は自分のニーズにあったポモドーロテクニック専用タイマーを作ってみました。会社のエンジニア1名とディレクター1名の3人で開発合宿に行った時に作ったものですが、開発合宿も割り込みを減らしたり短い時間で達成することを明確化したりして集中するのに良い枠組みです。
減量
ところで減量の話です。辛い #ajiting という記事でも触れたのですが、今年は 7 月から減量を始めて 3 ヶ月くらいで約 8 kg の減量をしました。少しは痩せたいというのと他の人が始めたのでなんとなくという軽い気持ちで始めたのですが、途中から楽しくなってちゃんと続けられました。こういうのは減量しないと死んでしまうとかの明確で重要な動機が無いと続けられないと思っていたのですが、これもただ始めることが大切だったと思います。
他にも続けるのに必要だったものがあります。
- 現状を見てもらう
- 記録をして可視化、現状把握をする
減量中は Google スプレッドシートに記録してグラフ化し、会社のダイエット仲間に見てもらっていました(#gachi_diet という Slack チャンネルがあります)。共有しているのでお前減量できてないじゃんと言われたくない気持ちから減量をやめることへの抑止力になりました。記録の可視化は現状把握と改善のための次のアクションに繋がります。少し体重増えたなーということが数値で分かったらその日の食事を気にしたり、結果が出ていることが分かって減量を続けるのが楽しくなります。
ポモドーロテクニックは良いものです。でも、気がついたらポモる(ポモドーロテクニックする)ことを忘れて集中できてないモードになっていたりします。ポモるという大枠に入るための工夫として、減量の体験が参考にならないかと思いました。なんかイタチごっこになっているような気もしますがとりあえず試してみることが大切です。ということで下記のような機能のあるタイマーが欲しくなりました。
- ポモに入ったというのとステータスを Slack に投稿(現状を見てもらう)
- タスクに取り組んでいる時間を記録、グラフ化
計測する値は本当に取り組んでいる時間でいいのか?という疑問はありますが、ひとまずこれで進めましょう。
ポモ子
今回作ったタイマーの様子です。名前はポモ子です。
- 魅惑の機能たち
- 時間が経過するか next を押すたびに「Working」「Short Break」を切り替えられる
- 4 回目の Short Break が Long Break になります
- なんと各ステータスの時間を変更可能!ソースコードのハードコーディングされた部分を弄ってみてください!自分は Whorking が 15 分で、Short Break が 3 分の設定でやってます(変更機能は作ったのですが画面作るのが面倒になってやめた)
- Working の時間を記録して日毎にグラフ表示
- 記録はじめた日からその日までのグラフが表示されます!直近のだけみたい時はうっとおしくて便利!
- pouchdb-server を起動していれば永続化とデータ共有が可能
- デスクトップアプリケーションとして動作
- メニューバーに残り時間が表示されます
- 各ステータスが終わると「Hi!」とだけ出てくる簡素で雑でシンプルな通知!
- Slack 投稿機能はまだ!
- 本当は今日実装しようかなと思っていたのですが、社内バーの AJITO で寿司を食べていたら時間がなくなりました
- 時間が経過するか next を押すたびに「Working」「Short Break」を切り替えられる
コードは以下になります(徹夜で作ったのでひどいのはご愛嬌)。
基本部分は Haskell 風味な AltJS (HTML や CSS の出力もできる) の Elm で実装し、デスクトップアプリケーション化は Electron を使っています。メニューバーへの通知機能を利用したので、Electron の方のコードを少し書く事にはなりましたが、Elm で出力した HTML を包むだけでデスクトップアプリが作れて Electron 便利という感想がありました。
タスク取り組み時間の記録は永続化したり共有したかったので localStorage ではなく PouchDB を使用。DB に接続できない間は localStorage に貯めておいて接続できたら送るという事ができて、DB 側の環境がなくてもスタンドアロンで動きます。
Elm の公式で提供されている例と我流でストリーム志向なクライアントサイドプログラミングをすると、パターンを知らないためかどう書くんだろうと迷うところが多かったので、色々調べてみたい気持ちになりました。Rx 周りを探せば良さそう。
ちょっと使ってみて
自分が作ったという愛着で最初の方は使っていましたが、測定値がタスク取り組み時間だとあんまり参考にならなくて、ポモるためのきっかけには関係ありませんでした。取り組み時間よりも何に取り組んで、それを達成したかどうかというところに関心があるかなぁと思うと、なかなか定量化し辛いですね。やはり Slack 通知機能を使って監視してもらうしかない。
まとめ
集中し続けるための工夫として、ポモドーロテクニックで使用するタイマーを作ってみました。まだまだ理想的なタイマーはできないようです。改善していきたい!
明日 12/23 の担当は @hironomiu さんです!もうすぐクリスマスね〜!
辛い #ajiting
#ajiting Advent Calendar 2015 - Adventar 15 日目の記事になります(15 日と 16 日の担当を交代しました)。 tabelog.com
201407300043
201409102028
201502190150
社内 Slack #daidai チャンネル AJI 氏より
201502210152
社内 Slack #daidai チャンネル BRT 氏より
201507090004
社内 Slack #daidai チャンネル ART 氏より
201507202025
201509092204
201509230119
201509260029
201509302205
社内 Slack #daidai チャンネル MMM 氏より
201510232244
社内 Slack #daidai チャンネル BRT 氏より
201511111929
201511202351
2015112042340
社内 Slack #daidai チャンネル BRT 氏より
201511242159
201512012354
201512142050
最後に
今年 7 月から減量をはじめて 8kg 減減りました。
明日の #ajiting Advent Calendar は kuromatsu さんです。
YAPC::Asia TOKYO 2015 に行きました
自分の中では 4 回目の YAPC::Asia TOKYO に行ってきました。初めて行ったのは新潟で学生やってた時に Niigata.pm で知り合った人たちが行くというので気になり、学生枠で参加したものでした(チケット無料!)。その時にいい話が聞けたり LT ソンとかやったりして楽しかったので裏方もやってみたいと思い、上京してからは 2 年連続スタッフで参加していました。
今年は最後の大花火というので、普通に参加しても当たり前のように楽しいやろ通常参加 2 回、スタッフ参加 2 回になってキリもいいやろとてきとうな理由をつけて通常参加。以下は見たトーク一覧。
- 見た講演
- HTTP/2時代のウェブサイト設計
- Perlで学ぼう!文系プログラマのための、知識ゼロからのデータ構造と計算量
- PietでLISP処理系を書くのは難しい
- Adventures in Refactoring
- Perl6 on JVM: It works??
- Perl で RTB の最前線を闘い抜く
- Lightning Talks
みんなうまくまとめてるだろうから一覧だけ。両日とも昼から参加してたから見たトーク数は少ないかな。盛り上がってるトークが多くて、可能なら分身して全レーン見たかった。
一番テンションがアガったのは「HTTP/2時代のウェブサイト設計」で、はじめから HTTP/2 に最適化されているサーバーを実装してみんなに使ってもらおうという気持ちが伝わった。
ノベルティについて
今回は VOYAGE GROUP として企業スポンサーのノベルティを手配をする担当の1人でした。企業パンフレットと AJITO と描かれた透明なステッカーです(AJITO は社内にあるバーのこと)。ステッカーは YAPC スタッフの方から言われていた分よりも少なめに出していました。自分のトートバックには入ってなかったから少なかったかーと思ってしまったけど、ノベルティ一覧の写真をあげている方の記事を読むと存在しているようで安心。
AJITO というバーは社内のエンジニア文化をよく表している場で、弊社には自慢しているこんな文化があるんだよと認知していただきたいものです。ノベルティによる周知についてはノベルティについては @fshin2000 さんの記事で
主観だけど、ステッカーは小さいので、イベントの文脈にあわせて頑張ったり、何かひねりがあったほうがいい気がします。もしすげークールなサービスならロゴだけでいいかもしれないです。そうでなくて、これから認知を増やしたい、新参サービスだったら、もう少し違う球を投げたほうが埋もれずに済むかもですね。
という話があって、ノベルティを作る際、いかに埋もれないかを意識してなかったことに気付きました。確かに、今回の YAPC::Asia TOKYO のイベントではスポンサーが多いのでノベルティも多い。ノベルティ作成に関わったので他の人よりもノベルティに興味がある自分でも、全てのノベルティやパンフレットに目を通していない有り様です。次にノベルティ作ることがあるなら、他のノベルティに埋もれない方法を考えていきたい。
まとめ
スタッフの皆さんやトークをした皆さんはお疲れ様でした!ありがとうございます!YAPC 最高!
InfluxDB の Schema 設計
時系列DBで SQL like なクエリが書ける InfluxDB と、その可視化ツールである Grafana を使おうかなーと思っているのだけれど、InfluxDB はバージョン 0.9 系がもうすぐ出るらしい。気になるのは 0.8 系と 0.9 系間の違いと Grafana の InfluxDB 0.9 系への対応具合になる。0.9 系はまだ出ていないし、Grafana の方もちゃんと対応しきっていない。今 InfluxDB 0.8 系でを使い始めて、Grafana が 0.9 対応した時に移行できない可能性があるという微妙な時期。
Clustering, tags, and enhancements to come in 0.9.0 を読むと 0.9 の機能で気になるのは Tags
のサポート。
Tags
の話の前にまず 0.8 の時代での Schema 設計の話をする。例えば server01
のコアごとの CPU load を記録したいとする。コアが2つあって、ひとつめを 1 番、ふたつめを 2 番とする。これを表現する方法はいくつかあって、データ点内にコア情報を含めるか、Series(名前の通り時系列なデータ点が入るところ)の名前にコア情報を含める方法がある。
前者は Series 名を server01.cpu
、カラムに core
というものを作って以下のようなデータ点を入れる方法。
{"core": 1, "value": 1.2, "time": 1400425947368} {"core": 2, "value": 1.7, "time": 1400425947368}
後者はシリーズ名を server01.core.1.cpu
と server01.core.2.cpu
のふたつを用意して、server01.core.1.cpu
には {"value": 1.2, "time": 1400425947368}
を入れ、server01.core.2.cpu
には {"value": 1.7, "time": 1400425947368}
を入れるという方法。
InfluxDB Schema Design Guidelines に書いてあるけれど、前者のカラムに core
を定義するやり方は index が効かない。コア 1 のデータがほしいだけなのにコア 2 のデータも走査してしまう。
一方、後者の Series 名にコア番号を含めるやり方だとコア 2 のデータを走査するというようなことがない。どっちのコアのデータもほしい場合は select * from /^server01\./ limit 100;
というような、Series 名に対する正規表現で複数 Series を対象としたクエリを投げることができる。このやり方は SQL like という割には直感的ではなくて select * from server01 where core = 1 limit 100;
と書くほうが SQL を書くのに慣れた人は直感的な気がする。そういう要望が多かったのかどうかは調べてないので知らないけど、0.9 からは index 化される Tags
を用意したようだ。
Tags
がどういう風に使われているかは v0.9.0 ドキュメントの Schema Design や
Reading and Writing Data を読めば分かる。
Clustering, tags, and enhancements to come in 0.9.0 の Migration のところを見ると、0.9 系に上げるまでは Series 名にコア番号を含めるやり方と同様に <tagName>.<tagValue>.<tagName>.<tagValue>.<measurement>
みたいな Series 名にしておけば Tags
を自動的に定義してくれるっぽい。
実際のところどうなのか・本当なのかは 0.9 系を動かして確かめたわけではないので、雑記クオリティということでひとつ。
正則化
暴君ハバネロ シビ盛というのを買ってビール1杯だけ飲もうとしたら、辛い & ウマいの効果で 1缶/5口 という感じになってしまった。最近は山椒が好きで(といっても2年くらいずっとだけど)メニューに山椒と書いてあるだけで頼んでしまう。
今日は PRML (パターン認識と機械学習) 勉強会の日でもあった。上巻 3.1.4 「正則化最小二乗法」のところ。回帰モデルに正則化項を与えて過学習を防ぐ。正則化項には q というパラメータがあり、q = 1 のときを lasso とか L1 ノルムとか呼ぶ。L1 ノルムはある線形回帰モデルがあった時、パラメータのいくつかが 0 になるような解を得やすく、不要なパラメータを削る次元削減に使える。回帰モデルにおけるL1正則化とL2正則化の効果 - old school magic が分かりやすい。
次は 3.2 バイアス-バリアンス分解の担当になっている。正則化のところでもラグランジュ未定乗数法とかを余裕で忘れていて(大学で習ったはずなのにもはや名前だけ知ってるとかいうレベル)、本当についていけるのかと思う。実際、今までやったところも1/10くらいしか理解していないと思う。でも、分からないところは分からないというとメンバーが一緒に考えてくれるので助かるし気楽で良い。
あるバッチが使ってる split コマンドで split: Output file suffixes exhausted
というエラーが出ていた。split は指定したサイズでテキストファイルを分割したりするのに使うコマンド。分割後のファイルは yabai_text.aa, yabai_text.ab, yabai_text.ac ... yabai_text.zz
とファイル名の suffix に a-z の文字列がつく。split コマンドのデフォルトでは zz (676 個) までしか分割せず、これを超えるとエラーが出る。a オプションで suffix の桁数を増やすことができるので、分割後のファイル数が 676 個を超える場合はこれで解決できる。
なんでデフォルトの最大分割数が決まっているのかが気になった。ディレクトリ内のファイル数の上限とかに配慮してなのかな。
2015-05-26 空腹関数からオブジェクト
連日雑記。2 日も続くなんて明日も地震が起きてしまう。
毎週月曜の TaPL 読みも 20 章「再帰型」に来た。20 章なんて遠いなと思ってたけど、難しい定義とかすっ飛ばしてトピックだけ拾い読みしてるから早い。しかし理解度としてはぬるめ。まあそれでも面白いところはいっぱいある。
今日読んで面白かったところは空腹関数からオブジェクトまでの流れ。空腹関数は f 0 1 2 3 ...
というふうに引数を無限に受け付ける関数で、ひとつ引数を受け取るとまたひとつ引数を受け取れる関数を返す。空腹関数は不動点演算子を使って定義できる。パクパク引数を食べまくるパクパク。
こういう構造を見るとストリームになりそうと思うよね。はい、ちゃんと定義されている。ストリームは空腹関数と同じように引数として unit を受け取り、何らかの数とストリームの組を返す関数として定義されている。この組の第一要素を head、第二要素のストリームを tail とみることができる。例では upfrom0 という 0 からの無限な整数列を定義している。
ストリームを一般化したプロセスというものもある。ストリームでは unit を受け取っていたが、ある数を受け取れるようにし、数と新たなプロセスを返すものをプロセスと呼ぶ。例えば今まで入力された数の総和を返すようなプロセスがあるとする。2 を渡すと {今までの総和に2を足した数, 新しい総和を返すプロセス}
というような組が返る。この組の第二要素のプロセスに 3 を渡すと、{今までの総和に 2 を足した数に 3 を足した数, 新しい総和を返すプロセス'}
という組が返ってくる。TaPL では第二引数の新しいプロセスに数を送る send という関数と、第一引数の値を取り出せる curr (current?)を定義しており、下記のようなことができる。
curr (send 20 (send 3 (send 5 process))); # => 28 : Nat
第二要素の 新しいプロセス
というものについて考えてみるとこれは状態を表しているようにも思える。最近は FRP が話題だったような気もするので(もう古い話題?)、ストリームといったら状態とピンとくる人も多いかも。
はい、ここでオブジェクトが出てくる。プロセスで、第一要素に何らかの数が入っているとしていた部分をレコードにしてあげれば、純粋関数的なオブジェクトを表すことができる。例えばレコードのフィールドとして定義されている関数で、increment のような状態を変える関数は、次の状態のオブジェクトとプロセスを返す。このようにして状態の変わるオブジェクトを表すことができる。かっこいい。
文だけだとよく分からないと思うので TaPL の定義を見たほうが早い(定義を書くとほぼ TaPL の写しになってしまうので文で書いてみた)。状態を変える関数が複数定義されているオブジェクトがあって、その状態の無限の遷移(無限に深い木)を想像すると夜も良く眠れそうな気がする。
ここまで書いてからストリームからオブジェクトの話は SICP にもあったなぁというのを思い出した。すぐ思い出せないといけないな。