もっと楽しむ! プログラミング言語 「豆」談義

第1回古くて新しい関数型言語「Lisp」

はじめに「僕たちは今……」

人間の思考に、ことば――すなわち言語は欠かせないものです。考えごとをするときに、ことばを使っていない人はいないでしょう。

さて、僕達は今、コンピュータを身近に感じる世界にいます。

コンピュータというのは、ハードウェアとソフトウェアに大きく分けることができますが、ソフトウェアはさまざまなプログラミング言語を用いて書かれたプログラムで構成されています。

プログラムの重要さは、コンピュータのハードウェアを変更しなくても、中のプログラムを変えることによってコンピュータの用途を「計算」⁠文書作成」⁠ゲーム」⁠勤怠管理」⁠ショッピング」と変えることができることからもわかります。つまり、皆さんが「コンピュータって便利だね」と、言うときに指すコンピュータとは、プログラミング言語で書かれたプログラムのことなのです。

そのプログラミングの始まりは、チャールズ・バベッジが作成した解析機関用に書かれたもので、そのプログラムを書いたのはバベッジの助手であったエイダ・ラブレス女史だという説があります(最近ではエイダ女史ではなく、バベッジが書いたというのが定説だそうです⁠⁠。

バベッジの解析機関から始まったプログラミング言語は、今、僕がネットで確認しただけでも200以上の種類があるわけですが、皆さんは、その中でどのくらいのプログラミング言語をご存じでしょうか。

たとえば、プログラミング言語の中には、皆さんも知っているif文だとかfor文のような演算子が存在しないものも多くあります。でも、if文がなかったら「iは3より大きいか」という分岐はどうやって作るのでしょう。また、⁠X=Y(XはYと等しい⁠⁠Y=Z」という情報から「X=Z」という推論を導き出すのが得意だというプログラミング言語もあります。推論が得意なんて、一体どういう分野で使われるプログラムを作るものなのでしょうか。

プログラミング言語の生い立ちには、プログラム言語を開発する人やその時代などいくつもの背景が存在します。計算を素早く行いたい、人工知能を作りたい、子供でも書けるようにわかりやすくしたい、インタラクティブなメディアを作るためなどなど――そうした自分の知らないプログラミング言語を、ちょっとだけでも知ることは、子供の頃、北京語やドイツ語や日本と異なることばの「こんにちわ」を覚えるのが面白かったのと同じで楽しいのではないでしょうか。

この連載は、そんな子供の頃の知識欲を満足させる遊びを、プログラミング言語で再現してみようと思い立ったところが出発点です。ですから、いろんなプログラミング言語を題材に面白い話を書いていければいいなぁ――と思っています。

さあ、古くて新しいLISPを体験してみませんか?

一番はじめに紹介するプログラミング言語はLISPです。なぜLISPが最初かと言うと、今回紹介していく予定のプログラミング言語のなかで一番古いからです。

LISPは連載を予定している言語を含めた数あるプログラミング言語の中でも昔からある部類になります。COBOLやFORTRANと同時代からある由緒正しいプログラミング言語なのです。読者の中には、⁠なんだ今さら古い言語かよ」と思う方もいらっしゃるかもしれませんが(私も最初はそうでしたが……⁠⁠、古いからと言って見くびってはいけません。LISPには、他の言語に参考にされる技術やアイディア、他の言語にはないパラダイムなど、今でも画期的な技術要素が満載です。LISPを説明する上では、その出自から数学理論など難しい単語がたくさん出てきますが、今回はそのような難しい話は抜きに、普段Javaや.Netなどのプログラミングに飽きた方に、こんな言語が昔から合ったのかと思っていただけるようにLISPの紹介をしてみたいと思います。

LISPの誕生

LISPは1958年に考案されました。これは先ほども説明しましたが、現在使われている言語の中ではFORTRANについで古いプログラミング言語です。当初は数学理論のラムダ計算――そんな数学モデルがある程度に思ってください――に用いる言語として使われていましたが、その数学理論を背景とした表現力と柔軟性から人工知能の分野で盛んに使われるようになりました。そして普及して使われるようになるとLISPの実装の容易性から、さまざまなLISPの方言(LISPの一種という意味)が生まれることになりました。

今現在、LISPと一言で言っても、標準化された正統派のCommon LISPや、Emacs LISP、Interlisp、schemeなどたくさんのLISPが出てきます。それぞれの方言は、特徴的な機能が初期のLISPにプラスアルファされて発展してきました。その中でもInterlispは、現在、Java界隈で注目されているAOPの機能が実装されていたり、さらに、Interlispの開発環境であるInterlisp-Dでは、今のEclipseなどの統合開発環境に相当する、対話型開発ツールやデバッカなどが実現されていました。ちなみにそれは1980年代半ばの頃です。その当時の一般的な業務系システムの開発環境が、メインフレームに文字ベースのTSS端末[1]だったことを考えると、相当先進的だったことが窺えます。

余談ですが、弊社には当時からLISPをバリバリ使っていたと言う猛者が何人かいます。当然、そのような人も時代の波によりJavaの開発をするわけですが、EclipseやAOP、DIなど新しめの技術を見ても、⁠そんなの昔からあったよ!」と、何を今さら感を込めて言うのですが、LISPの当時の時代背景を見直すと、昔からあった技術が一般化されてきているだけなのだとあらためて考えさせられることがあります。

関数型言語とは何か?

次にLISPを語る上で、必ず、LISPは関数型言語であるという説明が出てきます。では、関数型言語とはどのようなものなのでしょう?調べると大体ラムダ計算に基づいて設計されている言語などと難しい解釈が出てきますが、CやJavaなどの関数型以外の言語と比較するとわかりやすいです。CやJavaなどの言語では、処理を行う関数(Javaではメソッドですが、便宜上、本稿ではメソッドも関数と表記します)を定義し、その中で必要な変数を定義し、変数に結果を入れ出ししながら必要な結果を求めていくかと思います。これは、関数は変数の状態を変えていく手続きと考えることができます。

それに対して、関数型言語の基本的な思想では、変数は値を変えることがありません。

関数型言語では、関数を呼び出した戻り値を組み合わせてすべてを完結していきます。ただ、実際に変数の値を変えられないのは不便なため、Common Lispなどの多くのLISP方言では、変数の値を変更する機能は持っていますが、プログラミングする際の基本的な思想には変わりません。CやJavaなどの言語になれている人からすると、変数の値は変えないと言うことはピンとこないかもしれませんが、関数型言語とは副作用的に生じる変化ではなく、関数の戻り値を使って動作するプログラミングを書いていく手法・考え方などと理解していただくだけでも十分です。そして、LISPは、現在ある各種の関数型言語の最初の言語でもあります。

LISPクロージャ

ここまでで、LISPの歴史など概観を説明してきましたが、ここからはLISPの中身について見ていきます。LISPの言語特徴には、⁠LISPは式と文を区別しない」「LISPのプログラムはすべてリストで出来ている」などいったものがありますが、ここでそれらの意味や特徴を理解してもらうには、話が長くなるため、他の言語と比べて威力が十分に伝わりやすいLISPのクロージャの例を示して、LISPの威力を掴んでいただきたいと思います。

クロージャとは、関数自体を数値データなどと全く同様に式の戻り値として扱える機能のことです。言葉ではわかりづらいですが、次から説明する例を見てもらうと理解していただけると思います。

まず、クロージャを使わない簡単な例で「引数に1を足した値を返す」関数を、LISPと比較のためにJavaで書いてみます。

(構文の説明については、省略します。簡単な例なので、イメージだけをとらえてください)

LISP
(defun tasu(x)  (+ x 1))
Java
public int tasu(int x) {    return x + 1;}

この例の場合、どちらを見ても大して違いはないですね。

では、次に「引数に1を足す関数を返す」関数を考えてみましょう。先ほどの例と違うところは、今度は、値を返すのではなく、関数を返すところです。どうですか?Javaの場合は、そのまま関数を返すことはできないので、関数のクラスをつくってあげないと実現できませんね。

Java
// 1を足す関数クラスを返す操作
public function tasuFunction() {
    return new function();
}

// 1を足す関数クラスの定義
class function {
	int tasu(int x) {
		return x + 1;
	}
}

何だが、簡単なことをする割には仰々しいですね。では、LISPではどうでしょう?LISPでは、関数を数値などのデータと同列に扱えるクロージャの機能があるため、関数を返す関数を簡単に作れます。

LISP
(defun tasufunction()
	;;1を足す関数を返す  
	#'(lambda(x)
		(+ x 1)))

どちらが、簡潔かは一目瞭然かと思います。

この例と同じようなことは、Cでは関数ポインタをつかってもできます。しかし、LISPのクロージャがこれらと違うのは、CやJavaでは、あらかじめ静的に定義された関数やクラスの実体しか返さないのに対して、LISPではプログラムの中で動的に関数を作って返すことができます。LISPは、このクロージャの機能があるため、プログラムを非常に柔軟に作れるようになっています。


ここまでで、ほんの一端ですがLISPについて説明してきましたが、LISPがどのようなものかは分かっていただけたでしょうか? LISPは古くからありますが、古いからと言って使えない言語ではありません。むしろ、LISPの思想やプログラミングなどは、現在の言語を理解する&使う上でも参考になる部分は多々あります。機会があれば、ぜひ読者ご自身でLISPの心に触れてみてください。

おすすめ記事

記事・ニュース一覧