気付いてなくても存在するもの

新興のプログラミング言語として Rust がある。 この言語は Ownership と Lifetime という概念を導入することで不用意なメモリ操作を無くそうとするところに特徴がある。 しかし、 C などでプログラミングするときはオブジェクトの所有権や寿命は管理していなかったのかというとそんなことはない。 どのポインタがオブジェクトに対して責任を持つのか、オブジェクトを解体するのはいつか、プログラマの頭の中では管理していたはずだ。 概念として切り出して言語処理系で辻褄を確認することが可能だという気付きが Rust の設計に反映されているのである。

そんな事例は他にもある。 Scheme で言語仕様に現れる「継続」だって Scheme 特有のものではない。 Scheme で継続が特別なのは第一級のものとして言語仕様に盛り込まれてプログラマが陽に扱えるようにしたというところが特徴的なのであって、表には出てこなくても他の言語でも継続は存在している。 (私が Scheme の言語機能としての継続をいうとき「継続」ではなく「第一級継続」と呼んでいるのはそういう理由からだ。)

一方で、言語処理系が確認する部分を減らしているものもある。 動的型の言語を使っていても想定していなかった型を受取ればエラーになるわけで、しかしプログラムが動作するのはプログラマの脳内で型を管理しているからだ。 大抵の場合には実行時に最低限の確認は入るが型を陽に記述することはない。 これらは何を自動化するかの問題であって正解があるわけではない。 プログラマが自分の能力で管理可能なものであれば言語処理系の支援は邪魔になりうるし、プログラマの能力では管理しきれず自動化して欲しいところであれば言語処理系に頑張って欲しい。

プログラムは書いた人以外の人が読むこともあるので、言語処理系としては無くてよくても書いて欲しい情報というものはある。 Haskell のような強力な型推論機能で型を特定できるのであっても型を明記することがあるのはその方が読みやすいからだ。

そんなわけで、あるプログラミング言語が「簡潔な文法」だとか「アルゴリズムを表現するのに集中できる」といったことを利点として挙げていてもそれが利点とは限らない。 というより利点には違いないだろうがそのために他の利点を捨てていることもあるし、文法が複雑だと思ってもそれが必ずしも不利には働かない。 自分が気付いてなかった概念がそこに「有る」と気付かせてくれるのは無駄なことではない。

Document ID: 120e39dbdb463b05448c14d9a01cfb53

Haskell で TL/1 構文解析器を書いた

古いプログラミング言語 TL/1 の構文解析器を Haskell で書いてみた。

https://github.com/SaitoAtsushi/TL1hs

以前に Scheme で書いた TL/1 から C への変換器は仕様が曖昧なところを寛容な方向に解釈して実装したが、今回は自然に考えて、あるいは自然に実装するとこうなるという感覚を優先した。 TL/1 が実際に使われていた当時の処理系に近い解釈になっていると思う。 (貧弱な演算資源しかない環境では先読みが少なくなるように設計されていたに違いないと決め付けている。)

しかし、意図的に変えている箇所もある。 TL/1 の変数の大きさは 1 バイトであるが、その範囲で扱えないはずの大きな数値リテラルを許容するといった挙動にしている。 変数の大きさを 2 バイト以上に拡張した処理系にすることを考えているので構文解析の段階では大きさの判断をせず、もしエラーにするにしても後の工程にまかせるという意図だ。

既存の処理系、そして既存の処理系が動作する環境を私は持っていないので実際に動作を確かめて合わせるということも出来ない。 いくつかある TL/1 はそれぞれかなり違う部分もあるようなので、私のこれもそうした変種のひとつと考えて昔の処理系に対する再現性には期待すべきではないことには留意して欲しい。

Document ID: 4ee6938b5cc436a0720e06ce8eb0f0a9

分配エラー

プログラムというものは想定する使い方できちんと使えるようにすることは当然としても、使えてはいけないときにはエラーにすることも重要であるということを以前に書いたことがある。

エラーにしたいとき - 主題のない日記

しかし、 Scheme ではそれは少しばかり難しい。 R7RS では「エラーである」と表現されていても処理系の裁量で適当に無難な結果を返してもよいことになっている。 (エラーを検出して通知しなければならない場合については別の表現で書かれている。) そうした状況では処理系がエラーにすることを期待せずにプログラマが陽に書かなければ思わぬ事態になるかもしれないのである。

そのひとつが省略子付きのパターン変数にマッチしたものの分配だ。

例として以下のようなものが挙げられる。

(import (scheme base) (scheme write))

(define-syntax zipm
  (syntax-rules ()
    ((_ (x ...) (y ...))
     (list (list x y) ...))))

(display (zipm (1 2) (1 2 3)))

このときパターン変数 x にマッチするのは (1 2) であり y にマッチするのは (1 2 3) なので (x y) ... は数が合わずエラーである。 しかし、それを通知せずに短い方に長さを合わせる処理系は多い。 そうした処理系でも確実にエラーにすることを考えると以下のような書き方が思い付く。

(import (scheme base) (scheme write))

(define-syntax %zipm
  (syntax-rules ()
    ((_ (tx ...) (ty ...) () (y ys ...))
     (syntax-error "invalid input"))
    ((_ (tx ...) (ty ...) (x xs ...) ())
     (syntax-error "invalid input"))
    ((_ (tx ...) (ty ...) () ())
     (list (list tx ty) ...))
    ((_ (tx ...) (ty ...) (x xs ...) (y ys ...))
     (%zipm (tx ... x) (ty ... y) (xs ...) (ys ...)))))

(define-syntax zipm
  (syntax-rules ()
    ((_ (x ...) (y ...))
     (%zipm () () (x ...) (y ...)))))

(display (zipm (1 2 3) (1 2 3 4)))

事前に全てを想定できるわけではないにしても、特に汎用性の高い部品については入力を信用しないということを意識するのは重要であるとあらためて思う次第である。

Document ID: b3f37dd8178a9063779cf788e179efea

フィード

ブログ、またはその他のウェブサイトの更新を通知する仕組みとして RSS が知られている。 複数の版が異なる思想で設計されているので統一されずに混在してはいるのだが、 RSS が更新通知の標準としての地位を確立しているといえるだろう。

RSS を「更新通知」と考えるか「情報配信」と考えるかで利用形態、運用に差が生じる。 私は主に配信の意味で利用している。 つまり、ブログの全文を RSS リーダで読むという使い方をしている。 しかし、サイトによっては更新されたという情報のみしか含めていないという場合もあって、ブラウザでサイトを見にいかなければいけないということもしばしばある。 結局は面倒くさくなって全文配信していないサイトは見なくなってしまったりもする。

複数RSS を集約してひとつにまとめてくれるサービスもある。 たとえばはてなブログの「購読」の仕組みだ。 はてなブログのユーザははてなブログがホストしている他のブログの更新を知ることが出来て、集約された RSSRSS リーダで見ることも出来る。

しかし、はてなブログが集約した RSS には各ブログの最新記事しか含んでいないのだ。 一度に (私が RSS を巡回するする間隔より短かい間隔で) 複数の記事が投稿された場合には記事を取り零してしまう。 RSS を利用する各ウェブサービスが更新通知を意図したものなのか、内容の配信を意図したものなのか、自分の用途に合ったものを選択する必要があるということは意識しなければならない。

Document ID: 9f93377b8efe23a44555361ff105eac7

キャッシュ

「小説家になろう」など、小説ホスティングサイトから ePub 形式の電子書籍データを作るスクリプトを私は作って使っている。

https://github.com/SaitoAtsushi/yomou-publisher

このスクリプトは新規投稿分だけでなく必ずその小説の全てを取得してから ePub にするデザインになっている。 過去に取得した分に変更がなくても再度取得するという無駄をしているのだ。 どうしてキャッシュしないのかというと、変更箇所が検出できない場合があるからだ。

まず、 URL は以下のような形式になっている。

http://ncode.syosetu.com/作品コード/話数/

作品コードはNコードという名前が付いている。 短編の場合は話数の部分はないが、ここでは複数の話から成る長編作品のみを考えることにする。

そして目次ページにはそれぞれの話がいつ投稿されたのか、最終改訂日はいつかといった情報も書かれている。

以上を踏まえてこのような例を考えてみる。

パス 表題 投稿日 更新日
/n1234i/1 1日 -
/n1234i/2 2日 -
/n1234i/3 2日 -
/n1234i/4 3日 -

この時点で一回スクリプトを走らせて電子書籍化したとしよう。

この後で「承」が流れに合わないと判断して削除するとこうなる。

パス 表題 投稿日 更新日
/n1234i/1 1日 -
/n1234i/2 2日 -
/n1234i/3 3日 -

パスの話数の部分は常に連続する。 削除があるとその分を詰めて常に連続するようにさせられるのだ。 この挙動が問題をややこしくする元凶である。

更に、元々は「転」だった話こそが「承」にふさわしいと考えて改題するとこうなる。 (改題も内容の更新と同様に更新扱いされて更新日が変わる。 改題した日は4日とする。)

パス 表題 投稿日 更新日
/n1234i/1 1日 -
/n1234i/2 2日 4日
/n1234i/3 3日 -

この状態で再びスクリプトを走らせて電子書籍化するとなると、前回と比べてどこが改訂されたか判断できるだろうか。 ここから読み取れる可能性は複数種類ある。 「承」の内容が改訂されて「転」が削除されたようにも見えてしまうのだ。 同じようなことが最新話数あたりで起こると更新されたという事実さえ見えない場合も有り得る。

もちろんそういったことが起こるのはごく稀だとは思う。 しかし確実に起こり得ることだ。 だから私は、いっそ全くキャッシュしないという選択をしているのである。

Document ID: cb64fd3f95388f19b93fb404bf760cbb

ずるい人工知能

時空の覇者 Sa・Ga3」というゲームボーイ用のソフトがある。 ゲームシステムは特に目立ったところのないよくある RPG で、戦闘システムはターン制である。 ターン制というのは、味方全員がどう行動するかを入力してから敵味方双方が素早い順に実際の行動を開始するという方式だ。 行動の入力を終えたら都合が悪くてももう変更することは出来ない。

このゲームの戦闘ではプレイヤが行動を入力せずに「おまかせ」というモードを選択することも出来る。 このとき、ソフト内の人工知能が判断して行動するわけだが、ターンの最初に行動を決定しているのではなく、各キャラクタが実際に行動するときになって判断しているらしい挙動が見られる。 仲間のひとりが毒を受けたのをそのターンが終えない内に別の仲間が回復させるといった動きをするのだ。 事前に考えて行動を入力するプレイヤからすればおまかせモードの人口知能に対してずるいと感じても仕方がないだろう。

他にも落ち物パズルとして有名な「ぷよぷよ」では敵の人工知能が落ちてくるぷよを都合に合わせて色を決定しているという例は有名だ。 もちろんプレイヤは落ちてくるぷよの色を変えたり出来ないのだから対戦条件としては不公平である。

とはいうものの、プレイヤの肩代わりをする人工知能があからさまに馬鹿で弱ければプレイヤとしては不満であるし、敵に張合いがなくてもそれはそれで不満を感じるだろう。 結果的にそれほど不満がないように釣り合いを調整できていることを考えればこれはこれで悪くない選択なのかもしれない。 もちろん、人間並の判断が出来る人工知能が公平な条件で動くことが出来ればそれに越したことはないのだが、そこまで作り込むコストはかけられないだろうし、機械の計算能力の上限によってどうしても制限されてしまうことは仕方がない。 こういったゲームにおいて人工知能はずるくてもよいという割切ったデザインが選択肢としてあるということは興味深いと思う。

Document ID: 876d8c7356b9234db9163b6cc216a0ab

神はクオリアに宿る

雄大で美しい自然を見たとき、別分野の理論が双対の関係にあることを知ったとき、命の危機を乗り越えたとき、そんなときに人智を越えた何かが世界を動かしているような錯覚を得ることがある。 あくまで錯覚だ。 だが、感じているに過ぎないから存在しないといえるだろうか。 喜びや怒りと同様に心を震わせる何か。 その感覚そのものを神と名付けることは出来ると思っている。

いいことがあれば喜ばずにはいられないし、敵には怒りを感じずにいることは難しいだろう。 神を感じるというのもそれと同質なものだ。 感じないでいろといわれてもそんなことは出来ない。 自身の内にある感覚、神と呼べなくもないそれを生き方の指針とすることも、そういう信念であるというだけの話だ。

しかし、その神は偉大な何者かではなく、自身の一部であることに自覚的であるべきだと思う。 自分の内面と対話してより善くあろうとするのは決っして悪いことではないが、所詮は矮小なる自分自身でしかないのだ。 正しいことも間違っていることもある。 正しくても間違っていても自分自身が決断したことだ。 結果を受け止めて活かさなければならない。

神を第三者であるかのように考えるというのは自分自身の決断を他人のせいにすることであり、無責任なように思われる。 私を世間一般でいうところの分類で考えれば無神論者ということになるだろうが、自分の内面の一部を神と呼べなくもないと考えているところからすれば自分を信仰しているともいえるだろう。 自らを神と称するなど傲慢だろうか。 私の神は私だけのものだ。 自分の決断の結果を受け止めるのが自分自身ならば傲慢でいられようはずもない。

Document ID: 3962f01a40072547df166c1a238f91ac