見事に矛盾していると見抜けました。Prologといえば双方向代入これだけなら「どんなプログラミング言語で書いても一緒だろ」という物言いが付きそうなので、Prologならではのことをやってみようと思います。いじれば充足可能に出来そうです。部分を何か別の論理式に変えることを考えてみましょう。タブローを作るプログラムしか書いていない場合、多くのプログラミング言語ではこのようなことをするために新たにプログラムを書き足す必要がありそうですが、Prologではプログラムを一切書き換えること無くこのようなことが出来ます。いう少々複雑なものも出てきて面白いです。ような面白みのないものになってしまいました。今回は命題論理だけを扱いましたが、『論理学を作る』ではこのあとタブローを述語論理に対応させたりどんどん拡張していくので、こちらも暇があったらやってみようと思います。25日目のためのエントリかもしれません。今年の春、訳あって42個のプログラミング言語でLISP処理系を実装しました。ソースコードはすべてGitHub上においてます。作り終えたらやりたくなることは一つ。そんなわけで42個のLISP処理系の速度を計測しました。言語によって100倍以上の速度差があるため、すぐ終わるベンチマークでは速い言語同士の比較ができず、時間のかかるベンチマークだと遅い言語が終わらないため、3種類のベンチマークを用意して速度を比べました。使用し、各言語3回の計測を行い、その中間値を使用しました。言っていい加減な値なので、きちんとした結果を知りたい人は各自42個の言語処理系を用意して計測しなおしてください。この記事はクリスマスが暇で仕方ない人向けに必要以上に水増しして長くしてあるので、暇でない方は最後だけ読まれることをおすすめします。最初のベンチマークは相互再帰によるフィボナッチ数の計算。実行時間を測りました。15)計算自体は一瞬で終わる言語が多いため、実質的に起動時間のベンチマークといえるかもしれません。一部の言語処理系は起動に2秒以上かかるので、それにより大きく順位を落としています。対数グラフであることに注意してください。トップはCoffeScriptの0.002秒。続いてOCamlの0.007秒。3位はなんとOberon-2の0.01秒です。最下位はEの33.311秒、そしてCeylonの18.358秒、Scratchの6.25秒が続きます。Scratchは起動時間を含んでいませんが、それでもCeylonとEより速いのは明らかです。これには業界のアナリストも苦笑いでしょうか。目がいい人はグラフに41言語しかないことに気づいたかもしれません。これはIokeがスタックオーバフローを引き起こし計算を最後まで出来なかったためです。スタックサイズを増やす方法を調べたのですが見つからなかったので諦めました。次のベンチマークも相互再帰によるフィボナッチ数の計算。実行時間を測りました。LISPで書かれたLISP評価器の上で計算を行います。ある言語の上でLISP処理系が動き、そのLISP処理系の上でLISP評価器が動き、その上でフィボナッチ数の計算を行います。())読みやすい形のLISP評価器はGitHubにおいてあります。https://github.com/zick/ZickStandardLisp/blob/master/lisp.lsp今回はそれなりに時間がかかるため純粋に速さの勝負となります。リスト操作が非常に多いためコンスセルの実装方法も速度に大きな影響を与えそうです。これも対数グラフなので注意してください。トップはKotlinの0.764秒。続いてDartの0.782秒。OCamlの0.91秒が続きます。KotlinはJVMで動く言語ですがJavaを大きく引き離しているのが印象的です。JavaScript系の言語はどれも好成績を収めていますが、その中でも独自のVMを使うDartが他の言語を一歩上回りました。MLを倍近く引き離しました。F#ははるか後ろですがこれは私の実装方法が悪かったせいかもしれません。最下位はHaxeの130.88秒。Perlの104.08秒、Pikeの62.23秒が続きます。PerlやPikeのように型が柔軟な言語が後ろの方に来るのはある程度しかたがないことかもしれませんが、ガチガチに型を書いたHaxeがこうも遅いと悲しくなります。R、Icon、Ioke、Eはスタックオーバフローのため計測できず。Scratch、Io、Tcl、Falcon、Ceylon、Eiffelは5分以内に終わらなかったので打ち切りました。最後のベンチマークも相互再帰によるフィボナッチ数の計算。実行時間を測りました。LISPで書かれたLISP評価器の上で動く、LISPで書かれたLISP評価器の上で計算を行います。ある言語の上でLISP処理系が動き、そのLISP処理系の上でLISP評価器が動き、そのLISP評価器の上でLISP評価器が動き、その上でフィボナッチ数の計算を行います。())非常に時間が掛かることが予想されたため、二回戦の上位8言語に対してのみ計測を行いました。今回は対数グラフではありません。はじめに、V8上で動くCoffeScript、TypeScript、LiveScriptの3言語はスタックオーバフローため最後まで走り切ることができませんでした。MLは無事最後まで走り切ることができましたが、654.27秒というイマイチな記録。MLの倍以上速い296.88秒という記録でこれに続きました。記録は279.96秒。JVMで動く言語が多数あるなか本家が良い記録を残しました。記録は184.57秒。3位を大きく引き離しました。栄えある1位はKotlin。記録は177.09秒。JVM上で動く新しい言語が42言語の頂点に立ちました。ML、OCamlあたりが上位に行くことはなんとなく分かっていました。JavaがJVMの力により上位に来ることも予想していました。Kotlinが1位はおろか、上位に来ることさえ予想していませんでした。KotlinでLISPを実装した時に私は次のようなメモを書き残していました。2014-06-29Kotlinコンパイルして実行する方法が分からず長時間時間以上悩む。IDEの使い方しか載ってないような言語は等しく灰に還ればいいのに。whileとisの組み合わせがちょっと冗長。型を間違えると、お前は昔のC++かと言いたくなるくらい、怒涛の勢いでエラーを出す。言語としては悪く無いと思うけど、コマンドラインからの実行方法が書いてなくて悩んだせいで心象が悪い。思い返してみるとコマンドラインから実行する方法を見つけるまで非常に時間がかかり、ものすごく心象が悪かったんです。6月末に実装したわけですが、12月に時点では「ああ、IDEの使い方しか載ってなかった言語か」という程度しか覚えておらず、速かったか遅かったかなどまったく覚えていなかった訳です。ちなみに「whileとisの組み合わせ」というのはこれです。ように書きたいのですが、それが許されていないためネストも深くなるしコードも冗長になってしまいます。これはなんとか改善して欲しいですね。KotlinとJava、どうして差がついたのか...慢心、環境の違い同じJVMで動くKotlinとJavaでどうしてこうも差がついたのでしょうか。Kotlinのコンパイラがものすごい賢いコードを出力するのでしょうか。その可能性もなくは無いでしょうが、主な原因はコンスセルの表現の違いでしょう。Javaでは気まぐれで次のようにコンスセルを表現しました。cdr;}コンスもシンボルもLObjという一種類のクラスで表現し、data_フィールドに実体を入れて、型はtag_を見て判断します。してこの実装にしたかの詳しい経緯は12/24の記事を読んでください。詳しくない経緯は「気まぐれ」です。この実装でコンスのCAR部を取り出そうとすると、次のようなバイトコード(もしくはそれをJITコンパイルしたもの)が実行されます。CAR部を取り出すconsメソッドはこんな感じです。data_を返す一方、Kotlinではコンスセルを次のように表現しました。