Lispは1958年に登場してから、現在も広範囲に使用されているプログラミング言語です。長い歴史の中で、熱狂的なファンを多く獲得しています。Lispと言えば、丸カッコを多用する独特のS式や、強力なマクロによる高い拡張性を特徴とする言語です。今回は、Lispの魅力に迫ってみましょう。

1958年生まれのLisp

Lispが生まれたのは、プログラミング言語の1958年です。1958年と言えば、最初のプログラミング言語に数えられるFORTRANが登場して間もない頃です。人工知能研究の第一人者であるジョン・マッカーシーが開発しました。それで、Lispは人工知能研究で使われるようになり、普及していきました。

Lispが現代のプログラミング言語に与えた影響は大きいものです。自分自身を評価するeval関数を発明したことを始め、木構造、ガベージコレクション、動的型付け、条件分岐、高階関数、再帰など、現代のプログラミング言語に欠かせない要素を先取りして実装していました。

1970年代から1980年代になると、いろいろなLispが開発されました。そうしたLispはそれぞれ独自の機能を備えており、同じLispと言えど互換性がありませんでした。そのため、1980年代から1990年代には、多様なLisp言語を一つの言語に統一しようという流れになりました。このLispを標準化しようという動きは、一定の成功を収めました。それでも一つにまとまることはなく、現在では「Common Lisp」と「Scheme」の二系統に分かれています。

Lispの二大流派について

まず、前者のCommon Lispですが、これは1984年と1994年に米国の標準化機構のANSIによって標準化されました。Common Lispは多機能であることが特徴で、言語仕様はかなり大きなものになりました。これに対して、後者のSchemaは、Lisp本来のシンプルさを重視したもので、電気や電子技術の学会であるIEEEによって標準化されました。

ただし、Common LispとSchemeは、それぞれ標準化された仕様であることがポイントです。そのため、この仕様に基づいて、様々な処理系が開発されています。特に、Schemeの仕様書は非常に簡潔であるため、かなり多くの処理系が存在します。

例えば、教育用途に特化している「Racket」、日本人の川合史朗氏によって開発され、ライブラリが充実している「Gauche」、他のアプリの拡張言語として使うことのできる「Guile」など様々なものがあります。

ここでは、数あるLisp処理系の中から、Gaucheを利用して、Lispの気分を楽しんでみましょう。

Gaucheのインストール

Windows用のインストーラーが用意されています。GaucheのWebサイトからダウンロードしましょう。インストーラーをダブルクリックしてインストールできます。

Gauche > Windows
[URL] http://practical-scheme.net/gauche/download-j.html

インストールすると、デスクトップに「Gauche」というショートカットが作成されるので、それをダブルクリックすると対話型実行環境(REPL)が起動します。

macOSでは、Homebrewを利用してインストールするのが簡単です。ターミナル.appを起動したら、以下のコマンドを実行しましょう。Homebrewはこちらからを参考にしてインストールしてください。

brew install gauche

インストールが無事終わったら、ターミナル上で「gosh」と打ち込んで[Enter]キーを押してみよう。すると、対話型実行環境(REPL)が起動します。

Gaucheの対話実行環境を試してみよう

対話型実行環境が起動したら、簡単なプログラムを入力して動作を確かめてみましょう。以下のプログラムを実行すると、画面に「Hello, World!」と出力します。

(print "Hello, World!")

Lispの基本はS式です。S式とは、2分木ないしリスト構造の形式的な記述方式のことです。Lispでは、必ず「(」から始まり「)」で終わるのが基本です。そして、そのカッコの中にスペースで区切って要素を記述します。上記のプログラムもS式であり、『(関数 引数)』の形になっています。

次に、簡単な計算を実行して動作を確認してみましょう。以下は1 + 2の計算式を計算するプログラムです。

(+ 1 2)

このように、Lispで計算を行うには、『(演算子 値 値 ...)』のように記述する必要があります。これは、前置記法と呼ばれるもので、Lispでは計算式も前置記法で書く必要があります。

次に「(1 + 2) × 3」の計算を行うLispのプログラムを作ってみましょう。

(* (+ 1 2) 3)

どうでしょうか。(+ 1 2)を最初に計算し、その後、(* 3 3)を計算します。結果、正しく9を出力します。ここまでのプログラムを実行したのが、以下の画面になります。

  • Gaucheの対話型実行環境を実行しているところ

    Gaucheの対話型実行環境を実行しているところ

FizzBuzz問題を解いてみよう

それでは、もう少し、本格的なプログラムを見てみましょう。本連載では、いろいろなプログラムでFizzBuzz問題を解いています。FizzBuzz問題とは、次のような問題です。

> 1から100までの数字を出力する際に、3の倍数の時「Fizz」、5の倍数の時「Buzz」、3と5の倍数の時「FizzBuzz」と表示するプログラムを作って下さい。

Gaucheで作ると、以下のようになります。以下のプログラムを「fizzbuzz.scm」という名前で保存しましょう。

; Fizzの条件を関数で定義 --- (*1)
(define (is-fizz n)
  (= 0 (modulo n 3)))
; Buzzの条件を関数で定義 --- (*2)
(define (is-buzz n)
  (= 0 (modulo n 5)))

; FizzBuzzの値を返す関数を定義 --- (*3)
(define (fizzbuzz n)
  (cond
    ((and (is-fizz n) (is-buzz n)) "FizzBuzz")
    ((is-fizz n) "Fizz")
    ((is-buzz n) "Buzz")
    (else n)))

; 100回fizzbuzz関数を呼び出す --- (*4)
(dotimes (n 100)
  (print (fizzbuzz (+ n 1))))

コマンドラインから以下のコマンドを実行すると、プログラムを実行することができます。

gosh fizzbuzz.scm

実行すると、以下のように表示されます。

  • FizzBuzzを実行したところ

    FizzBuzzを実行したところ

プログラムを確認してみましょう。(*1)ではdefineを利用して関数is-fizzを、(*2)では関数is-buzzを定義します。(*3)では上記のis-fizzとis-buzzを利用して、FizzBuzzの値を返す関数fizzbuzzを定義します。cond文は、順に条件を評価し、条件に合致した値を返す構文です。(*4)ではdotimesを利用して、100回fizzbuzz関数を呼び出し、結果を出力します。

Lispに慣れていない方は、動作が分かりづらい感じたことでしょう。ともう少しかみ砕いて確認してみましょう。対話型実行環境(REPL)を起動し、以下のプログラムを実行して動作を確認してみましょう。

まず、繰り返し構文のdotimesを試してみましょう。以下は、1から5の数値を順に画面に表示する例です。(ここでは、プログラムと実行結果の両方を紹介します。実際には、『gosh>』の後にある部分を入力しましょう。)

gosh> (dotimes (n 5) (print n))
0
1
2
3
4

次に、cond文を試してみましょう。cond文は次の書式で利用します。条件を順番に確認していって、条件に合致した値がcondの結果となります。

[書式]
(cond
(条件1 値1)
(条件2 値2)
...
(else 値)

対話型実行環境で確かめてみましょう。ここでは、2番目に指定した条件が真になり、"2"という値を返します。

gosh> (cond ((= 3 5) "1") ((= 3 3) "2"))
"2"

まとめ

以上、今回は古くて最新の関数型言語Lispの魅力を紹介しました。Lispは深淵でそして強力です。とは言え、現実的に業務でLispが使われることは少ないかもしれません。しかし、Lispを学ぶなら、プログラミング言語の持つ新しい側面に気づくことができるでしょう。そして、それは自分の書くコードの品質を大きく高めることができます。そして、筆者が観察するところによると、Lispのできる人は大抵プログラマーにモテるので、果敢にLispに挑戦してみると良いでしょう。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2005年IPAスーパークリエイター認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。