101
stibear (@stibear1996) LISP講義 PART Ⅰ

Lisp講義1

Embed Size (px)

DESCRIPTION

use in the Lisp lesson of NPCA

Citation preview

Page 1: Lisp講義1

stibear (@stibear1996)

LISP講義 PART Ⅰ

Page 2: Lisp講義1

LISPの文法 いきなり…

Page 3: Lisp講義1

と,その前に…

Page 4: Lisp講義1

Scheme

• Lispには,たくさんの方言があります

• 今回の講義では,その中でも,Schemeを使います

• 理由としては,シンプルで理解しやすいと思われるためです

Page 5: Lisp講義1

• 3

• 3

• 6

• 2

• (+ 1 2)

• (- 5 2)

• (* 2 3)

• (/ 8 4)

LISPの文法

式(プログラム) 評価結果(実行後)

Page 6: Lisp講義1

簡単にはこんな感じ

Page 7: Lisp講義1

仕組み

• 読み込み(READ)

• 評価(EVAL)

• 表示(PRINT)

• これがループして行われる

• REPL(Read-Eval-Print Loop)と呼ばれる

Page 8: Lisp講義1

LISPの式

(+ 1 2)

演算子 引数

Page 9: Lisp講義1

LISPの式

(+ 1 2)

演算子 引数

※ 引数は演算子に先行して評価されます

Page 10: Lisp講義1

文法に関してはこれだけ

Page 11: Lisp講義1

LISPの構文1 さて次は…

Page 12: Lisp講義1

• x

• y

• 30

• ERROR!!

• (define x 10)

• (define y 20)

• (+ x y)

• (* x z)

define

式(プログラム) 評価結果(実行後)

Page 13: Lisp講義1

仕組み

• Lispの環境に記憶

• (define x 10)とすると,xは10と記憶される

• 環境に記憶することを束縛という

• xは10に束縛される,という

Page 14: Lisp講義1

図解

環境 x=10 y=20 … … …

(define x 10) x

(define y 20) y

束縛

(+ x y) 30

評価

参照

Page 15: Lisp講義1

ならば…

Page 16: Lisp講義1

(+ x 40)とすると?

Page 17: Lisp講義1

図解

(+ x 40) 50 評価

10 40

Page 18: Lisp講義1

整数はそのまま整数に評価されます

Page 19: Lisp講義1

シンボル

• xやyといったものは,シンボルと呼ばれます

• シンボルはhoge,stibearなどなんでも良い

• hideo54やsaiko-no-natsuもシンボルとして扱えます

Page 20: Lisp講義1

define

• よって,defineはシンボルと値を結びつけます

• つまり,束縛するということです

Page 21: Lisp講義1

アトムとリスト Lispといえば…

Page 22: Lisp講義1

リストとアトム

• ()で囲われた式をリストといいます

• そうでないものをアトムといいます

• (+ 1 2)

• (define hoge fuga)

• (foo bar baz)

• これらはすべてリスト

• a

• 200

• これらはすべてアトム

Page 23: Lisp講義1

LISPの構文2 try to eat...

Page 24: Lisp講義1

いきなりですが

Page 25: Lisp講義1

40を評価すると40です

Page 26: Lisp講義1

では

Page 27: Lisp講義1

何を評価すると シンボルxになるでしょう?

Page 28: Lisp講義1

答え:quoteを使います

Page 29: Lisp講義1

• x

• hoge

• saiko-no-natsu

• (quote foo)

• (quote x)

• (quote hoge)

• (quote saiko-no-natsu)

• (quote (quote foo))

quote

式(プログラム) 評価結果(実行後)

Page 30: Lisp講義1

quote

• quoteを使うと,評価を1回止めることができます

• 便利のため,(quote 何々)と書く代わりに,‘何々と書けます

Page 31: Lisp講義1

評価のおさらい

環境 x=10 y=20 … … …

(quote x) x

x 10

評価

参照

評価

Page 32: Lisp講義1

(define husband ‘wife)として

Page 33: Lisp講義1

husbandを評価すると?

Page 34: Lisp講義1

答え:シンボルwifeが返ります

Page 35: Lisp講義1

特殊オペレータ すぺしゃる!

Page 36: Lisp講義1

特殊オペレータ

• quoteやdefineはすべての引数が必ずしも評価されません

• これらが特殊オペレータであるためです

• ほかにも,ifなどが特殊オペレータです

• 特殊オペレータが成す式を特殊フォームといいます

Page 37: Lisp講義1

関数

• 特殊オペレータに対して,+や-のような, 引数がすべて評価されるような演算子を関数といいます

• (関数 引数0 引数1...)のような式を関数呼び出しといいます

• 関数呼び出しを評価すると,関数の返り値が得られます

Page 38: Lisp講義1

同図像性 ホモイコニシティ

Page 39: Lisp講義1

いきなりの難しいセクション!

Page 40: Lisp講義1

しかし, これぞLispの神髄であります

Page 41: Lisp講義1

同図像性

• 簡単に言うと,

• プログラムである式とデータである値が等価だということ

Page 42: Lisp講義1

ところで

Page 43: Lisp講義1

リスト

• 括弧で囲われた式はリスト

• コンスによって成ります

Page 44: Lisp講義1

コンス

• コンスはcar部とcdr部という2つの記憶域を持ちます

• コンスは関数consによって作ることができます

• (cons ‘a ‘b)→(a . b)

• (a . b)はcar部にシンボルa,cdr部にシンボルbを持ちます

Page 45: Lisp講義1

コンス

car

(cons 'a 'b) (a . b)

cdr

a

b

評価

Page 46: Lisp講義1

コンス

• 関数carや関数cdrでそれぞれcar部,cdr部が得られます

• (car (cons ‘a ‘b))→a

• (cdr (cons ‘a ‘b))→b

• コンスは値へのアドレスを持っているだけ

• なのでどんな値も格納できます

• コンスのcdr部がコンスなら,ドットは省略されます

• (cons 'a (cons 'b 'c))→(a b . c)

Page 47: Lisp講義1

コンス

car

(cons 'a (cons 'b 'c)) (a b . c)

cdr

a b

評価

c

car cdr

Page 48: Lisp講義1

コンス

• コンスのcdr部が空リスト()だった場合,cdr部の表示は省略

• (cons ‘a (cons ’b ‘())→(a b)

Page 49: Lisp講義1

コンス

car

(cons 'a (cons 'b '())) (a b)

cdr

a b

評価

car cdr

Page 50: Lisp講義1

リスト(再定義)

• 空リスト

• もしくは,cdr部にリストを格納したコンス

Page 51: Lisp講義1

リスト

空リスト

値 値

Page 52: Lisp講義1

リスト(a b c)を作るには

• (cons ‘a (cons ‘b (cons ‘c ‘())))

• ‘(a b c)

• (list ‘a ‘b ‘c)

Page 53: Lisp講義1

という訳で

Page 54: Lisp講義1

データと式は一緒ですね

Page 55: Lisp講義1

ちなみに

Page 56: Lisp講義1

リストのcdr

• (car ‘(a b c))→a

• (cdr ‘(a b c))→(b c)

Page 57: Lisp講義1

リストのcdr

car cdr

a b c

car cdr car cdr

このコンスのcdrを取る

Page 58: Lisp講義1

リストのcdr

b c

car cdr car cdr

Page 59: Lisp講義1

関数 ふぁ,ふぁ,ふぁんくしょんっ

Page 60: Lisp講義1

関数を作ってみましょう

Page 61: Lisp講義1

関数

• 特殊オペレータlambdaを使って作ります

• (lambda (n) (+ n 1))

• 上は引数を1つとって,それに1を足したものを返す関数です

• 上の式で,nは仮引数で…とかいう話はCとかと一緒なので省略

• 呼び出す時は,リストの最初に置いて,その後に引数を続けます

• (+ 10 20)→30

• 同様に

• ((lambda (n) (+ n 1)) 10)→11

Page 62: Lisp講義1

• #<procedure>

• 11

• 21

• (lambda (n) (+ n 1))

• ((lambda (n) (+ n 1)) 10)

• ((lambda (n) (+ n 1)) 20)

lambda

式(プログラム) 評価結果(実行後)

Page 63: Lisp講義1

関数

• 関数も値

• よってdefineを使ってシンボルを束縛できる

• (define plus1 (lambda (n) (+ n 1)))

• これでplus1というシンボルを使って関数呼び出しができます

• (plus1 29)→30

Page 64: Lisp講義1

関数

• (lambda (仮引数...) 式...)

• 仮引数は束縛変数とも呼ばれ,その関数内でのみ参照できます

• つまり,束縛変数のスコープはその関数内ということです

• 対して,どこでも参照できる変数を大域変数といいます

• 束縛変数の,大域変数に対応する呼び方として,

• 局所変数という呼び方もあります

• 関数は,環境を新たに作ることで,束縛変数を実現しています

Page 65: Lisp講義1

関数

環境 x=10 y=20 … … …

((lambda (x y) (+ x y)) 2 3)

束縛

新たな環境 x=2 y=3

参照

Page 66: Lisp講義1

LISPの構文3 Let It Be

Page 67: Lisp講義1

let

• 局所変数を定義するために,let特殊オペレータを使います

• (let ((a 2) (b 3)) (+ a b))→5

• 簡単ですね

• let特殊オペレータは新たに環境を作ります

Page 68: Lisp講義1

let

環境 x=10 y=20 … … …

(let ((x 2) (y 3)) (+ x y))

束縛

新たな環境 x=2 y=3

参照

Page 69: Lisp講義1

lambdaの時とそっくりですね

Page 70: Lisp講義1

let

• letはlambdaの構文糖衣として考えることができます

• つまり,letはlambdaと等価です

• (let ((a 2) (b 3)) (+ a b))

• ((lambda (a b) (+ a b)) 2 3)

Page 71: Lisp講義1

構文糖衣

• syntax-sugerの訳語

• Wikipediaには,次のように書いてあります

糖衣構文(とういこうぶん)は、プログラミング言語において、読み書きのしやすさのために導入される構文であり、既に定義されている他の構文の(人間にとってより理解しやすい)書換えとして定義されるもののことである。構文糖(こうぶんとう)あるいは構文糖衣ともいう。

Page 72: Lisp講義1

構文糖衣

• quoteの構文糖衣として,「’」があります

• また,関数と定義するときの構文糖衣として,次のように書けます

• (define hoge (lambda (foo) (bar baz)))

• (define (hoge foo) (bar baz))

• どちらも正しい関数定義です

Page 73: Lisp講義1

スコープ ちょっと脱線

Page 74: Lisp講義1

スコープ

• 関数の説明の時にも出てきました

• その変数が見えている(=参照可能な)範囲のことです

• トップレベルでdefineすると,変数のスコープはグローバルに

• 関数の仮引数やletで定義された変数のスコープは,ローカルに なります

• また,Schemeは,レキシカルスコープと呼ばれる種類のスコープを持ちます

Page 75: Lisp講義1

レキシカルスコープ

• 字句的,構文,静的スコープなどともいいます

• これによってクロージャ(関数閉包)というものを成します

Page 76: Lisp講義1

• x

• test

• 100

• 100

• (define x 100)

• (define (test) x)

• (test)

• (let ((x 10)) (test))

レキシカルスコープ

式(プログラム) 評価結果(実行後)

Page 77: Lisp講義1

クロージャ 骨を折ります

Page 78: Lisp講義1

クロージャ

• Schemeにおいて,クロージャは無名関数と同義であるといえます [要出典]

• カプセル化や遅延評価などのためによく使います

Page 79: Lisp講義1

• counter

• c1

• 1

• 2

• 3

• (define (counter) (let ((c 0)) (lambda () (set! c (+ c 1)) c)))

• (define c1 (counter))

• (c1)

• (c1)

• (c1)

クロージャ

式(プログラム) 評価結果(実行後)

Page 80: Lisp講義1

副作用 副作用使用罪だ!!

Page 81: Lisp講義1

副作用

• set!は副作用をもたらす特殊オペレータです

• その他,displayなども副作用をもたらします

• 破壊的代入やI/O制御は副作用を伴います

Page 82: Lisp講義1

再帰 私に還りなさい

Page 83: Lisp講義1

再帰

• あるものについて記述する際に、記述しているものそれ自身への参照が、その記述中にあらわれることをいう(Wikipediaより)

• 再帰的に関数を呼び出すことを再帰呼び出しといいます

• ある関数のなかでその関数が呼び出されているとき, それは再帰関数であるといえます

Page 84: Lisp講義1

• fact

• 120

• (define (fact n) (if (= n 0) 1 (* n (fact (- n 1))))

• (fact 5)

再帰関数

式(プログラム) 評価結果(実行後)

Page 85: Lisp講義1

末尾再帰

• 関数の末尾文脈での関数呼び出しは末尾呼び出しと呼ばれます

• 再帰的な末尾呼び出しを末尾再帰といいます

• Schemeでは,末尾呼び出しが最適化されます

• ループ構造と等価なものに展開され,スタックを消費しないものになります

• 非常に簡単にいうと,無駄が少ないです

Page 86: Lisp講義1

• (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

Page 87: Lisp講義1

非末尾再帰版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)))))

Page 88: Lisp講義1

非末尾再帰版fact関数呼び出し

(* 5 (* 4 (* 3 (* 2 (* 1 1)))))

(* 5 (* 4 (* 3 (* 2 1))))

(* 5 (* 4 (* 3 2)))

(* 5 (* 4 6))

(* 5 24)

120

Page 89: Lisp講義1

末尾再帰版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

Page 90: Lisp講義1

LISPの構文4 Let It Be再び

Page 91: Lisp講義1

named-let

• 名前付きletというもので,ループを上手く書くことができます

Page 92: Lisp講義1

named-letを使ったfact関数

• (define (fact n) (let rec ((a n) (b 1)) (if (= a 0) b (rec (- a 1) (* a b)))))

• letrecを使って似たように定義ができます

Page 93: Lisp講義1

高階関数 今でもあなただけが 青春の...

Page 94: Lisp講義1

高階関数

• 関数を引数として取ったり,返り値として返したりする関数

Page 95: Lisp講義1

map関数

• リストの各要素にマッピング

• (map func list0 list1 ... listN)

Page 96: Lisp講義1

sort関数

• リストの要素をソート

• (sort lst func)

Page 97: Lisp講義1

fold関数

• 畳み込み

Page 98: Lisp講義1

演習 このセクションを書こうとした人は途中で寝てしまいました。

Page 99: Lisp講義1

適当に演習

Page 100: Lisp講義1

参考

• 今回このスライドを作るにあたって, http://lyrical.bugyo.tk/ (魔法言語 リリカル☆Lisp) を参考にしました

• 非常に良い教材です

Page 101: Lisp講義1