2013/10/8
- 数学の記法で書くと
- n! =
-
fact(n) = 1 * 2 * 3 * ... * n
(define (fact n)
(if (<= n 0 )
1
(* n (fact (- n 1)))
)
)
(fact 3)
(define (square x) (* x x))
to square something, multiply it by itself
- (cube x) => x^3
- (foo x) = > 3x^2 + 2x + 1
(cons 1 2) |
(cons 1 nil) |
|
ドット記法 | (1 . 2) |
(1 . nil ) or (1) |
[1,2] |
[1,null] or [1] |
(list 1 2 3 4)
(cons 1 (cons 2 (cons 3 (cons 4))))
(define bar (list 1 2 3 4)) => bar
(car bar) => 1
(cdr bar) => (2 3 4)
(cadr bar) => 2
(cadr bar) => 2
(caddr bar) => 3
(cddr bar) => (3 4)
(cdr (cddr bar)) => (4)
(cddr (cddr bar)) => ()
2013/10/22
- 部分式を評価し値を得る
- 演算子に引数を適用
- 合成式を評価し値を得る
- square
(define (square x)
(* x x)
)
- 正書法は下記
(define
lambda (x) (* x x)
)
(expression)は評価されると値を返す
(define (square x) (* x x))
(define (sum-of-squares x y)
(+ (square x) (square y))
)
(define (f a) (sum-of-squares (+ a 1) (* a 2)))
これはf(a) = (a+1)^2 + (2a)^2
を表す
(f 5)
を実行- fの本体に5を適用
- a を 5 に置換
(sum-of-squares (+ a 1) (* a 2))
→(sum-of-squares (+ 5 1) (* 5 2))
(sum-of-squares x y)
をx = 6
、y = 10
で置換(+ (square x) (square y))
→(+ (square 6) (square 10))
(* x x)
のx
に6と10を置換し計算し結果を戻す(+ 36 100)
- この値を結果として返却 =>
136
-
(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)))))
-
(* 5 (*4 (* 3(* 2 1))))
-
(* 5 (*4 (* 3 2)))
-
(* 5 (*4 6))
-
(* 5 24)
-
120
-
計算量:O(n)
- 時間計算量:2n+1
- 縦軸
- 空間計算量:n+1
- 横軸
- 時間計算量:2n+1
-
keywords
- 再帰呼出
- 再帰定義
factrial
の例のように再帰呼出が末尾で発生するものを**末尾再帰 ( tail recursion )**と呼ぶ
→これは効率化が可能
n! = 1*2*3...*n
- 「1かける2、2かける3、6かける4」と普段は考える。こっちの方が効率が良い
counter = 1,2,...,n
product = counter * product
(ただし、初期値はcounter
、product
ともに1
)
(define (fact-i n)
(fact-iter 1 1 n)
)
(define (fact-iter product counter max-count)
(if (> counter max-count)
product ;counterがmax-countを超えたらproductを結果として返す。
(fact-iter (* counter product)
(+ counter 1)
max-count
)
)
)
(fact 5)
(fact-iter 1 1 5)
(fact-iter 1 2 5)
(fact-iter 2 3 5)
(fact-iter 6 4 5)
(fact-iter 24 5 5)
(fact-iter 120 6 5)
このようなものを線形反復プロセスと呼ぶ。
末尾再帰が反復になり、空間量が減少。
(define (fact n)
(define (iter product counter)
(if (> counter n)
product
(iter (* counter product)
(+ counter 1))))
(iter 1 1)
)
- 手続き
iter
はfact
の中で有効。 - 仮パラメータ
counter
,product
はiter
内のみ有効 - 仮パラメータ
n
はfact
の中で有効→iter
内でも利用可能 iter
のパラメータcounter
,product
は外部からは情報を隠匿- オブジェクト指向言語の1つの特徴へと発展
末尾再帰でないプログラムは、反復に置換しづらい。
処理系にやさしいプログラムを心がけよう。
(fact 5000)
- 末尾再帰と線形反復では約2倍の高速化
- コンパイラーが最適化を行うので、末尾再帰のコードをコンパイルすると線形反復に最適化されるので、同じコードが生成される。
2013/10/29
(define (improve guess x)
(average guess (/ x guess)))
- √を求めるには、面積が√の中になる正方形の辺の長さを求めれば良い。
- 適当な値から中央値を取って近似させていく
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x) ))
(define (improve guess x)
(average guess (/ x guess)))
(define (average x y)
(/ (+ x y) 2))
(define (good-enough? guess x)
(< (abs (- (square guess) x)) 0.001))
(define (sqrt x) (sqrt-iter 1.0 x))
`sqrt` -- `sqrt-iter`
|
-- good-enough?
| |
| -- `square`
| -- `abs`
-- improve
|
-- `average`
- 内部実装の隠蔽
(define (square x) (* x x))
(define (square x)
(exp (double (log x))))
(define (double x) (+ x x))
- `e^(2log(x)) = e^(log(x^2)) = x^2`
- 計算機内では2進数で扱われるので、`double`などを用いるほうが高速
- 局所名の隠蔽
(define (square x) (* x x))
(define (square y) (* y y))
- 束縛変数
- 仮パラメータは手続きで束縛
- 自由変数
- 束縛(capture)されていない
- 有効範囲(scope)
- 変数の束縛されている式の範囲
(define (sqrt x)
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x) ))
(define (improve guess x)
(average guess (/ x guess)))
(define (average x y)
(/ (+ x y) 2))
(define (good-enough? guess x)
(< (abs (- (square guess) x)) 0.001))
(sqrt-iter 1.0 x))
- それぞれの`x`の有効範囲は?
- 静的有効範囲
- 一度には1枚の円盤しか動かせない
- 小さい円盤の上には大きな円盤は置けない
| | |
| | |
1 2 3
(define (move-tower size from to extra)
(cond ((= size 0) #true)
(else
(move-tower (- size 1) from extra to)
(print-move from to)
(move-tower (- size 1) extra to from))
))
(define (print-move from to)
(newline)
(display "move top disk from")
(display from)
(display "to")
(display to))
(define (solbe-tower-of-hanoi size from to)
(move-tower size from to (- 6 from to)))
| | |
||| | |||
||||| ||||| |||||
1 from 2 to 3 extra
form
からextra
に一度移してからto
に戻す
- 擬似コード(javascript)で
function Ack(m,n){
if(m = 0) n+1
elseif(n = 0) Ack(m-1, 1)
else Ack(m-1, Ack(m,n-1)
}
Ack(1,2)
Ack(0, Ack(1,1))
Ack(1,1)+1
Ack(0, Ack(1,0))+1
Ack(1,0)+2
Ack(0,1)+2
1+1+2
4
(ack 0 2)
(ack 1 2)
(ack 2 2)
(ack 3 2)
計算過程を書くこと
それぞれ理由も記せ
(ack 0 n) ≡ n+1
(ack 1 n) ≡ ?
(ack 2 n) ≡ ?
(ack 3 n) ≡ ?
(ack 4 n) ≡ ?
- 1ドルの両替方法は何通り?
(define (count-change amount)
(cc amount 5))
(define (cc amound kinds-of-coins)
(cond ((= amount 0) 1)
((or (< amount 0) (= kinds-of-coins 0)) 0)
(else
(+ (cc amount (- kinds-of-coins 1))
(cc (- amount
(first-denomination
kinds-of-coins))
kinds-of-coins)))))
(define (first-denomination kinds-of-coins)
(cond ((= kinds-of-coins 1) 1)
((= kinds-of-coins 2) 5)
((= kinds-of-coins 3) 10)
((= kinds-of-coins 4) 20)
((= kinds-of-coins 5) 50)
)
)
- R(n)がΘ(f(n))
- 上下限
- R(n)がO(n)
- 上限
- R(n)がΩ(n)
- 下限
- Θ(1)
- constant growth
- Θ(n)
- linear growth
- Θ(b^n)
- exponential growth
- Θ(log(n))
- logarithmic growth
- Θ(n^m)
- power law growth
- うさぎのつがい
- 内部反射回数
function fib(n){
if (n = 0) 0
elseif (n = 1) 1
else fib(n-1) + fib(n-2)
}
(define (fib n)
(cond ((= n 0) 0)
((= n 1) 1)
(else (+ (fib (- n 1))
(fib (- n 2)))))
)
(define (fib-i n)
(define (iter a b count)
(if (= count 0)
b
(iter (+ a b) a (- count 1))))
(iter 1 0 n)
)
2013/11/5
n >= 2
に対して、C(n) = C(n-1)+C(n-2)+1
F(n) = C(n)+1
とおくとF(n)-F(n-1)+F(n-2)
forn>=2
F(0)=2
,F(1)=2
|手続き|ステップ数|スペース| |再帰型|2/√5 O(Φ^n)|O(Φ^n)| |繰返型|n+1 O(n)|O(1)| |テーブル参照型|1 O(1)|O(n)|
(define (fast-expt b n)
(cond ((= n 0) 1)
((even? n)
(square (fast-expt b (/ n 2))))
(else
(* b (fast-expt b (- n 1))) )))
(define (even? n)
(= (remainder n 2) 0) )
- X^16
- 16=10000(2)より2進数を4回左シフト
- まず,1を
SX
,0をS
で置換 - 先頭の
SX
を除く - 得られた
S
とX
をSquare
、xをかける
と読む
- 23= 10111(2)
- SX S SX SX SX
- S SX SX SX
- a mod b = r
- GCD(a, b) = GCD(b, r)
が成立する。
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b)) ))
a≡b mod n
(congruent modulo n)- 「a mod n と b mod n が等しい」
- x modulo nは剰余
- a+b mod n ≡ (a mod n + b mod n) mod n
- a*b mod n ≡ (a mod n * b mod n) mod n
2013/11/12
∑i = a + ∑(a→b)i
(define (sum-integers a b)
(if (> a b)
0
(+ a (sum-integers (+ a 1) b))))
∑i = f(a) + ∑f(i)
(define (sum-f a b)
(if (> a b)
0
(+ (f a) (sum-f (sum-f (+ a 1) b)))))
∑(i=a,next(i)→b) {f(i)}
- fの関数の中身: term
- 値の増え方: next
(define (sum term a next b)
(if (> a b)
0
(+ (term a)
(sum term (next a) next b))))
↓
(define (sum-integers a b)
(define (inc x) (+ x 1))
(sum f a inc b))
Π(a -> b)(i) = a * Π(a+1 -> b)(i)
(define (product-integers a b)
(if (> a b)
1
(* a (product-integers (+ a 1) b)))
)
Π(a -> b)f(i) = f(a) * Π(a+1 -> b)f(i)
(define (product-f a b)
(if (> a b)
1
(* (f a) (product-f (+ a 1) b))))
(define (product term a next b)
(if (> a b)
1
(* (term a)
(product term (next a) next b))))
(define (accumulate combiner null-value term a next b)
(if (> a b)
null-value
(combiner (term a)
(accumulate combiner null-value term (next a) b))))
combiner
はsum
を再現するなら+
、product
を再現するなら*
を与える
(define (factrial n)
(accumulate * 1 identity 1 inc n))
(define (plus4 x) (+ x 4))
は(define (plus4 (lambda (x) (+ x 4))))
と等価。
((lambda (x y z)(+ x y (square z))) 1 2 3)
- x=1, y=2, z=3 を代入して実行
(+ 1 2 (square 3))
f(x,y) = x(1+xy)^2 + y(1-y) + (1+xy)(1-y)
を解くのに補助変数を使いたい
(define (f x y)
(define (f-helper a b)
(+ (* x (square a)
(* y b)
(* a b) )))
(f-helper
(+ 1 (* x Y)
(- 1 y)))
)
(define (f x y)
(let ((a (+ 1 (* x y)))
(b (- 1 y)))
(+ (* x (aquare a))
(* y b)
(* a b) )))
##変数スコープ
(let ((x 7))
(+ (let ((x 3))
(+ x (* x 10)) )
x) )
(let ((x 3))
(+ x (* x 10)) )
=> 33
(let ((x 7))
(+ 33 x) )
=> 40
(let ((x 5))
(let* ((x 3)
(y (+ x 2)) )
(* x y) ))
- 第1章は手続き抽象化
- 第2章はデータ抽象化
- 基本データ構造
- 合成データオブジェクト
- データ抽象化で手続きの意味を拡張
「具体から抽象へは行けるが、抽象から具体へは行けない」
- 構成子(constructor)
- 選択子(selector)
- 述語(predicate)
- 入出力(input-output)
次回は有理数から
- 課題
- 2.1.3 ~ 2.2.2までを読む
- ex 2.17 ~ 2.28
2013/11/19
(define (jmc n)
(if (> n 100)
(- n 10)
(jmc (jmc (+ n 11)))
))
(make-rat <n> <d>)
(numer <x>)
(denom <x>)
(rational? <x>)
(equal-rat <x> <y>)
(define (add-rat x y)
(make-rat (+ (* (numer x)(denom y))
(* (denom x)(numer y)))
(* (denom x)(denom y))))
(define (sub-rat x y)
(make-rat (- (* (numor x)(denom y))
(* (denom x)(numor y)))
(* (denom x)(denom y))))
(define (mul-rat x y)
(make-rat (* (numer x)(numer y))
(* (denom x)(denom y))))
(define (div-rat x y)
(make-rat (* (numer x)(denom y))
(* (denom x)(numer y))))
(define (equal-rat? x y)
(= (* (numer x)(denom y))
(* (denom x)(numer y))))
- ペア(対, pair)で表現する
(define (make-rat n d) (cons n d))
(define (numer x)(car x))
(define (denom x)(cdr x))
この表現では不十分
- 分母が0になる可能性
- 既約分数に直す必要
make-rat
で行う?denom
やnumer
で行う?denom
やnumer
を利用する回数が多いなら、コストになる?
add-rat
やsub-rat
などからmake-rat
を利用する
(make-rat n d)
が満たすべき条件
- 通常のセルで構築:
- 構築子: cons
- 選択子: car, cdr
- 以下の手続きで構築
(define (make-rat n d)
(define (dispatch m)
(cond ((= m 0) n)
((= m 1) d)
(else (error ""))))
dispatch )
;ここまで構築子
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
(define (foo (cons 10 25)))
(lambda (m) (m 10 25))
(car foo)
((lambda (m) (m 10 25)) (lambda (p q) p))
((lambda (p q) p) 10 25)
10
(null? <e>)
<e>
がnilか
(eq? <e1> <e2>)
<e1>
と<e2>
が同じオブジェクトか
(cons <e1> <e2>)
<e1>
と<e2>
からpairを生成
(car <pair>)
(cdr <pair>)
(define (list-ref items n)
(define (iter count li)
(cond ((null? (cdr li))())
((= count n)(car li))
(else
(iter (+ count 1) (cdr li))))
)
(if (pair? items)(iter 0 items)
() ;itemがリストじゃなければ()を返す
)
)
(define (length items)
(if (null? items)
0
(+ 1 (length (cdr items)))))
↓
(define (length-i items)
(define (iter count items)
(if (null? (cdr items))
count
(iter (+ count 1) (cdr items)))
)
(if (null? items)
0
(iter 1 items)))