この実装でコンスセルのCAR部を取り出そうとすると、次のようなバイトコード(もしくはそれをJITコンパイルしたもの)が実行されます。getCarを呼ぶgetCarはKotlinが自動生成するメソッドで、次のようになります。carを返すKotlinの方が短いですね。Javaはどうあがいても2回参照をたどらないとCARにたどり着けません。Kotlinは一回の参照でCARにたどり着けます。何度もリスト操作を繰り返す今回のベンチマークではこれが大きな差を産んだのではないでしょうか。仮説だけを書いているといろんな人に怒られそうなので、実際に検証してみました。こちらはJavaのプロファイル結果。findVarが一番時間をくっているというのは環境に連想リストを使っているので当然の結果ですね。二番目というところです(どうしてこのメソッドがこんなに遅いか調べるのは読者の練習問題とします)。それ以降にもリスト関連のメソッドが並んでいます。次はKotlinのプロファイル結果。相当するものがありません。それからJavaのmakeConsよりKotlinのCons.<init>が速いというのもポイントですね。考えてみたらJavaはLObjとConsの2つのオブジェクトを作るのに対し、Kotlinは単一のオブジェクトしか作らないのでこれも当然ですね。この結果を見る限り、やはりコンスの実装の違いが実行時間に影響を与えているようです。ここでひとつの疑問が出てきます。Javaの実装をKotlinと同じようにすればどちらが速いのか。これは読者の練習問題とします。父兄の皆様へ「うちの言語(こ)がこんなに遅いわけがない!」「あんたの書き方が悪いんだ!うちの言語(こ)に謝れ!!」とお怒りの方もいらっしゃるかもしれません。最初に書きましたがソースコードはすべてGitHub上においてますので、納得がいくまでご自由に改造してください。計測はセルフサービスとなっておりますので予めご了承ください。小学生並みの感想Kotlinが一番速かったです。24日目のためのエントリです。)OzとBooでLISPを作りました。今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその41〜42個目です。Ozは人を殴り殺せる分厚さの青い本で知り、簡単そうだったので最後に楽をしようと選びました。BooはWikipediaで見つけ、簡単そうなので最後の最後に楽をしようと選びました。Ozの思い出Ozはマルチパラダイム言語で、関数型言語としても使えるし、オブジェクト指向言語としても使えるし、並列論理型言語としても使える、めっちゃカッケー言語です。不思議なことにあまり流行っていません。不思議なことに私も書いたことがありませんでした。マニュアルを見るとそこには本の紹介がこれまで使った言語でも何回かありました。言語/処理系の公式サイトのマニュアルのページを見ると本の紹介が載っているというパターン。「この言語を学ぶためにはまず本を買え」ということでしょうか。何のためにウェブサイトがあるんだと言いたくなります。そのたびにイラッ☆っとしてきたんですが、Ozもそのパターンでした。紹介されている本は幸いにも日本語版があるんですが、その価格は8200円+税。現在の貨幣価値に換算すると8856円である。これは酷いと言いたいところですが、私はすでにこの本を持っていたので何の問題もありませんでした。外観さて、いろんなパラダイムが使えるOzですが、その文法は(私はあまり好きではない)begin/end系です。nil}end中括弧{}がブロックや集合以外の目的で使われているというのが少し珍しいのではないでしょうか。LISPに慣れている人はこの中括弧をただの括弧に読み替えるとしっくり来るかと思います。このNreverseの定義に今回言いたいことがほぼすべて入っているのですが、順をおって説明します。LISPオブジェクトの表現Ozはマルチパラダイム言語ゆえ、データを色々な方法で表現することが出来ます。クラスを使ってもいいし、DylanのときのようにOzの型をそのまま使うというのもひとつの手です。今回のように簡単なLISPを作るのであればタプルを使うのが恐らく最善かと思います。ような名前と値の列の組です。名前はシンボルで任意の名前を使えます。このタプルはパターンマッチングができるので非常に便利です。いう部分がありますね。これは値がこのパターンにマッチした場合、変数DがコンスのCDR部に束縛される訳です。タプルを使うために面倒な宣言などを書く必要もありませんし、非常に楽な上に便利なわけですね。Ozの変数はimmutableで書き換えることは出来ません。mutableなものを表現するためにはセルを使います。D})endこのMakeConsは初期値Aをもつセルと初期値Dをもつセルを作り、それをタプルでまとめています。いう箇所が出てきますね。セルの中身を書き換える操作です。このセルのお陰で簡単に楽しい破壊的ライフがおくれるわけですね。最後に邪魔をするのはbegin/endそんなこんなで楽しくOzを書くことが出来たのですが、最後まで邪魔だったのがbegin/end系の文法です。42個中2個目の言語の時から「begin/endの文化があまり好きでない」と書きましたが、最後まで好きには慣れませんでした。書いてしまい「endが足らない」というコンパイルエラーにあったり悲しい目に会いました。「42個の言語でLISPを実装する」ことが決まった時には「最初と最後は難しくて面白い言語を使おう」と考えていました。実際に最初の言語はScratchというなかなかインパクトのある言語を使いました。そんな思いも使った言語が20を超えたあたりから「もうなんでもいいから早く終わりたい。出来るだけ簡単な言語。出来るだけ簡単な言語を使って少しでも早く終わらせるんだ」という非常に軟弱な思いによってかき消されてしまいました。このBooですがテキトーに調べてみると「型の付いたPythonみたいな言語」みたいな紹介がありました。こりゃとんでもなく楽に違いないと思い、最後の最後に一番楽な思いをしてやろうとBooを一番最後に使うことにしました。外観ではBooのプログラムを見てみましょう。型を明示している箇所を除くとPythonみたいです。使おうとしたら型のエラーが出ました。型の強い言語ではよくあることですね。Scalaなら相互再帰する関数は型を明示する。何らかの手段を取らないといけないというのは容易に想像が付くのですが、Booでの相互再帰の方法がいくら調べても見つかりませんでした。これまでの41個の実装ではアトムのリードとリストのリード、アトムの表示とリストの表示、evalとevlisとapplyとprogn。様々な関数が相互再帰を使っていました。相互再帰が使えないとなると、42個目にして実装方法を変えなければいけません。するかしばらく考えました。この日は朝はランニング、午前から昼にかけてOzでLISPを実装、午後はBooでLISPを実装、夕方からは友人と焼肉を食べに行くという予定でした。OzでLISPを実装するところまでは順調だったのに、すぐに終わると考えていたBooでまさかの大問題。ここで遅れが出てしまうと焼き肉に支障をきたします。プログラムの美しさと焼き肉を天秤にかけた結果、「相互再帰をすべて手で展開してしまえ」という結論に至りました。自己再帰は出来るのだから、単純に展開してしまうだけで動くはず。考えて美しさも何も気にせずに単純作業としてプログラムを書きました。他の言語では4個以上の関数に分けていたeval関連の関数もすべて1つの関数に。それはもう悲しいプログラムが出来上がりました。ever.という言葉を書いてしまうくらい。タイプミスしたことに気づかないくらい悲しみに満ち溢れていました。この作業が案外簡単だったのが驚きでした。Booを使わなければ気づけなかったかもしれませんね。書いてコンパイルエラーになるという悲しい思いを何度もしました。本当にありがとうございました。23日目のためのエントリです。)PikeとProcessingでLISPを作りました。今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその39〜40個目です。Pikeはちょうどこのころネットニュースで「いまPikeが熱い!」みたいな記事が出てたので使うことにしました。Processingは名前は知っていたものの明らかにLISPを作るのには不向きと思って使っていなかったんですが、「名前を知ってる言語は使ってしまえ」戦略により使うことにしました。Pikeの思い出さてここで問題ですPikeの公式サイトを開いて初心者用のチュートリアルのページまで辿り着いてください。30秒以内に辿りつけた人は私より優秀なので、ぜひとも84個の言語でLISPを実装してください。外観Pikeは皆おなじみC-likeの文法です。}}詳しくは調べてませんが、スロットの解決は実行時に行われるようです。書くだけなので、実質的に型がないのと変わりません。コンパイル時に型をチェックしたい人には残念でしょうが私にとっては極めて楽でした。_typeofオブジェクトの型を比較する機能を探したんですが見つけることが出来ませんでした。代わりにオブジェクトの型を返す関数を見つけたので以下の様な方法で型を見ることに。}まあ、動くんですけどなんだか微妙な感じに。流行りの言語はやはり楽だった冒頭に書いたとおり(当時)「いまPikeが熱い!」みたいな記事が出て、それなりに多くの人が反応していただけあって、Pikeは使いやすかったです。テキトーに書いてもなんだかよく分からないけど動く。Squirrel並みの速さでLISPを完成させることが出来ました。それ故に思い出が少ないです。Processingの思い出Processingといえばナウなヤングにバカウケな言語で、なんだかよく分からんけど視覚的にインパクトのあるチャラチャラしたプログラムを書くことに特化しているやつです。今回はターミナルしか使いません。標準入力なんてなかったProcessingの主な使用目的を考えたら当たり前のことなのですが、Processingには標準入力から文字列を読み込む機能なんてありません。Javaと連携する機能があるのでそれを使って標準入力から文字列をとってこようとしたのですが、コンパイルは通るもののなぜか上手く行かず。しかたがないのでLISPのプログラムはファイルから読み込むという方式に。これまでScratchを除くすべての言語で標準入力を使っていたので残念で仕方ありません。Processingを使うのをやめて別の言語を探すほどの元気はなかったのでこの方式で逃げました。外観またしてもC-likeな文法です。ret;}次はLObjの定義を見てみましょう。...}私の過去の記事を読んでいた人はすぐに分かりますね。書かない分記述が短くなったJavaです。したらまったくJavaっぽくない書き方ができたのかもしれませんが、ようやく40個目の言語までたどり着き、少しでも早く42個の言語でLISPを書き終えたかった私は深く考えず、限りなくJavaに近い何かを書くことに専念しました。小学生並みの感想やはり、頭を使わずに書ける言語は素晴らしいと思いました。22日目のためのエントリです。)DylanとIokeとEでLISPを作りました。今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその36〜38個目です。DylanはLISPの派生言語と知っていたので絶対楽ができると思い、最後の方に楽をしようととっておいたものを満を持して使った感じです。IokeはIoやSmalltalkについてWikipediaで調べている時に見つけたので使いました。Eを選んだ理由はとよく覚えていませんが、恐らくWikipediaで見つけたのだと思います。「E」という名の言語は複数あるようですが、今回使ったのは「分散計算のための言語」であるEです。worldを作ろうとしたら「まずこのコマンド実行しろ」という記述が出てきました。実行すると複数のファイルが生成されました。worldを作るだけでも複数のファイルを適切な名前で適切な場所に置かないといけない言語」じゃないですか。ライブラリの依存関係とか解決してくれるのはいいですが、単一のファイルでもコンパイルして実行できるようにして欲しいです。あと、Dylanのツールはとにかく大量に出力します。コンパイラも成功失敗にかかわりなく物凄い量のメッセージを出します。あまりに出力が多すぎるためコンパイルエラーを見落とすということさえもありました。「DylanはかつてはLISPと同様のS式を使っていたが、途中で独自の文法を使うようになった」ということは知っていたのですが、それがどんな文法かは知りませんでした。(私があまり好きじゃない)begin/end系の文法でした。#());end;LISPみたいな言語を使えると思い込んでいた私はもうショックすぎて手元が狂いそうになりました。後の記述は省略可能と知って無事息を吹き返しました。見た目はアレですが、よく見るとLISPを知っている人なら簡単にLISPに書き換えられそうな感じですね。確かにLISPの派生言語という気もします。インチキ今回はLISPのオブジェクトはDylanのオブジェクトをそのまま使うというインチキをしました。LISPのシンボルにはDylanのシンボルをそのまま使い、LISPのコンスにはDylanのペア、LISPのSUBRにはDylanの関数をそのまま使うといった感じです。特殊なものとして、LISPのエラーはDylanの文字列を使い、LISPのEXPRはDylanのベクタを使って「引数/本体/環境」の3つ組を表しました。使って型を見るだけです。これで超簡単、超ラクチンとか思っていたんですが、いざ出来上がったものを見てみると、なんだかイマイチな感じに。単純に汚いのは最初から予想していたので別にいいんですが、なんだかよく分からないけど納得がいかない感じになってしまいました。書き直したい気もしましたが、先を急ぐためそのまま放置することに。Iokeの思い出Iokeは簡潔に言うと「JVMで動くIoみたいな言語」です(*)。JVMで動く言語は当たりが多いし、Ioは既に使ったことがあるので簡単に出来る予感がしました。実際にはCLRとかの上でも動くらしいよ、お兄ちゃん!外観Iokeのプログラムはこんな感じの見た目です。ret)ここでIoのnreverseを見てみましょう。いう違いくらいしかありません。実際には見た目以上に色々と違うのですが、そのあたりは各自勉強して下さい。あとデータ型のチェックについては後述します。持つ」と書いてあります。ここで言うcellは値を入れる箱で、メソッドもcellの中に入れます。d))IokeはIoやJavaScriptと同じprototype-baseの言語であるという前提知識のもとに見るとmimicの意味がなんとなく分かるのではないでしょうか。オブジェクトをコピーしているわけです。object)とも呼ぶ」と書いてあります。なんて言葉も出てきます。いうメソッドがあります。引数が文字列なのか意味が分かりません。引数はオブジェクトです。こっちのほうがしっくりします。ちなみに、Ioのときはこれについて速さの議論をしましたが、もう面倒なのでIokeではkind?を使わない話なんてしません。