SlideShare a Scribd company logo
stibear
(@stibear1996)
LISP講義
PART Ⅰ
LISPの文法
いきなり…
と,その前に…
Scheme
• Lispには,たくさんの方言があります
• 今回の講義では,その中でも,Schemeを使います
• 理由としては,シンプルで理解しやすいと思われるためです
• 3
• 3
• 6
• 2
• (+ 1 2)
• (- 5 2)
• (* 2 3)
• (/ 8 4)
LISPの文法
式(プログラム) 評価結果(実行後)
簡単にはこんな感じ
仕組み
• 読み込み(READ)
• 評価(EVAL)
• 表示(PRINT)
• これがループして行われる
• REPL(Read-Eval-Print Loop)と呼ばれる
LISPの式
(+ 1 2)
演算子 引数
LISPの式
(+ 1 2)
演算子 引数
※ 引数は演算子に先行して評価されます
文法に関してはこれだけ
LISPの構文1
さて次は…
• x
• y
• 30
• ERROR!!
• (define x 10)
• (define y 20)
• (+ x y)
• (* x z)
define
式(プログラム) 評価結果(実行後)
仕組み
• Lispの環境に記憶
• (define x 10)とすると,xは10と記憶される
• 環境に記憶することを束縛という
• xは10に束縛される,という
図解
環境 x=10 y=20 … … …
(define x 10) x
(define y 20) y
束縛
(+ x y) 30
評価
参照
ならば…
(+ x 40)とすると?
図解
(+ x 40) 50
評価
10 40
整数はそのまま整数に評価されます
シンボル
• xやyといったものは,シンボルと呼ばれます
• シンボルはhoge,stibearなどなんでも良い
• hideo54やsaiko-no-natsuもシンボルとして扱えます
define
• よって,defineはシンボルと値を結びつけます
• つまり,束縛するということです
アトムとリスト
Lispといえば…
リストとアトム
• ()で囲われた式をリストといいます
• そうでないものをアトムといいます
• (+ 1 2)
• (define hoge fuga)
• (foo bar baz)
• これらはすべてリスト
• a
• 200
• これらはすべてアトム
LISPの構文2
try to eat...
いきなりですが
40を評価すると40です
では
何を評価すると
シンボルxになるでしょう?
答え:quoteを使います
• x
• hoge
• saiko-no-natsu
• (quote foo)
• (quote x)
• (quote hoge)
• (quote saiko-no-natsu)
• (quote (quote foo))
quote
式(プログラム) 評価結果(実行後)
quote
• quoteを使うと,評価を1回止めることができます
• 便利のため,(quote 何々)と書く代わりに,‘何々と書けます
評価のおさらい
環境 x=10 y=20 … … …
(quote x) x
x 10
評価
参照
評価
(define husband ‘wife)として
husbandを評価すると?
答え:シンボルwifeが返ります
特殊オペレータ
すぺしゃる!
特殊オペレータ
• quoteやdefineはすべての引数が必ずしも評価されません
• これらが特殊オペレータであるためです
• ほかにも,ifなどが特殊オペレータです
• 特殊オペレータが成す式を特殊フォームといいます
関数
• 特殊オペレータに対して,+や-のような,
引数がすべて評価されるような演算子を関数といいます
• (関数 引数0 引数1...)のような式を関数呼び出しといいます
• 関数呼び出しを評価すると,関数の返り値が得られます
同図像性
ホモイコニシティ
いきなりの難しいセクション!
しかし,
これぞLispの神髄であります
同図像性
• 簡単に言うと,
• プログラムである式とデータである値が等価だということ
ところで
リスト
• 括弧で囲われた式はリスト
• コンスによって成ります
コンス
• コンスはcar部とcdr部という2つの記憶域を持ちます
• コンスは関数consによって作ることができます
• (cons ‘a ‘b)→(a . b)
• (a . b)はcar部にシンボルa,cdr部にシンボルbを持ちます
コンス
car
(cons 'a 'b) (a . b)
cdr
a
b
評価
コンス
• 関数carや関数cdrでそれぞれcar部,cdr部が得られます
• (car (cons ‘a ‘b))→a
• (cdr (cons ‘a ‘b))→b
• コンスは値へのアドレスを持っているだけ
• なのでどんな値も格納できます
• コンスのcdr部がコンスなら,ドットは省略されます
• (cons 'a (cons 'b 'c))→(a b . c)
コンス
car
(cons 'a (cons 'b 'c)) (a b . c)
cdr
a b
評価
c
car cdr
コンス
• コンスのcdr部が空リスト()だった場合,cdr部の表示は省略
• (cons ‘a (cons ’b ‘())→(a b)
コンス
car
(cons 'a (cons 'b '())) (a b)
cdr
a b
評価
car cdr
リスト(再定義)
• 空リスト
• もしくは,cdr部にリストを格納したコンス
リスト
空リスト
値 値
値
リスト(a b c)を作るには
• (cons ‘a (cons ‘b (cons ‘c ‘())))
• ‘(a b c)
• (list ‘a ‘b ‘c)
という訳で
データと式は一緒ですね
ちなみに
リストのcdr
• (car ‘(a b c))→a
• (cdr ‘(a b c))→(b c)
リストのcdr
car cdr
a b c
car cdr car cdr
このコンスのcdrを取る
リストのcdr
b c
car cdr car cdr
関数
ふぁ,ふぁ,ふぁんくしょんっ
関数を作ってみましょう
関数
• 特殊オペレータlambdaを使って作ります
• (lambda (n) (+ n 1))
• 上は引数を1つとって,それに1を足したものを返す関数です
• 上の式で,nは仮引数で…とかいう話はCとかと一緒なので省略
• 呼び出す時は,リストの最初に置いて,その後に引数を続けます
• (+ 10 20)→30
• 同様に
• ((lambda (n) (+ n 1)) 10)→11
• #<procedure>
• 11
• 21
• (lambda (n) (+ n 1))
• ((lambda (n) (+ n 1)) 10)
• ((lambda (n) (+ n 1)) 20)
lambda
式(プログラム) 評価結果(実行後)
関数
• 関数も値
• よってdefineを使ってシンボルを束縛できる
• (define plus1 (lambda (n) (+ n 1)))
• これでplus1というシンボルを使って関数呼び出しができます
• (plus1 29)→30
関数
• (lambda (仮引数...) 式...)
• 仮引数は束縛変数とも呼ばれ,その関数内でのみ参照できます
• つまり,束縛変数のスコープはその関数内ということです
• 対して,どこでも参照できる変数を大域変数といいます
• 束縛変数の,大域変数に対応する呼び方として,
• 局所変数という呼び方もあります
• 関数は,環境を新たに作ることで,束縛変数を実現しています
関数
環境 x=10 y=20 … … …
((lambda (x y) (+ x y)) 2 3)
束縛
新たな環境 x=2 y=3
参照
LISPの構文3
Let It Be
let
• 局所変数を定義するために,let特殊オペレータを使います
• (let ((a 2) (b 3)) (+ a b))→5
• 簡単ですね
• let特殊オペレータは新たに環境を作ります
let
環境 x=10 y=20 … … …
(let ((x 2) (y 3)) (+ x y))
束縛
新たな環境 x=2 y=3
参照
lambdaの時とそっくりですね
let
• letはlambdaの構文糖衣として考えることができます
• つまり,letはlambdaと等価です
• (let ((a 2) (b 3)) (+ a b))
• ((lambda (a b) (+ a b)) 2 3)
構文糖衣
• syntax-sugerの訳語
• Wikipediaには,次のように書いてあります
糖衣構文(とういこうぶん)は、プログラミング言語において、読み
書きのしやすさのために導入される構文であり、既に定義されている
他の構文の(人間にとってより理解しやすい)書換えとして定義され
るもののことである。構文糖(こうぶんとう)あるいは構文糖衣とも
いう。
構文糖衣
• quoteの構文糖衣として,「’」があります
• また,関数と定義するときの構文糖衣として,次のように書けます
• (define hoge (lambda (foo) (bar baz)))
• (define (hoge foo) (bar baz))
• どちらも正しい関数定義です
スコープ
ちょっと脱線
スコープ
• 関数の説明の時にも出てきました
• その変数が見えている(=参照可能な)範囲のことです
• トップレベルでdefineすると,変数のスコープはグローバルに
• 関数の仮引数やletで定義された変数のスコープは,ローカルに
なります
• また,Schemeは,レキシカルスコープと呼ばれる種類のスコープ
を持ちます
レキシカルスコープ
• 字句的,構文,静的スコープなどともいいます
• これによってクロージャ(関数閉包)というものを成します
• x
• test
• 100
• 100
• (define x 100)
• (define (test) x)
• (test)
• (let ((x 10)) (test))
レキシカルスコープ
式(プログラム) 評価結果(実行後)
クロージャ
骨を折ります
クロージャ
• Schemeにおいて,クロージャは無名関数と同義であるといえます
[要出典]
• カプセル化や遅延評価などのためによく使います
• counter
• c1
• 1
• 2
• 3
• (define (counter)
(let ((c 0))
(lambda ()
(set! c (+ c 1))
c)))
• (define c1 (counter))
• (c1)
• (c1)
• (c1)
クロージャ
式(プログラム) 評価結果(実行後)
副作用
副作用使用罪だ!!
副作用
• set!は副作用をもたらす特殊オペレータです
• その他,displayなども副作用をもたらします
• 破壊的代入やI/O制御は副作用を伴います
再帰
私に還りなさい
再帰
• あるものについて記述する際に、記述しているものそれ自身への参
照が、その記述中にあらわれることをいう(Wikipediaより)
• 再帰的に関数を呼び出すことを再帰呼び出しといいます
• ある関数のなかでその関数が呼び出されているとき,
それは再帰関数であるといえます
• fact
• 120
• (define (fact n)
(if (= n 0) 1
(* n (fact (- n 1))))
• (fact 5)
再帰関数
式(プログラム) 評価結果(実行後)
末尾再帰
• 関数の末尾文脈での関数呼び出しは末尾呼び出しと呼ばれます
• 再帰的な末尾呼び出しを末尾再帰といいます
• Schemeでは,末尾呼び出しが最適化されます
• ループ構造と等価なものに展開され,スタックを消費しないものに
なります
• 非常に簡単にいうと,無駄が少ないです
• (define (fact n)
(fact-tc n 1))
• (define (fact-tc n m)
(if (= n 0) m
(fact-tc (- n 1)
(* n m))))
• (define (fact n)
(if (= n 0) 1
(* n (fact (- n 1))))
末尾再帰
非末尾再帰版fact 末尾再帰版fact
非末尾再帰版fact関数呼び出し
(fact 5)
(* 5 (fact 4))
(* 5 (* 4 (fact 3)))
(* 5 (* 4 (* 3 (fact 2))))
(* 5 (* 4 (* 3 (* 2 (fact 1)))))
(* 5 (* 4 (* 3 (* 2 (* 1 (fact 0))))))
(* 5 (* 4 (* 3 (* 2 (* 1 1)))))
非末尾再帰版fact関数呼び出し
(* 5 (* 4 (* 3 (* 2 (* 1 1)))))
(* 5 (* 4 (* 3 (* 2 1))))
(* 5 (* 4 (* 3 2)))
(* 5 (* 4 6))
(* 5 24)
120
末尾再帰版fact関数呼び出し
(fact 5)
(fact-tc 5 1)
(fact-tc 4 5)
(fact-tc 3 20)
(fact-tc 2 60)
(fact-tc 1 120)
(fact-tc 0 120)
120
LISPの構文4
Let It Be再び
named-let
• 名前付きletというもので,ループを上手く書くことができます
named-letを使ったfact関数
• (define (fact n)
(let rec ((a n) (b 1))
(if (= a 0) b
(rec (- a 1) (* a b)))))
• letrecを使って似たように定義ができます
高階関数
今でもあなただけが 青春の...
高階関数
• 関数を引数として取ったり,返り値として返したりする関数
map関数
• リストの各要素にマッピング
• (map func list0 list1 ... listN)
sort関数
• リストの要素をソート
• (sort lst func)
fold関数
• 畳み込み
演習
このセクションを書こうとした人は途中で寝てしまいました。
適当に演習
参考
• 今回このスライドを作るにあたって,
http://lyrical.bugyo.tk/ (魔法言語 リリカル☆Lisp)
を参考にしました
• 非常に良い教材です
終

More Related Content

Lisp講義1