Quantcast
Channel: DMM WEBCAMP Advent Calendarの記事 - Qiita
Viewing all articles
Browse latest Browse all 25

Lispって何?[Lisp入門の入門]

$
0
0

Lispとは

Lispと聞いて、「は?それ何?」と思ったそこのあなた!是非この記事を読んでみてください!
Lispは、人工知能プログラムやWebアプリ開発に使われてきています。
オライリー社の動物本と呼ばれる本の中でも、Lispは非常に印象的ですよね。
目が5つある象みたいなキャラクターでおなじみ。宇宙から来た動物みたいで、一度見たら忘れないはず。

出典: Land of Lisp

独特なプログラミング言語なので、一度やってみる価値があります。
この記事で入門の入門の文法を学べます。読みながら一緒に書いていきましょう!

はじめに

Common Lisp入門・基本などの記事がありますが、ここでは入門の入門です。
インストールから始まり、基本的な文法を一緒に書きながら学んでいきましょう。
これまでC,Java,Ruby,JavaScriptなどを書いたことがありますが、Lispはホントに特殊です。
やってみての私の感想は2つあります。

①カッコがとにかく多い
最後の閉じカッコが5-6個続くこともあり、対応するカッコがわからなくなってしまいます。
②プログラマーになった気になれる
かなり古い言語であるからか独特です。人によって合う合わないの相性があると思います。でも、書いてる内にプログラマーになった気になれてオススメです笑

特徴

実は、Lispは多くの特徴をもっているんです。
①コンパイルなしで実行可能
プログラムをコンパイルすることなく、その場で実行することができます。
②信者がいる
Lispには、Lisperという「Lispを信奉し伝道する人、熱狂的なファン」が多くいます。それだけ魅力的なのだと思います。
③動的言語である
型を定義しなくてよいです。

概略

1958年 JohnMcCarthyによって考案 (彼は人工知能AIという言葉の生みの親です)
1960-現在 さまざまな方言が生まれる
1975年 Lispの方言Schemeが登場
1984年 Common Lispが登場(さまざまな方言を標準化しようとした
1995年 Paul Grahamと Rovert Morrisは CommonLispで Viawebというアプリを書いた。Viawebでは、ユーザは専門知識なしにWebブラウザを使用して独自の通販サイトを構築しホスト(サイトをIPアドレス上に置く)できた。
1998年 Yahoo!が Viawebを買収し、Yahoo!Storeとしてショッピングモールサービスを提供した。

Yahoo!StoreがLispで書かれていたのは驚きですよね。

準備しよう

インストール

Lispには主に3つの方言(Common Lisp, Schema, Emacs Lisp)があります。方言とは、Lispから派生してできた言語です。今回使うのは、Common Lispの処理系の1つである、CLISPです。何だかケンタッキーのフライドチキンみたいにサクサクしておいしそう笑

Macの場合

Homebrewからインストールすることができます。

$ brew install clisp

基本操作

コンソール画面

$ clisp : 起動

起動するとこんな感じで出てきます。
最後に出てくる[1]>から、コマンドやコードを入力していきます。

$ clisp
  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8
  I  \ `+' /  I      8         8           8     8        8    8
   \  `-+-'  /       8         8           8      ooooo   8oooo
    `-__|__-'        8         8           8           8  8
        |            8     o   8           8     o     8  8
  ------+------       ooooo    8oooooo  ooo8ooo   ooooo   8

Welcome to GNU CLISP 2.49 (2010-07-07) <http://clisp.cons.org/>

Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2010

Type :h and hit Enter for context help.

[1]>

コードの入力

試しに数やコードを入力してみましょう。
「1」を入力すると1が、足し算のコード「(+ 1 1)」を入力すると計算結果2が返ります。
今度は、文字を入力しましょう。
「a」と入力すると、小文字aが大文字Aに変換され、「Aは値をもっていない」というエラーが起きます。
文字に値が代入されていない場合、文字そのままでは読み取ってくれないので、文字の前にシングルクォーテーションをつけます。
「'a」と入力すると、小文字aが大文字Aに変換・シンボルとして認識され、Aと返されます。

[1]> 1
1
[2]> (+ 1 1)
2
[3]> a

*** - SYSTEM::READ-EVAL-PRINT: variable A has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of A.
STORE-VALUE    :R2      Input a new value for A.
ABORT          :R3      Abort main loop
Break 1 [4]> 'a
A
Break 1 [4]> 'b
B

コマンドの入力

ターミナル上でコードを入力できますが、間違えたらもう一度打ち直さないといけない、終了したら再び使うことができないなどのデメリットがあります。
なので、テキストエディタで記述すると簡単です。
この時に1つ注意が必要です。
この記事では、ファイルの拡張子を「.lsp」としています。「.lisp」という拡張子でも大丈夫です。

(load "ファイル"): ファイルの読み込み

拡張子が.lspのファイルを読み込ませます。
試しにLISPという文字を表示させるlisp.lspを書きましょう。

lisp.lsp
(print 'lisp)

読み込ませた時の前半部分が出力結果、後半部分が読み込みに成功したか(成功したらT)を表示します。

[1]> (load "lisp.lsp")
;; Loading file lisp.lsp ...
LISP
;; Loaded file lisp.lsp
T
(bye): 終了

このコマンドでCLISPのコンソールを終了し、通常のターミナルに戻ります。

[1]> (bye)
Bye.

clispのコンソール画面を出してファイルを読み込むのは、面倒ですよね〜
そこで、ファイル読み込みまでを一括でやってみましょう。

一気にファイル読み込みまでやろう

1つのコマンドを打つだけです。非常に簡単ですね。

$ clisp ファイル名

実際にさきほどのコードで試してみましょう。

$ clisp lisp.lsp
LISP

コードを書いてみよう

四則演算

演算子(+,-,*,/)を前に記述する前置記法(ポーランド記法)になってます。

calculation.lsp
(print (+ 1 2)
(print (- 1 2)
(print (* 1 2)
(print (/ 1 2)

出力結果
3
-1
2
1/2

expt: べき乗

calculation.lsp
(print (expt 3 4))
(print (expt 33 33))

出力結果
81
129110040087761027839616029934664535539337183380513

述語

述語とは真(t)か偽(nil)を返す関数です。

equal: 2つの引数が等しいか

pre.lsp
(print (equal 1 1))
(print (equal 1 2))
(print (equal 'a 'a))

出力結果
t
nil
t

atom: アトムであるか

pre.lsp
(print (atom '(a b c d)))
(print (atom 'a))

出力結果
nil
t

numberp: 数値であるか

pre.lsp
(print (numberp 1))
(print (numberp 'a))

出力結果
t
nil

symbolp: シンボルであるか

pre.lsp
(print (symbolp 1))
(print (symbolp 'a))

出力結果
nil
t

zerop: 0であるか

pre.lsp
(print (zerop 1))
(print (zerop 0))

出力結果
nil
t

oddp: 奇数であるか

pre.lsp
(print (oddp 1))
(print (oddp 2))

出力結果
t
nil

evenp: 偶数であるか

pre.lsp
(print (evenp 1))
(print (evenp 2))

出力結果
nil
t

リスト: 0個以上の要素が()で囲われたもの

first, car: 最初の要素を返す

list.lsp
(print (first '(a b c d)))
(print (car '(a b c d)))
(print (car '(a b)))

出力結果
a
a
a

rest, cdr: 最初の要素以外を返す

list.lsp
(print (rest '(a b c d)))
(print (cdr '(a b c d)))
(print (cdr '(a b)))

出力結果
(b c d)
(b c d)
b

carとcdrについて

ここで、carとcdrのメモリ内部の仕組みについて見ていきます。
1個の箱に1個のデータが入っていて、2個の箱の組をコンス(cons)とします。
リスト(a b c)は3個のコンスから構成されています。
その1つ目のコンスを見てみましょう。
1番目の箱(car部)はデータ部といい、「a」という文字を指すポインタとなっています。
2番目の箱(cdr部)は次ポインタ部といい、リスト(b c)を指すポインタとなっています。
car_cdr.png

図1: リスト(a b c)のメモリ配置

car(カー)= Contents of Address part of Register
cdr(クダー)= Contents of Decrement part of Register
突然この2つの単語が出てきて、「これ何語だよ」と感じた方もいらっしゃるかもしれません。ちゃんと語源があるようです。
1954年のIBM 704という昔のコンピュータで、セルの前半部であるアドレス部を取り出すのがcarで、後半部であるデータ部を取り出すのがcdrだったようです。

second: 2番目の要素を返す

list.lsp
(print (second '(a b c d)))
(print (first (rest '(a b c d)))) ;; firstとrestの組み合わせでも表現できる

出力結果
b
b

third: 3番目の要素を返す

list.lsp
(print (third '(a b c d)))
(print (second (rest '(a b c d)))) ;; secondとrestの組み合わせでも表現できる

出力結果
c
c

fourth: 4番目の要素を返す

10番目の要素を取り出すtenthまで用意されているそうです。

list.lsp
(print (fourth '(a b c d)))
(print (third (rest '(a b c d)))) ;; thirdとrestの組み合わせでも表現できる

出力結果
d
d

cons: 2つの引数からリストを作る

1つ目の引数を、2つ目の引数であるリストの先頭に加えることでリストを作ります。

list.lsp
(print (cons 'a 'b))
(print (cons 'a 'nil))
(print (cons 'a ()))
(print (cons 'a '(b c)))

出力結果
(A . B) ;;真ん中にあるドットが両側の要素をつなげている(ドット対)
(A)     ;;nilはリストを終わらせる
(A)     ;;空リストはnilと同じ意味
(A B C)

list: 引数のリストを作る(3つ以上の引数でも可)

list.lsp
(print (list 'a 'b 'c))
(print (cons 'a (cons 'b 'c)))
(print (cons 'a (cons 'b (cons 'c 'nil))))
(print '(a b c))

出力結果
(A B C)
(A B . C) ;;ドットができてしまう
(A B C)
(A B C)

append: リストを連結する

consと似ていると思った方もいるかもしれません。
1つ目の引数に違いがあります。
基本的にconsは要素、appendはリストを取ります。

list.lsp
(print (append '(a b) '(c d)))

出力結果
(A B C D)

length: リストの長さを返す

list.lsp
(print (length '(a b c)))

出力結果
3

関数

defun: 関数の定義(define function)

Lispでの関数定義はdefunで行います。
試しに階乗(例: 5!=5*4*3*2*1)の関数factを書いてみましょう。

defun.lsp
(defun factorial(n)
  (if (= n 0) 1
    (* n (factorial (- n 1)))))
(print (factorial 5))

出力結果
120

最後に

ハッカーと画家

この記事のためにハッカーと画家を全部読んできました。
簡単にまとめると、筆者のPaul Grahamはこの本で「Lisp最高」と言っています。Lispというマイナーな言語で「Viaweb」を書いたお陰で、競合と差別化を図り成功できたのです。そして、シリコンバレーで「Y Combinator」というシードアクセラレータ(スタートアップ企業を育成する会社)を立ち上げ、AirbnbやDropboxなどに投資し有名になりました。

Lispのマクロが非常に強力だと書いてあったので、次回はマクロについて勉強して書きます。
ここまで読んでいただき、ありがとうございました。

出典: ハッカーと画家

最後に、ハッカーと画家で引用されていたEric Raymond氏の言葉(「How To Become A Hacker」より)と、筆者のPaul Graham氏の言葉をお借りして締めたいと思います。

Lispは、それをものにした時の素晴らしい悟り体験のために勉強しましょう。この体験は、その後の人生でより良いプログラマになる手助けとなるはずです。たとえ、実際にはLispそのものをあまり使われなくても。
引用: 「How To Become A Hacker」 - Eric Raymond

LispのプログラムコードはLISPのデータオブジェクトからできている。それは、ソースコードは文字列でできていて、文字列は言語でサポートされている、というようなつまらない意味じゃない。Lispのコードは、ひとたびパーサによって読まれたら、あなたが解析できるデータ構造になるんだ。
引用: 「ハッカーと画家」 - Paul Graham

参考文献

「Land of Lisp」 - O'REILLY
「ハッカーと画家」 - Paul Graham
「How To Become A Hacker」 - Eric Raymond


Viewing all articles
Browse latest Browse all 25

Trending Articles