[LSP42] LuaとPythonとRuby
(この記事はLISP Implementation Advent Calendar 2日目のためのエントリです。)
LuaとPythonとRubyでLISPを作りました。
https://github.com/zick/LuaLisp
https://github.com/zick/PyLisp
https://github.com/zick/RbLisp
動機
今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその2〜4つ目です。
Lua、Python、Rubyという言語を選んだのは簡単そうだったからです。1つ目のScratchで苦労したので楽がしたかったんです。どの言語もほとんど書いたことがありませんでしたが、実際に簡単だったため、1日で3つの言語でLISPを実装することが出来ました。しかし、その半面、LISPを書き終えてもこれらの言語がどんな言語なのかさっぱり分からないままでした。自分が使った機能のことさえよく分かっていません。
Luaの思い出
イマドキの言語にもかかわらず、複合データ型はテーブル(連想配列)しかないようです。ひょっとしたら配列はテーブルとは別の扱いかもしれませんが、よく調べてないので分かりません。いずれにせよ、よく調べずに書けてしまうのですから素晴らしいことです。LISPのデータはタグとデータを1つのテーブルに入れることで表現しました。
function makeCons(a, d) return { tag = 'cons', car = a, cdr = d } end
Luaはブロックを(小/中/大)括弧ではなく、 if X then Y end
のようにendを使って表現します。基本的に私はこのbegin/endの文化があまり好きでなく避けてきたため、begin/end系の言語を100行以上書いたのはこれが初めてかもしれません。
begin/endのネストが無駄に深くならないようにelseifというキーワードも準備されています。しかし、私はこのelseifというものがあまり好きではありません。Cの else if のように単なるifとelseだけで表現できる方がシンプルで好きです。もっともCのelse ifは文法に曖昧さを招いてしまうのですが、それがまた好きです。
Luaは文法に関して個人的に好みでない点もありましたが、全体を通してみると書きやすく、サクサク動いた感じでした。
Pythonの思い出
PythonはLuaと違い、クラスを作ることが出来るのですが、Luaで作ったLISPを単純に移植したのでテーブル(Pythonの言葉だと辞書?)を使ってLISPのデータを表現しました。
def makeCons(a, d): return { 'tag': 'cons', 'car': a, 'cdr': d }
単純な移植なので悩むこともなくさらっと書けたと思います。
Pythonと言えばブロックをインデントを使って表す言語で、これまた私の好みではないのですが、Luaで山ほどendを書いた直後だったので、endを書かずにすんだのは正直うれしかったです。
あと、Pythonはelseifではなくelifです。紛らわしい。
もんだい
クラスがあるのになんでもテーブルで書くのはあんまりだと思ったzickくんは、クラスを使ってLISPを書き直しました。
class Cons: def __init__(self, a, d): self.car = a self.cdr = d ... if isinstance(x, Cons): ....
しかし、zickくんの予想に反し、実行速度は大幅に遅くなってしまいました(3.44sec -> 19.37sec)。
速度が低下した理由を答えなさい。なお処理系はPython 2.7.5とする。
Rubyの思い出
Rubyにクラスがあるのは言うまでもないのですが、またもや単純にLuaで書いたものを移植したのでテーブル(Rubyの言葉だとハッシュ?)を使ってLISPのデータを表現しました。
def makeCons(a, d) return { 'tag' => 'cons', 'car' => a, 'cdr' => d } end
RubyもLuaと同じくbegin/end系の言語です。また山ほどendを書かないといけないのかとちょっとうんざりしました。
あと、Rubyはelseifやelifではなくelsifです。いい加減にして欲しい。
Rubyで一番驚いたのは高階関数が素直に書けないことでした。 def myFunction ... end
と関数を定義した後に myFunction
と書いても関数を参照できないようです。しかたがないので、SUBRは非常に遺憾ながらオブジェクト指向言語らしく、クラスとメソッドを使って作ることになってしまいました。後日「RubyはLISP2であり、CLの#'f
はmethod(:f)
、CLの(funcall f arg)
はf[arg]
と書ける」という話を人から聞き、コードはシンプルになりました。何か色々と間違っているような気もしますが気のせいだということにしておきます。
もんだいない
クラスがあるのになんでもテーブルで書くのはあんまりだと思ったzickくんは、クラスを使ってLISPを書き直しました。
class Cons def initialize(a, d) @car = a @cdr = d end attr_accessor :car, :cdr end
zickくんの予想通り、実行速度は大幅に改善されました(10.09sec -> 2.02sec)。めでたしめでたし。
小学生並みの感想
高級なデータ構造が使え、ごみ集めがある言語でLISPを作るのは非常に簡単でよかったです。
小学校低学年並の疑問: クラスには、メガネをかけた委員長とか居ないんですか?
[…] はよく覚えていないのですが、確かWikipediaを眺めてテキトーに決めたんだと思います。Lua、Python、Ruby、R、Perlといった有名な(少なくても私が名前を知っていた)言語が続いていたので変 […]
[…] た。少し考えてみたらまったく同じことを過去にもやったことを思い出しました。RubyでLISPを実装した時です。さらに考えてみると、RubyもSmalltalkの影響を受けている言語です。これを踏 […]