JavaScriptでLispインタープリターを作ろう(3) ~条件分岐「if」を導入~
前回の記事では、JavaScriptで記述した小さな四則演算インタープリターの実装を解説した。
この四則演算インタープリターは、電卓としてはうまく機能することが前回の解説で理解できたかと思う。しかし、このインタープリターには、プログラムを記述する際に必要となる条件分岐という概念が含まれていないため、プログラミング言語っぽさがない。今回は、この四則演算インタープリターに、条件分岐if
を導入し、よりプログラミング言語らしい言語に一歩近付ける。
条件分岐if
を導入するための改善はとても簡単だ。evaluate
関数を以下のように変更するだけである。
var evaluate = function(x, env) { if (Array.isArray(x)) { if (x[0] === 'if') { // if というキーワードだった場合 return String(evaluate(x[1], env)) !== 'false' ? evaluate(x[2], env) : evaluate(x[3], env); } else { // if ではない場合、今までどおりの処理を行う var exps = x.map(function(exp) {return evaluate(exp, env);}); var proc = exps.shift(); return proc.apply(null, exps); } } else { var y = env[x]; return y !== undefined ? y : x; } };
四則演算インタープリターからの変更点は次の通り。
これまではArray.isArray(x)
がtrue
だった場合、そのまま関数を実行していた。しかし今回は、配列の第1要素がif
だった場合は特殊な処理を行うように変更した。
すなわち、配列の第1要素がif
だった場合は配列の第2要素を評価し、その評価結果がtrue
になった場合は配列の第3要素を評価し、そうでなければ第4要素を評価するようにしている。
このような処理を追加することにより、以下の様な記述が可能になる。
>> evaluate(['if', true, 1, 2], globalEnv); 1
>> evaluate(['if', false, 1, 2], globalEnv); 2
このように、配列の第二引数がtrue
かfalse
かによって、挙動が変えられるようになった。他の例も見てみよう。
// この例では ['+', 1, 2] が計算される >> evaluate(['if', true, ['+', 1, 2], ['+', 2, 3]], globalEnv); 3
// この例では ['+', 2, 3] が計算される >> evaluate(['if', false, ['+', 1, 2], ['+', 2, 3]], globalEnv); 5
// この例では ['+', 1, 2] が計算された後に 1 が足される >> evaluate(['+', 1, ['if', true, ['+', 1, 2], ['+', 2, 3]]], globalEnv); 4
これらの例から、if
が正常に機能している事がお分かりいただけると思う。さて、「if」の条件がtrue
/false
の固定値では面白く無いので、if
を有効活用するために、ライブラリーに関数を追加し、値の大小比較を行えるようにしてみる。また、JavaScriptのalertダイアログを表示する関数も追加し、処理状況を確認可能にする。
追加したのは、=
,>
,<
,<=
,>=
,alert
といった関数である。これらを追加したことにより、以下の様なコードも試せるようになる。
// ['mod', 9, 3] を計算すると 0 となり、ゼロとイコールであるので 1 が結果となる >> evaluate(['if', ['=', 0, ['mod', 9, 3]], 1, 2], globalEnv); 1
条件分岐if
の導入に関する説明は以上だ。意外と簡単だったのではないだろうか。if
が導入されたことで、単純な電卓だった四則演算インタープリターが、一歩、プログラミング言語に近づいたと感じて頂ければ幸いである。
[近棟 稔]