こんにちは。
武田です。

普段はPerlやPHPなどのウェブ向けの言語使っているのですが、
今回は[Lisp]を触ってみたいと思います。

Lispって何?

  • 言わずと知れた歴史ある言語
  • 完全に括弧で囲われたポーランド記法によるプログラミング言語(カッコ書きめちゃ多い)
  • 強力なマクロ機能
  • すさまじい拡張性(そのため方言がたくさんあり)
  • 難解で習得者が非常に少ない
  • 使いこなすと凄腕ハッカーになれる(らしい)

普段と毛色が違うので大変そうですが挑戦してみましょう!

実験環境

  • Cent OS 6.3
  • Common Lisp(SBCL 1.0.38-3.el6)

インストール

とりあえず手元のPCにインストールしてみましょう。
今回利用するのは沢山あるLispの中からベターとされる[Common Lisp]を使います。

yumで使えそうなパッケージがあるかと検索

[root@localhost ~]# yum search "Common Lisp"
sbcl.x86_64 : Steel Bank Common Lisp

それっぽいのがあるのでこれをインストール

yum install sbcl.x86_64 

(中略)

Dependencies Resolved

==============================================================================
 Package                          Arch    Version          Repository    Size
==============================================================================
Installing:
 sbcl                             x86_64  1.0.38-3.el6     epel          14 M
Installing for dependencies:
 cl-asdf                          noarch  20101028-1.el6   epel          88 k
 common-lisp-controller           noarch  7.4-2.el6        epel          20 k

Transaction Summary
==============================================================================
Install       3 Package(s)

(中略)


Installed:
  sbcl.x86_64 0:1.0.38-3.el6

Dependency Installed:
  cl-asdf.noarch 0:20101028-1.el6common-lisp-controller.noarch 0:7.4-2.el6

Complete!

インストール完了

触ってみる

  • まずはアプリケーションを立ち上げてみましょう
[adjust@localhost ~]$  sbcl
This is SBCL 1.0.38-3.el6, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
*

とりあえずは起動しましたね。

それでは簡単な小学校の足し算ばりに簡単な[ 1 + 1 ] をしてみましょう

Lispでは左側に演算子を置くポーランド記法というちょっと変わった書き方をするようです。

  • ()で閉じて
  • 左側に +
  • その後に1 1
* (+ 1 1)
2

ちゃんと計算できてますね。

数値演算

# 加算
* (+ 1 2 3 4 5)
15

# 減算
* (- 20 10)
10

# 乗算
* (* 3 5)
15

# 除算
* (/ 15 3)
5

一般的な計算は簡単ですね。
でもこれだと足すだけ、引くだけとかしか出来ないですね。組み合わせたい時はかっこを使えばいいようです。

# 掛けてから足す
* (+ 10  ( * 3 5 ))
25

整数だけではなく浮動小数点数もOK


# 加算 * (+ 1.1 1.2) 2.3 # 減算 * (- 2.5 1.3) 1.2

ちょっとビックリした所は分数も計算できちゃうとこですね
(どこでこんなの使うんだよ。。。)

# 加算
* (+ 1/3 1/3)
2/3

* (+ 1/3 2/3)
1

# 減算
* (- 2/3 1/3)
1/3

# 乗算
* (* 2/3 4)
8/3

# 除算
* (/ 5/3 1/3)
5

変数

変数の定義には 特別式setq を使うみたいです。

# 変数 hoge に 999 を代入
*  (setq hoge 999)
999

# hoge + 1 = 1000
* (+ hoge 1)
1000

# 配列を代入することもできます
* (setq array '(1 2 3 4 5 6 7 8 9 10))

* array
(1 2 3 4 5 6 7 8 9 10)

配列のn番目を取り出すための関数(first – tenth)もあるようです。s
※最初とか最後じゃないのが新鮮

* (first array)
1
* (second array)
2
* (third array)
3
* (fourth array)
4
* (fifth array)
5
* (sixth array)
6
* (seventh array)
7
* (eighth array)
8
* (ninth array)
9
* (tenth array)
10

配列に同じ値があるか判断する関数(member)。ここらへんも使いそうですね。

# 存在する5を指定
* (member 5 array)
(5 6 7 8 9 10)

# 存在しない999を指定
* (member 999 array)
NIL

関数

関数はこんな形で定義できます。

# 与えられた数に10を足す関数を定義
*(defun plus10 (x) 
  (+ x 10))

# 関数を実行
* (plus10 15)
25

条件分岐

  • if

アプリケーションなのだからif文とか使えないとですね。
まずは条件に 真 or 偽 を渡すシンプルなif文で実験

# 条件に真を渡す
* (if t (string "真") (string "偽"))
"真"

# 条件に偽を渡す
* (if nil (string "真") (string "偽"))
"偽"
*

;common lispでは
; 真 : t
; 偽 : nil
;と表現するようです。

真偽の指定以外は普通のif文ですね

では渡ってきた値が 1 かそうでないかを返す関数を作ってみます。

(defun is-one (x)
   (if (equal x 1)                ; equalは値が同じかを判定する関数
       (string "1です")           ; 真の時に実行
       (string "1じゃないです")   ; 偽の時に実行
   )
)

# 作った関数に1を渡す
* (is-one 1)
"1です"

# 2を渡す
* (is-one 2)
"1じゃないです"
*

まあ普通ですね。
equal関数を見つけられなくてハマった。。。

  • cond

条件が複数ある場合は cond関数を利用するようです。

(defun check-number (x)
    (cond 
      ((equal x 1) (string "1です"))  ; 1つ目の条件
      ((equal x 2) (string "2です"))  ; 2つ目の条件
      ((string "それ以外です"))       ; 上記にしない
    )
)

# 作った関数に1を渡す
* ( check-number 1)
"1です"

# 2を渡す
* ( check-number 2)
"2です"

# 3を渡す
*( check-number 3)
"それ以外です"

loop処理

やっぱりloop処理も欲しいですね

  • dotimes
    簡単なn回の繰り返しなら dotimes 関数でいいようです。
5回の繰り返し
* (dotimes (i 5) (print i))

0
1
2
3
4
NIL
  • dolist
    これはphpでいう foreachみたいな挙動のようです。
# リストを定義
* (setq array '(1 2 3 4 5 6 7 8 9 10))
(1 2 3 4 5 6 7 8 9 10)

# リストArrayでループを回す
* (dolist (i array) (print i))
1
2
3
4
5
6
7
8
9
10
NIL

ファイルからの実行

いままでは sbclのコマンドプロンプトから1行1行して入力していましたが
長いアプリケーションの場合はしんどい思います。
sbclはファイルからの実行も可能らしいのでテストしましょう。

# 前述の処理を実ファイルに書き込む
[adjust@localhost tmp3]$ cat sample.lisp

# シンプルな関数を定義
(defun is-one (x)
   (if (equal x 1)                ; equalは値が同じかを判定する関数
       (string "1です")           ; 真の時に実行
       (string "1じゃないです")   ; 偽の時に実行
   )
)

# 関数を実行。今回は実行結果を表示するようにPrintに渡してます。
(print (is-one 1))

# lispをファイルから実行
[adjust@localhost tmp3]$ sbcl --script sample.lisp

"1です" 

ちゃんと実行できてますね。

fizzbuzz

ここまで材料が揃ったら簡単なアプリケーション作れそうです。
ちょっとfizzbuzz作ってましょうか。

# fizzbuzzプログラム
cat sample.lisp
(dotimes (i 100)
  (cond 
    ; 15の倍数(3の倍数かつ5の倍数)
    ((equal (mod (+ 1 i) 15) 0) (format t "FizzBuzz~%")) ; modはPHPの%演算子に相当
                                                         ; printではなく表示の調整が効くformatを利用。改行しないと見づらかったため。~% で改行
    ; 3の倍数
    ((equal (mod (+ 1 i) 3)  0) (format t "Fizz~%"))
    ; 5の倍数
    ((equal (mod (+ 1 i) 5)  0) (format t "Buzz~%"))
    ; 条件にヒットしない場合
    (t (format t "~A~%" (+ 1 i)))                        ; ~Aでパラメータを引き渡す事を指定
  )
)

# プログラム実行
sbcl --script fizzbuzz.lisp
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
(略)

まとめ

ほんとさわりの部分ですがLisp触ってみましたがいかがでしょうか?
本当はもっとLispらしい事をやりたかった(ラムダ式、マクロ等)のですが
片手間でやるには難解なレベルだったので一旦この形とさせていただきました。

今後Lispの勉強が捗ったら続きを書くかもです。。。

参考