プログラミング言語/Scheme
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
*Scheme [#id4921b9]
#contents
*はじめに [#n0265760]
「Scheme?聞いたことねぇな」と思いのそこのあなた。~
ちょっと立ち寄ってみませんか?~
~
Schemeの基礎の基礎から初めてScheme中級者(世に存在する難...
*言語の特徴 [#fa6921f3]
Perlなどと同じスクリプト言語である。~
プログラム全体が「リスト構造」という構造を持っている。(...
Perlの代わりに使ってcgiとかも書けちゃう。~
C等より抽象度が高い。(例えば整数の実装が表に出ておらず、...
その他難しいことはよく知りません。
*処理系のインストール [#x5131cfe]
Schemeは言語仕様の小ささも売りであり、そのため多くの実装...
ここではGaucheという処理系を使うことにします。~
~
**Gaucheのインストール(Windows) [#p3d7ac56]
[[Gauche公式:http://www.shiro.dreamhost.com/scheme/gauche...
んで解凍しておしまい。~
binの中にあるgosh.exeを実行して
gosh>
と表示されたらOK~
(のちのち便利なように gauche/bin/ にPATHを通しておきまし...
~
**Gaucheのインストール(Windows以外) [#qa451660]
同じく[[Gauche公式:http://www.shiro.dreamhost.com/scheme/...
詳しくはダウンロードページに書いてあります。~
MacOS X上でも動くらしいです。~
~
*とりあえず動かそう [#t99c1dad]
**Hello,World [#n2630372]
慣例に従って"Hello,World"と表示するプログラムを書いてみま...
(display "Hello,World")
(newline)
これを適当な名前で保存し、(拡張子は.scmにするのが一般的...
gosh D:/programs/scheme/test.scm
などととしてください。~
(gosh.exeのあるフォルダにPATHが通っていることを想定して...
~
ちなみに、UNIX系OS上では、ファイルの一行目に
#!/usr/local/bin/gosh
としてtest.scmを実行可能にすることでtest.scmを直接実行で...
~
**解説 [#ya5dce36]
(display "Hello,World")
見たとおり、displayという関数(Schemeでは手続きという)を...
Schemeでは手続きを呼ぶときに
(手続き名 引数1 引数2 … 引数n)
とします。
(newline)
改行を表示します。~
~
**むずかしめの解説 [#ned8d11b]
むずかしめの解説です。おもいきり読み飛ばしてOKです。~
~
言語の特徴のところで「プログラム全体がカッコでくくられた...
しかしこのプログラム、どうみても(display "Hello,World")と...
~
実はこのプログラムは、
((lambda () (begin
(display "Hellow,world")
(newline)
)))
の略記だと考えることが出来ます。これで全体が一つのカッコ...
いきなりわけが分からなくなりましたが、今はこのコードを理...
後に関数呼び出しやlambda式をやると理解できます。~
~
*計算してみよう [#k0b233e2]
**対話式実行 [#n2b91510]
次は計算をさせてみましょう。~
ですがその前に、対話式実行というのを説明します。(たいした...
今回はgoshにファイル名を渡すのではなく、goshを単体で起動...
gosh>
と表示されるはずです。~
このウィンドウ上でプログラムを1行づつ打ち込み、実行させる...
~
**計算しよう [#c5fd49de]
goshを起動し、(+ 1 2)と打ち込んでエンターを押してください。
gosh>(+ 1 2)
3
このようになるはずです。~
~
これを見て色々思い浮かぶでしょうが、一つ一つ見ていきまし...
-(1 + 2)じゃないの?
Schemeでは「ポーランド記法」という書き方を使いますので(+ ...
簡単に言うと、
(演算子 被演算子1 被演算子2 … 被演算子n)
の形に式を書けばよいです。(詳しく知りたい方は検索してくだ...
例えば {-b+√(b^2-4ac)} / 2a は
(/ (+ -b (√ (- b^2 4ac))) 2a)
などとなるでしょう。(これは正しいScheme式の書き方ではあり...
~
-外側のカッコは何?
この式の意味は、「+という手続きを、1と2を引数にして呼び出...
(これはたとえではなく本当の話です。Schemeでは + は基本構...
Schemeでは関数呼び出しは外側にカッコをつけることになって...
+ という関数があって、+(1,2) を計算したと思えばいいでしょ...
~
-(display (+ 1 2))じゃないの?
対話式実行時は、Scheme処理系は「与えられたリストを評価し...
なのでdisplayは必要なく、答えが3になる式を与えれば3と表示...
(HelloWorldの時のようにファイル名を与えて実行するなら、d...
~
*手続きを定義しよう [#n917d5cf]
計算もできたので、今度は手続き(一般に言う関数)を定義し...
まず下のコードをエディタで打ち込んで保存してみてください。
(define (inc x)
(+ x 1)
)
~
**実行しよう [#h4823dfc]
goshを単体で起動し、今作ったプログラムをloadします。
gosh>(load "D:/programs/scheme/inc.scm")
#t
となるはずです。注意事項として、windowsではディレクトリの...
~
ちなみに#tは(load …)を「評価した結果」です。(Schemeでは...
さしあたりは「Cで関数が成功したら1が返ってくるようなもの...
~
loadが出来たら実行します。
gosh>(inc 1)
2
gosh>(inc 10)
11
などとなります。~
(カッコを忘れないことと、inc(1)ではないことに注意。)~
ちなみに言語の特徴のところでも書きましたが、
gosh>(inc 10000000000000000000000000000000000000000000000)
としてもあふれてマイナスになったりしません。
**解説 [#j054fccd]
(define (inc x)
incという手続きを、次に与えるリストで定義します。~
xは仮引数と呼ばれ、C風に書くと
int inc(int x);
のxにあたります。~
~
(+ x 1)
これは 計算しよう でやったのと同じです。~
この式を評価するとxに1を足したものになります。~
~
)
defineの終わりです。前にも書いたとおりプログラム全体が一...
~
*基本構文 [#ede79fdb]
手続きを定義して呼び出す方法をやりました。~
次はプログラムをする上で必要なifなどの構文についてです。~
~
**全ては評価される [#ia022369]
ifなどをやる前に、評価されるとはどういうことかをやります...
Schemeでは全ての式は評価されて何らかの値(または別の式)...
例えば、Cでは
if(x>=0){
printf("xはプラスです");
}
else{
printf("xはマイナスです");
}
は、x≧0の時は上側のパスを、それ以外のときは下側のパスを通...
しかし、Schemeで上と同じコードを(Schemeの文法に則って)...
「評価すると条件が真なら printf("xはプラスです") が、偽な...
~
分かりにくいですね。先にifのところを読んで例を見てからも...
~
~
**if [#x68dbbef]
構文
(if 条件 式1 式2)
条件が真なら式1が、偽なら式2が返ります。~
条件設定は一つだけで、else ifとかにあたるものは次のcondの...
~
例1:
(define (abs x)
(if (>= x 0) x (* -1 x))
)
xの絶対値を返すプログラムです。条件ももちろんポーランド記...
~
例2:
(define (abs2 x)
(* (if (>= x 0) 1 -1) x)
)
xの絶対値を返すプログラムその2です。~
これは全ての式が評価されることを理解するよい例です。~
処理系は(if (>= x 0) 1 -1)を評価し、(>= x 0)が成り立てば...
Cで書くとすれば(if …)は
(x>=0 ? 1 : -1)
となるでしょうか。~
~
例3:~
評価した結果は数字や式(例1での(* -1 x)など)だけではなく...
(define (abs3 x)
((if (>= x 0) identify abs) x)
)
但し
(define (identify x) x)
(if …)は(>= x 0)が成り立てばidentifyになるので、全体とし...
成り立たなければabsになるので(abs x)となり|x|が返ります。~
(別にはじめから(abs x)とすればよいのですが、まぁ例なので...
~
**cond [#p15b0213]
構文
(cond (条件1 式1) (条件2 式2) … (条件n 式n))
条件を1から順番に評価し、一番最初に成り立ったもの(#tを返...
~
例1:
(define (divide2 x)
(cond
((odd? x) x)
((even? (/ x 2)) (/ x 4))
(else (/ x 2))
)
)
xが奇数ならxを、それ以外で(つまりxが偶数で)x/2が偶数な...
odd?とeven?ははじめから定義されている手続きで、(odd? x)は...
~
最後の条件であるelseは意味的にはC等のelseと同じです。~
式の評価という観点から見ると、elseは常に真を返すといえま...
つまり、
(define (divide2 x)
(cond
((odd? x) x)
((even? (/ x 2)) (/ x 4))
(#t (/ x 2))
)
)
と書いても同じ結果がえられます。~
~
例2:
(define (divide2-2 x)
(/ x (cond ((odd? x) 1) ((even? (/ x 2)) 4) (#t 2)))
)
divide2と同じ結果を生じます。~
(cond …)が評価されてxを割る数が決まるわけですね。~
「式が評価されて結果が返る」ということがだいぶ分かってき...
~
**begin [#bdf5b1a7]
構文
(begin 式1 式2 … 式n)
beginは与えられた式を1から順に最後まで実行します。~
beginを評価した結果は、式nを評価した結果と同じになります。~
~
例1:
gosh>(begin (display "test") (newline) #t)
画面にtestと表示して改行し、#tを返します。~
実行すると、評価された結果も印字されるので、
gosh>(display-test)
test
#t
となります。~
begin式を評価した結果は最後の式を評価した値と同じになりま...
gosh>(if (begin (display "test") (newline) #t) 1 0)
test
1
のようになります。~
まずdisplayとnewlineを実行したのでtestと改行が印字されま...
次にbeginの評価結果は#t(真)ですからifの条件が成り立ち、...
~
**#tと#f [#ra8cc83d]
今までなんとなく使ってきた#tですが、ここで一応説明してお...
これらは評価されると、#tは真を、#fは偽をあらわします。
つまり、
gosh>(if #t 1 0)
1
gosh>(if #f 1 0)
0
ということです。~
~
**二項演算子 [#w6577155]
(演算子 引数1 引数2)
の形で、評価されると計算結果または真偽を返します。~
+、-、*、/、=、>、<、>=、<=、and、orがあります。~
(但し+、*、and、orは3つ以上の引数を取れます。)~
~
厳密にはこれらは演算子ではなく手続きなのですが、演算子だ...
~
**単項演算子 [#u7e40be8]
(演算子 引数)
の形で、評価されると真偽を返します。~
not、number?、null?などがあります。~
(これらも本当は手続きですが演算子だと思って問題ありませ...
~
Cでは !1 は偽、 !0 は真ですが、Schemeでは
gosh>(not 1)
#f
gosh>(not 0)
#f
のように両方とも偽になります。~
これは「評価して値が返ってきたものは#tとみなす」からです。
gosh>(if 1 #t #f)
#t
gosh>(if 0 #t #f)
#t
但し、もちろん#fのnotは#tですので
gosh> (not (if #t #f #t))
#t
となります。~
~
**文字と文字列 [#raa6429b]
例えば引数がnameなら名前を、ageなら年齢を表示するにはどう...
試しに次のようにしてみましょう。~
~
例1:
(define (private-information type)
(cond
((= type name) Hiroyuki)
((= type age) 1000000)
(#t Error)
)
)
このプログラムは大いに問題ありですが、ともかくloadは成功...
(なぜ成功するかはすぐ後で明らかになります。)~
~
では実行してみましょう。
gosh> (private-information name)
*** ERROR: unbound variable: name
nameが見つからないといわれてしまいました。~
これは処理系が「nameという名前の変数を探しにいったから」...
そんな変数は定義していないので当然見つかりません。~
(逆に言うと、もしnameやらageやらErrorやらがdefineされて...
~
Schemeでは変数名と文字列を区別するために、文字列は手続き(...
また、文字列と文字列が等しいかどうかを見るには = ではなく...
~
例2:
(define (private-information type)
(cond
((eq? type (quote name)) (quote Hiroyuki))
((eq? type (quote age)) 1000000)
(#t (quote Error))
)
)
loadして実行すると、
gosh> (private-information (quote age))
1000000
gosh> (private-information (quote name))
Hiroyuki
となります。正しく動きました。~
(引数として与える文字列もquoteを通さないといけないことに...
~
しかし、いちいち(quote …)と書くのは面倒なので、簡略化した...
それが「'」です。~
~
例3:
(define (private-information type)
(cond
((eq? type 'name) 'Hiroyuki)
((eq? type 'age) 1000000)
(#t 'Error)
)
)
このプログラムは例2と同じ結果を生じます。~
呼び出すときも(private-information 'age)のように ' を使う...
~
注:
gosh> (eq? (quote test) 'test)
#t
~
*ちょっと複雑なことをしてみよう [#r994155f]
では今までのことを使って多少複雑なプログラムを書いてみま...
例1:
(define (factorial n)
(if (= n 0) 1 (* n (factorial (- n 1))))
)
n!を返します。~
このように再帰呼び出しもできます。~
~
例2:
(define (pascal x y)
(cond
((or (= y 0) (= y (+ x 1))) 0)
((= x 1) 1)
(#t (+ (pascal (- x 1) (- y 1)) (pascal (- x 1) y)))
)
)
パスカルの3角形のx行y列の値を返します。~
~
*変数を使おう [#gf0f61ee]
次は変数を使ってみましょう。~
**グローバル変数 [#a3d76242]
(define 変数名 値)
で変数を定義することができます。~
~
例1:
gosh>(define a 10)
a
gosh>(+ a 1)
11
となります。~
~
またいくつか疑問が湧きそうですのでみていきましょう。
-手続き定義の式とそっくり、ってか同じじゃない?
そうです。~
(define a b)は、文字通り「aをbで定義する」という意味です。~
ですから(define a 10)とすれば以後処理系はaを10だと思いま...
(define (inc x) (+ x 1))とすれば(inc 5)を(+ 5 1)だと思っ...
-aをdefineした直後に表示される a って何?
全ての式は評価されるということを書きました。そして対話式...
つまり(define …)の返り値が第1引数をあらわす文字だというだ...
gosh>(+ 1 (define a 10))
*** ERROR: operation + is not defined between 1 and a
~
**ローカル変数 [#h961dc82]
***defineを使う [#c1efcd04]
(define …)を使って関数の外側で定義した変数は全ての手続き...
これでは名前などが衝突して不便なので、ある手続きからだけ...
次の例を見てください。~
~
例1:
(define (inc-5 x)
(define n 5)
(+ x n)
)
これをloadして(inc-5 1)を呼び出すと確かに6になります。さ...
gosh>n
とすると
*** ERROR: unbound variable: n
となります。~
これは変数が定義されていない時に出るエラーです。~
つまり、inc-5の中でdefineされた変数nはinc-5の外からは見え...
~
***letを使う [#h7123117]
上述したdefineでもよさそうですが、通常はletを使います。~
(なぜdefineでなくletを使うのか自分は知りません。どなたか...
~
構文
(let ((変数名1 値) (変数名2 値) … (変数名n 値)) 変数を使...
上のinc-5をletを使って書き直すと次のようになります。
(define (inc-5 x)
(let ((n 5)) (+ x n))
)
定義したい変数が1つの時も(変数名 値)の周りにもう一つカッ...
~
***let* [#k2a9be16]
letでは、変数は全てまとめて定義されるため、たとえば次のよ...
(define (distance x y)
(let ((xx (* x x)) (yy (* y y)) (disdis (+ xx yy)))
(sqrt disdis)
)
)
(sqrt n)はnの平方根を返す手続きで、はじめから用意されてい...
変数が全てまとめて定義されるので、disdisを定義する時点で...
ですから実行すると
*** ERROR: unbound variable: xx
といわれてしまいます。~
~
そこで登場するのがlet*です。
(define (distance x y)
(let* ((xx (* x x)) (yy (* y y)) (disdis (+ xx yy)))
(sqrt disdis)
)
)
これは上のプログラムのletをlet*に変えただけですが、正しく...
let*では変数を前から順番に定義するため、disdisを定義する...
*内部手続きを使う [#n9e918cd]
変数名と同じく手続きも衝突するとやっかいです。~
そこで「ある手続きからしか見えない手続き」を定義してみま...
~
例1:
(define (fact-iteration n)
(define (inner n count ans)
(if (= count n)
(* n ans)
(inner n (+ count 1) (* count ans))
)
)
(inner n 1 1)
)
n!を返すプログラムの「繰り返し処理」版です。~
まずinnerという内部関数を定義して、(inner n 1 1)を返り値...
(このnはinnerのdefineよりも外側にありますから、innerの仮...
~
最初は何をやっているのか分かりにくいでしょうから順に見て...
試しに(fact-iteration 3)を評価してみます。~
#(fact-iteration 3)が呼び出されると、(inner 3 1 1)が呼ば...
#3≠1なので、(inner 3 2 (* 1 1))が呼ばれる
#3≠2なので、(inner 3 3 (*2 (* 1 1)))が呼ばれる
#3=3なので、(inner 3 3 (*2 (* 1 1)))の評価結果はans×coun...
#順に呼び出し元の手続きに値を返し、結局(fact-iteration 3)...
階乗が計算されていく様子が分かりやすいように(* count ans)...
このように、「計算結果を引数として再帰した自分にもう一度...
~
fact-iterationの外からはinnerは見えませんので、
gosh>inner
*** ERROR: unbound variable: inner
となります。~
これで手続きに一般的な名前をつけても他人と衝突する心配が...
~
例2:
(define (add x y)
(define (- a b) (+ a b))
(- x y)
)
gosh> (add 1 2)
3
gosh> (- 1 2)
-1
このように、手続きの内部で定義した手続きは外にはまったく...
*リスト演算 [#y4dcb2e6]
SchemeをはじめとるするLisp系言語が得意とされるリスト演算...
はじめは何の意味があるのか分からないかもしれませんが、気...
**cons、car、cdr [#kb6055a0]
構文
(cons 引数1 引数2)
(car ペア)
(cdr ペア)
consは引数1と引数2を要素に持つペアを作ります。~
car(カーと読む)は与えられたペアの第一要素を返します。~
cdr(クダーと読む)は与えられたペアの「第一要素を取り除い...
(これは第二要素と同じではありません。例えば「ペアのペア...
gosh> (cons 1 2)
(1 . 2)
gosh> (define x (cons 'a 'b)
x
gosh> (car x)
a
gosh> (cdr x)
b
gosh> (cdr (cons 1 (cons 2 (cons 3 4))))
(2 3 . 4)
一個だけあるドットが非常に気になるのさっ という方は次のli...
~
**list [#o2fd0f4e]
ペアのペアのペアのペアノ…などという面倒なことをしなくても...
~
構文
(list 引数1 引数2 … 引数n)
例:
gosh> (list 1 2 3 4)
(1 2 3 4)
gosh> (car (list 1 2 3 4))
1
gosh> (cdr (list 1 2 3 4))
(2 3 4)
gosh> (cdr (list 'a 'b))
(b)
はい、今回は気になるドットがありませんね。さらに最後の例...
(ちなみにこれは「要素がbだけのリスト」が返ってきており、...
とりあえずは気にしないで構いませんが、余力がある人や気に...
~
~
-append
リストはappendを使って結合することができます。
gosh> (append (list 1 2 3) (list 4 5 6))
(1 2 3 4 5 6)
gosh> (append (list 1 2 3) (cons 4 5))
(1 2 3 4 . 5)
consで作ったペアもappendできます。~
「それじゃあペアとリストは同じなの?」とか「4と5の間のド...
(さしあたりは「ドットを表示したくなかったらlistにしとけ...
~
**caar、caaar、caaaar [#n71bdb1e]
(1 2 3 4)というリストから4を取り出すことを考えてみましょ...
gosh> (define x (list 1 2 3 4))
x
gosh> (cdr x)
(2 3 4)
gosh> (cdr (cdr x))
(3 4)
gosh> (cdr (cdr (cdr x)))
(4)
gosh> (car (cdr (cdr (cdr x))))
4
gosh>
ようやく取り出すことができました。~
非常に面倒だし、最後の方はカッコが多くてわけがわかりませ...
~
そこで用意されているのが、caarやcadddr等です。~
caarはcarとcarを合わせたもの、cadddrはcarと3つのcdrを合わ...
(他にも、carとcdr合わせて4つまでの組み合わせなら全て用意...
gosh> (cadddr x)
4
gosh> (cadr (cons (cons 1 2) (cons 3 4)))
3
この組み合わせ演算は、後ろから順番にリストにかかります。~
たとえば、(caddr x)は『「"xのcdr"のcdr」のcar』となります...
~
**null?と空リスト [#qb11319e]
与えられたリストが空かどうかを判定するには、手続きnull?を...
例えば要素が一つしかないリストのcdrは空リストです。~
gosh> (define x (list 1))
x
gosh> (null? (cdr x))
#t
この手続きは、リストを扱う再帰プログラムでの終了判定にと...
(数字で言えば再帰ごとに1減らしていって「0ならば…」という...
~
また、空リストを直接表すには '() を使います。~
(これはlistを引数なし(つまり要素なし)で呼び出した(list...
gosh> (null? '())
#t
gosh> (append '(1 2 3) (list 4 5 6) '())
(1 2 3 4 5 6)
~
**listを使って何か作ってみる [#g7541e94]
今回やったcons、list、append、null?を使って、行列式を求め...
(使っている項目は上に挙げた4つにif、cond、結果を引数とし...
リスト演算の威力が少しでも伝われば幸いです。
(define (det A)
(define (scholor? matrix)
(if (and (null? (cdr matrix)) (null? (cdar matrix))) #...
)
(define (delete-elm j vector)
(define (delete-elm-inner j count top middle end)
(if (= j count)
(append top end)
(delete-elm-inner j (+ count 1) (append top (list mi...
)
)
(delete-elm-inner j 1 '() (car vector) (cdr vector))
)
(define (delete-column j matrix)
(if (null? (cdr matrix))
(list (delete-elm j (car matrix)))
(cons (delete-elm j (car matrix)) (delete-column j (c...
)
)
(define (acum-det vector smallen-matrix)
(define (acum-det-inner vector retu matrix result)
(let ((hugou (if (even? retu) -1 1)))
(cond
((null? vector) result)
((= (car vector) 0) (acum-det-inner (cdr vector) (+...
(else (acum-det-inner (cdr vector) (+ 1 retu) matri...
)
)
)
(acum-det-inner vector 1 smallen-matrix 0)
)
(if (scholor? A)
(caar A)
(acum-det (car A) (cdr A))
)
)
gosh> (det (list (list 1 0) (list 0 1)))
1
gosh> (det (list (list 2 0 0) (list 0 2 0) (list 0 0 2)))
8
引数はリストのリスト(数のリストを行ベクトルだと思い、さ...
delete-elemは与えられたベクトル(リスト)の第m番目を削除...
(行列式の計算には第1行に関する余因子展開を使っています。...
~
**map [#gb9b685f]
mapは(1引数の)関数とリストを引数にとり、「与えられたリ...
つまり、組{f(x),(1 2 3 4)}からリスト(f(1) f(2) f(3) f(4))...
~
構文
(map 関数 リスト)
例1:
gosh> (map inc '(0 1 2))
(1 2 3)
gosh> (map even? '(1 2 3 4))
(#f #t #f #t)
mapをつかって[[初心者用課題]]の素数の問題を解いてみます。~
~
例2:
(define (n-primes n)
(define (make-list n)
(define (make-list-inner n count result)
(if (= count n)
(append result (list count))
(make-list-inner n (+ count 1) (append result (list ...
)
)
(make-list-inner n 1 '())
)
(define (is-prime? n)
(define (is-prime?-inner n count)
(cond
((= n 1) #f)
((> count (sqrt n)) #t)
((integer? (/ n count)) #f)
(else (is-prime?-inner n (+ count 1)))
)
)
(is-prime?-inner n 2)
)
(define (predicate p)
(define (test x)
(if (p x) x #f)
)
test
)
(define (remove-f sequence)
(cond
((null? sequence) '())
((car sequence) (cons (car sequence) (remove-f (cdr s...
(else (remove-f (cdr sequence)))
)
)
(remove-f (map (predicate is-prime?) (make-list n)))
)
gosh> (n-primes 100)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71...
但し、内部手続きmake-listは1以上n以下の自然数を全て含むリ...
~
下から2行目のmapの第2引数が分かりにくいと思うので説明して...
(predicate is-prime?)を評価すると定義からtestになるわけで...
つまり、(predicate is-prime?)を評価すると「1変数の関数tes...
~
ここで「testはpredicateの内部手続きだから、外からは見えな...
~
答えとしては、「mapから直接は見えないが、predicateを通し...
(これによりSchemeでオブジェクト指向のようなこともできま...
~
大切なことは、プログラムが『モジュール化』されているとい...
モジュール化することにより、例えばn以下の偶数を全て返すプ...
(これはSchemeに限らずどの言語にも共通する重要な考え方で...
~
**練習問題 [#m0fd514a]
list演算はSchemeを使いこなす上で非常に重要なので、練習問...
~
問題1(易):
mapのところで作った素数を求めるプログラムは、各モジュー...
(remove-f (map (predicate is-prime?) (make-list n)))
を見ただけですぐに理解できます。
その反面、一度1からnまでのリストを作り出す必要があり、n...
これを解消するために、先にリストを作ってしまわないで、素...
ただし手続きis-prime?はすでに与えられているものとしてよ...
問題2(難):
「エラトステネスの篩」を使って2以上n以下の素数のリスト...
*lambda [#t8f3b1f3]
**関数を引数として渡す [#s198ac53]
前節でmapをやったときに、引数の中に関数が入っていました。~
Schemeでは手続きの名前を書いてやるだけで手続きを引数とし...
Cでは「関数の本体」を値渡ししてやることはできないのでアド...
きに名前が評価されて「本体をあらわす式」になり、その式が...
この「本体をあらわす式」がここでやるlambda式です。~
しかしいきなりlambdaは難しいのでとりあえずは手続きを引数...
~
例1:
(define (double f x)
(f (f x))
)
(define (inc x) (+ x 1))
gosh> (double inc 5)
7
(double f x)は(一般的な書き方で言うと)f(f(x))を返します...
簡単ですね。~
~
**lambda式 [#l159cad7]
上の例で、incを渡してやるのにわざわざ一度incという名前を...
また、例えばmapで素数を表示した時のpredicateのようにわざ...
そこで、lambdaを使って「名前のない、手続きの本体」を表現...
~
構文:
(lambda (引数並び) 手続きの内容を表す式)
この式を評価すると、「手続きの本体」が返ります。~
見てのとおり、手続きの名前はどこにもあらわれません。~
使い方になれるまで若干分かりにくいですので、例で見ていき...
~
例1:
gosh> ((lambda (x) (+ x 1)) 1)
2
(lambda・・・)が手続きの本体で、それを引数に1を設定して呼び...
引数並びに現れているxが内容を表す式のxに対応します。~
~
例2:
gosh> ((lambda () (+ 1 2)))
3
(lambda () (+ 1 2))は(+ 1 2)という無引数の関数を返します。~
外側にもう一組カッコがあるのは、「無引数の関数lambdaを呼...
(Schemeでは関数呼び出しにたとえ引数がなくてもカッコがい...
~
lambdaを使って、先ほどのプログラムは次のように実現できま...
~
例3:
gosh> (double (lambda (x) (+ x 1)) 5)
7
**lambdaとdefineの関係 [#m3637a0d]
lambdaで作り出した手続きの本体にdefineを使って名前をつけ...
~
例1:
(define inc (lambda (x) (+ x 1)))
gosh> (inc 1)
2
処理系はincを丸々(lambda…)で置き換えますから、上のように...
実はこれは、(define (inc x) (+ x 1))と全く同じです。~
(意味的に同じではなく、内部の処理も同じ。後者の書き方はl...
~
**練習問題 [#pdea1525]
*参考サイト [#tbf1f514]
このwikiではSchemeの最低限の使い方をやりました。~
Schemeの応用やScheme的考え方などについては以下のサイトを...
このwikiをマスターすれば大体は読めるハズです。
-[[独習Scheme三週間:http://www.sampou.org/scheme/t-y-sche...
前半は入門を、後半はSchemeにおけるオブジェクト指向などを...
また、最後の方でSICP(次節「参考図書」参照)の第4章にあた...
(後半むずかしめ)
-[[Scheme演習:http://www-ui.is.s.u-tokyo.ac.jp/~hara2001/...
「東京大学理学部情報科学科 2年生 の「Scheme演習」の講義用...
東大とおそるるなかれ、ほとんどはこのwikiの範囲で理解でき...
SICPの中から重要な問題を選んで演習問題としてあるので、実...
-[[Gaucheプログラミング:http://karetta.jp/book-cover/gauc...
Schemeの(このwikiより詳しく難しい)入門と、その応用。~
Schemeを使ったCGIなどの実用プログラミングはこのサイトがど...
このサイトの内容が[[プログラミングGauche:http://www.oreil...
*参考図書 [#abeb8287]
Schemeを学ぶのに参考となりそうな本をあげておきます。~
マイナーすぎて扱った本がほとんどない上に、たいていの書店...
-[[計算機プログラムの構造と解釈:http://www.amazon.co.jp/%...
Schemeを使ってプログラミングの基本的考えを学ぼうという本。~
ものっすごい高いことと日本語訳がイマイチなので、下記の英...
-[[Structure & Interpretation of Computer Programs:http:/...
上の本の原書。通称SICP。英語です。~
なんとMITのサイトで[[全文が無料で公開されている:http://mi...
-[[プログラミング言語Scheme:http://www.amazon.co.jp/%E3%8...
図書館でちらっと見ただけですがなかなかよさそうでした。~
これも[[原書:http://www.amazon.com/Scheme-Programming-Lan...
終了行:
*Scheme [#id4921b9]
#contents
*はじめに [#n0265760]
「Scheme?聞いたことねぇな」と思いのそこのあなた。~
ちょっと立ち寄ってみませんか?~
~
Schemeの基礎の基礎から初めてScheme中級者(世に存在する難...
*言語の特徴 [#fa6921f3]
Perlなどと同じスクリプト言語である。~
プログラム全体が「リスト構造」という構造を持っている。(...
Perlの代わりに使ってcgiとかも書けちゃう。~
C等より抽象度が高い。(例えば整数の実装が表に出ておらず、...
その他難しいことはよく知りません。
*処理系のインストール [#x5131cfe]
Schemeは言語仕様の小ささも売りであり、そのため多くの実装...
ここではGaucheという処理系を使うことにします。~
~
**Gaucheのインストール(Windows) [#p3d7ac56]
[[Gauche公式:http://www.shiro.dreamhost.com/scheme/gauche...
んで解凍しておしまい。~
binの中にあるgosh.exeを実行して
gosh>
と表示されたらOK~
(のちのち便利なように gauche/bin/ にPATHを通しておきまし...
~
**Gaucheのインストール(Windows以外) [#qa451660]
同じく[[Gauche公式:http://www.shiro.dreamhost.com/scheme/...
詳しくはダウンロードページに書いてあります。~
MacOS X上でも動くらしいです。~
~
*とりあえず動かそう [#t99c1dad]
**Hello,World [#n2630372]
慣例に従って"Hello,World"と表示するプログラムを書いてみま...
(display "Hello,World")
(newline)
これを適当な名前で保存し、(拡張子は.scmにするのが一般的...
gosh D:/programs/scheme/test.scm
などととしてください。~
(gosh.exeのあるフォルダにPATHが通っていることを想定して...
~
ちなみに、UNIX系OS上では、ファイルの一行目に
#!/usr/local/bin/gosh
としてtest.scmを実行可能にすることでtest.scmを直接実行で...
~
**解説 [#ya5dce36]
(display "Hello,World")
見たとおり、displayという関数(Schemeでは手続きという)を...
Schemeでは手続きを呼ぶときに
(手続き名 引数1 引数2 … 引数n)
とします。
(newline)
改行を表示します。~
~
**むずかしめの解説 [#ned8d11b]
むずかしめの解説です。おもいきり読み飛ばしてOKです。~
~
言語の特徴のところで「プログラム全体がカッコでくくられた...
しかしこのプログラム、どうみても(display "Hello,World")と...
~
実はこのプログラムは、
((lambda () (begin
(display "Hellow,world")
(newline)
)))
の略記だと考えることが出来ます。これで全体が一つのカッコ...
いきなりわけが分からなくなりましたが、今はこのコードを理...
後に関数呼び出しやlambda式をやると理解できます。~
~
*計算してみよう [#k0b233e2]
**対話式実行 [#n2b91510]
次は計算をさせてみましょう。~
ですがその前に、対話式実行というのを説明します。(たいした...
今回はgoshにファイル名を渡すのではなく、goshを単体で起動...
gosh>
と表示されるはずです。~
このウィンドウ上でプログラムを1行づつ打ち込み、実行させる...
~
**計算しよう [#c5fd49de]
goshを起動し、(+ 1 2)と打ち込んでエンターを押してください。
gosh>(+ 1 2)
3
このようになるはずです。~
~
これを見て色々思い浮かぶでしょうが、一つ一つ見ていきまし...
-(1 + 2)じゃないの?
Schemeでは「ポーランド記法」という書き方を使いますので(+ ...
簡単に言うと、
(演算子 被演算子1 被演算子2 … 被演算子n)
の形に式を書けばよいです。(詳しく知りたい方は検索してくだ...
例えば {-b+√(b^2-4ac)} / 2a は
(/ (+ -b (√ (- b^2 4ac))) 2a)
などとなるでしょう。(これは正しいScheme式の書き方ではあり...
~
-外側のカッコは何?
この式の意味は、「+という手続きを、1と2を引数にして呼び出...
(これはたとえではなく本当の話です。Schemeでは + は基本構...
Schemeでは関数呼び出しは外側にカッコをつけることになって...
+ という関数があって、+(1,2) を計算したと思えばいいでしょ...
~
-(display (+ 1 2))じゃないの?
対話式実行時は、Scheme処理系は「与えられたリストを評価し...
なのでdisplayは必要なく、答えが3になる式を与えれば3と表示...
(HelloWorldの時のようにファイル名を与えて実行するなら、d...
~
*手続きを定義しよう [#n917d5cf]
計算もできたので、今度は手続き(一般に言う関数)を定義し...
まず下のコードをエディタで打ち込んで保存してみてください。
(define (inc x)
(+ x 1)
)
~
**実行しよう [#h4823dfc]
goshを単体で起動し、今作ったプログラムをloadします。
gosh>(load "D:/programs/scheme/inc.scm")
#t
となるはずです。注意事項として、windowsではディレクトリの...
~
ちなみに#tは(load …)を「評価した結果」です。(Schemeでは...
さしあたりは「Cで関数が成功したら1が返ってくるようなもの...
~
loadが出来たら実行します。
gosh>(inc 1)
2
gosh>(inc 10)
11
などとなります。~
(カッコを忘れないことと、inc(1)ではないことに注意。)~
ちなみに言語の特徴のところでも書きましたが、
gosh>(inc 10000000000000000000000000000000000000000000000)
としてもあふれてマイナスになったりしません。
**解説 [#j054fccd]
(define (inc x)
incという手続きを、次に与えるリストで定義します。~
xは仮引数と呼ばれ、C風に書くと
int inc(int x);
のxにあたります。~
~
(+ x 1)
これは 計算しよう でやったのと同じです。~
この式を評価するとxに1を足したものになります。~
~
)
defineの終わりです。前にも書いたとおりプログラム全体が一...
~
*基本構文 [#ede79fdb]
手続きを定義して呼び出す方法をやりました。~
次はプログラムをする上で必要なifなどの構文についてです。~
~
**全ては評価される [#ia022369]
ifなどをやる前に、評価されるとはどういうことかをやります...
Schemeでは全ての式は評価されて何らかの値(または別の式)...
例えば、Cでは
if(x>=0){
printf("xはプラスです");
}
else{
printf("xはマイナスです");
}
は、x≧0の時は上側のパスを、それ以外のときは下側のパスを通...
しかし、Schemeで上と同じコードを(Schemeの文法に則って)...
「評価すると条件が真なら printf("xはプラスです") が、偽な...
~
分かりにくいですね。先にifのところを読んで例を見てからも...
~
~
**if [#x68dbbef]
構文
(if 条件 式1 式2)
条件が真なら式1が、偽なら式2が返ります。~
条件設定は一つだけで、else ifとかにあたるものは次のcondの...
~
例1:
(define (abs x)
(if (>= x 0) x (* -1 x))
)
xの絶対値を返すプログラムです。条件ももちろんポーランド記...
~
例2:
(define (abs2 x)
(* (if (>= x 0) 1 -1) x)
)
xの絶対値を返すプログラムその2です。~
これは全ての式が評価されることを理解するよい例です。~
処理系は(if (>= x 0) 1 -1)を評価し、(>= x 0)が成り立てば...
Cで書くとすれば(if …)は
(x>=0 ? 1 : -1)
となるでしょうか。~
~
例3:~
評価した結果は数字や式(例1での(* -1 x)など)だけではなく...
(define (abs3 x)
((if (>= x 0) identify abs) x)
)
但し
(define (identify x) x)
(if …)は(>= x 0)が成り立てばidentifyになるので、全体とし...
成り立たなければabsになるので(abs x)となり|x|が返ります。~
(別にはじめから(abs x)とすればよいのですが、まぁ例なので...
~
**cond [#p15b0213]
構文
(cond (条件1 式1) (条件2 式2) … (条件n 式n))
条件を1から順番に評価し、一番最初に成り立ったもの(#tを返...
~
例1:
(define (divide2 x)
(cond
((odd? x) x)
((even? (/ x 2)) (/ x 4))
(else (/ x 2))
)
)
xが奇数ならxを、それ以外で(つまりxが偶数で)x/2が偶数な...
odd?とeven?ははじめから定義されている手続きで、(odd? x)は...
~
最後の条件であるelseは意味的にはC等のelseと同じです。~
式の評価という観点から見ると、elseは常に真を返すといえま...
つまり、
(define (divide2 x)
(cond
((odd? x) x)
((even? (/ x 2)) (/ x 4))
(#t (/ x 2))
)
)
と書いても同じ結果がえられます。~
~
例2:
(define (divide2-2 x)
(/ x (cond ((odd? x) 1) ((even? (/ x 2)) 4) (#t 2)))
)
divide2と同じ結果を生じます。~
(cond …)が評価されてxを割る数が決まるわけですね。~
「式が評価されて結果が返る」ということがだいぶ分かってき...
~
**begin [#bdf5b1a7]
構文
(begin 式1 式2 … 式n)
beginは与えられた式を1から順に最後まで実行します。~
beginを評価した結果は、式nを評価した結果と同じになります。~
~
例1:
gosh>(begin (display "test") (newline) #t)
画面にtestと表示して改行し、#tを返します。~
実行すると、評価された結果も印字されるので、
gosh>(display-test)
test
#t
となります。~
begin式を評価した結果は最後の式を評価した値と同じになりま...
gosh>(if (begin (display "test") (newline) #t) 1 0)
test
1
のようになります。~
まずdisplayとnewlineを実行したのでtestと改行が印字されま...
次にbeginの評価結果は#t(真)ですからifの条件が成り立ち、...
~
**#tと#f [#ra8cc83d]
今までなんとなく使ってきた#tですが、ここで一応説明してお...
これらは評価されると、#tは真を、#fは偽をあらわします。
つまり、
gosh>(if #t 1 0)
1
gosh>(if #f 1 0)
0
ということです。~
~
**二項演算子 [#w6577155]
(演算子 引数1 引数2)
の形で、評価されると計算結果または真偽を返します。~
+、-、*、/、=、>、<、>=、<=、and、orがあります。~
(但し+、*、and、orは3つ以上の引数を取れます。)~
~
厳密にはこれらは演算子ではなく手続きなのですが、演算子だ...
~
**単項演算子 [#u7e40be8]
(演算子 引数)
の形で、評価されると真偽を返します。~
not、number?、null?などがあります。~
(これらも本当は手続きですが演算子だと思って問題ありませ...
~
Cでは !1 は偽、 !0 は真ですが、Schemeでは
gosh>(not 1)
#f
gosh>(not 0)
#f
のように両方とも偽になります。~
これは「評価して値が返ってきたものは#tとみなす」からです。
gosh>(if 1 #t #f)
#t
gosh>(if 0 #t #f)
#t
但し、もちろん#fのnotは#tですので
gosh> (not (if #t #f #t))
#t
となります。~
~
**文字と文字列 [#raa6429b]
例えば引数がnameなら名前を、ageなら年齢を表示するにはどう...
試しに次のようにしてみましょう。~
~
例1:
(define (private-information type)
(cond
((= type name) Hiroyuki)
((= type age) 1000000)
(#t Error)
)
)
このプログラムは大いに問題ありですが、ともかくloadは成功...
(なぜ成功するかはすぐ後で明らかになります。)~
~
では実行してみましょう。
gosh> (private-information name)
*** ERROR: unbound variable: name
nameが見つからないといわれてしまいました。~
これは処理系が「nameという名前の変数を探しにいったから」...
そんな変数は定義していないので当然見つかりません。~
(逆に言うと、もしnameやらageやらErrorやらがdefineされて...
~
Schemeでは変数名と文字列を区別するために、文字列は手続き(...
また、文字列と文字列が等しいかどうかを見るには = ではなく...
~
例2:
(define (private-information type)
(cond
((eq? type (quote name)) (quote Hiroyuki))
((eq? type (quote age)) 1000000)
(#t (quote Error))
)
)
loadして実行すると、
gosh> (private-information (quote age))
1000000
gosh> (private-information (quote name))
Hiroyuki
となります。正しく動きました。~
(引数として与える文字列もquoteを通さないといけないことに...
~
しかし、いちいち(quote …)と書くのは面倒なので、簡略化した...
それが「'」です。~
~
例3:
(define (private-information type)
(cond
((eq? type 'name) 'Hiroyuki)
((eq? type 'age) 1000000)
(#t 'Error)
)
)
このプログラムは例2と同じ結果を生じます。~
呼び出すときも(private-information 'age)のように ' を使う...
~
注:
gosh> (eq? (quote test) 'test)
#t
~
*ちょっと複雑なことをしてみよう [#r994155f]
では今までのことを使って多少複雑なプログラムを書いてみま...
例1:
(define (factorial n)
(if (= n 0) 1 (* n (factorial (- n 1))))
)
n!を返します。~
このように再帰呼び出しもできます。~
~
例2:
(define (pascal x y)
(cond
((or (= y 0) (= y (+ x 1))) 0)
((= x 1) 1)
(#t (+ (pascal (- x 1) (- y 1)) (pascal (- x 1) y)))
)
)
パスカルの3角形のx行y列の値を返します。~
~
*変数を使おう [#gf0f61ee]
次は変数を使ってみましょう。~
**グローバル変数 [#a3d76242]
(define 変数名 値)
で変数を定義することができます。~
~
例1:
gosh>(define a 10)
a
gosh>(+ a 1)
11
となります。~
~
またいくつか疑問が湧きそうですのでみていきましょう。
-手続き定義の式とそっくり、ってか同じじゃない?
そうです。~
(define a b)は、文字通り「aをbで定義する」という意味です。~
ですから(define a 10)とすれば以後処理系はaを10だと思いま...
(define (inc x) (+ x 1))とすれば(inc 5)を(+ 5 1)だと思っ...
-aをdefineした直後に表示される a って何?
全ての式は評価されるということを書きました。そして対話式...
つまり(define …)の返り値が第1引数をあらわす文字だというだ...
gosh>(+ 1 (define a 10))
*** ERROR: operation + is not defined between 1 and a
~
**ローカル変数 [#h961dc82]
***defineを使う [#c1efcd04]
(define …)を使って関数の外側で定義した変数は全ての手続き...
これでは名前などが衝突して不便なので、ある手続きからだけ...
次の例を見てください。~
~
例1:
(define (inc-5 x)
(define n 5)
(+ x n)
)
これをloadして(inc-5 1)を呼び出すと確かに6になります。さ...
gosh>n
とすると
*** ERROR: unbound variable: n
となります。~
これは変数が定義されていない時に出るエラーです。~
つまり、inc-5の中でdefineされた変数nはinc-5の外からは見え...
~
***letを使う [#h7123117]
上述したdefineでもよさそうですが、通常はletを使います。~
(なぜdefineでなくletを使うのか自分は知りません。どなたか...
~
構文
(let ((変数名1 値) (変数名2 値) … (変数名n 値)) 変数を使...
上のinc-5をletを使って書き直すと次のようになります。
(define (inc-5 x)
(let ((n 5)) (+ x n))
)
定義したい変数が1つの時も(変数名 値)の周りにもう一つカッ...
~
***let* [#k2a9be16]
letでは、変数は全てまとめて定義されるため、たとえば次のよ...
(define (distance x y)
(let ((xx (* x x)) (yy (* y y)) (disdis (+ xx yy)))
(sqrt disdis)
)
)
(sqrt n)はnの平方根を返す手続きで、はじめから用意されてい...
変数が全てまとめて定義されるので、disdisを定義する時点で...
ですから実行すると
*** ERROR: unbound variable: xx
といわれてしまいます。~
~
そこで登場するのがlet*です。
(define (distance x y)
(let* ((xx (* x x)) (yy (* y y)) (disdis (+ xx yy)))
(sqrt disdis)
)
)
これは上のプログラムのletをlet*に変えただけですが、正しく...
let*では変数を前から順番に定義するため、disdisを定義する...
*内部手続きを使う [#n9e918cd]
変数名と同じく手続きも衝突するとやっかいです。~
そこで「ある手続きからしか見えない手続き」を定義してみま...
~
例1:
(define (fact-iteration n)
(define (inner n count ans)
(if (= count n)
(* n ans)
(inner n (+ count 1) (* count ans))
)
)
(inner n 1 1)
)
n!を返すプログラムの「繰り返し処理」版です。~
まずinnerという内部関数を定義して、(inner n 1 1)を返り値...
(このnはinnerのdefineよりも外側にありますから、innerの仮...
~
最初は何をやっているのか分かりにくいでしょうから順に見て...
試しに(fact-iteration 3)を評価してみます。~
#(fact-iteration 3)が呼び出されると、(inner 3 1 1)が呼ば...
#3≠1なので、(inner 3 2 (* 1 1))が呼ばれる
#3≠2なので、(inner 3 3 (*2 (* 1 1)))が呼ばれる
#3=3なので、(inner 3 3 (*2 (* 1 1)))の評価結果はans×coun...
#順に呼び出し元の手続きに値を返し、結局(fact-iteration 3)...
階乗が計算されていく様子が分かりやすいように(* count ans)...
このように、「計算結果を引数として再帰した自分にもう一度...
~
fact-iterationの外からはinnerは見えませんので、
gosh>inner
*** ERROR: unbound variable: inner
となります。~
これで手続きに一般的な名前をつけても他人と衝突する心配が...
~
例2:
(define (add x y)
(define (- a b) (+ a b))
(- x y)
)
gosh> (add 1 2)
3
gosh> (- 1 2)
-1
このように、手続きの内部で定義した手続きは外にはまったく...
*リスト演算 [#y4dcb2e6]
SchemeをはじめとるするLisp系言語が得意とされるリスト演算...
はじめは何の意味があるのか分からないかもしれませんが、気...
**cons、car、cdr [#kb6055a0]
構文
(cons 引数1 引数2)
(car ペア)
(cdr ペア)
consは引数1と引数2を要素に持つペアを作ります。~
car(カーと読む)は与えられたペアの第一要素を返します。~
cdr(クダーと読む)は与えられたペアの「第一要素を取り除い...
(これは第二要素と同じではありません。例えば「ペアのペア...
gosh> (cons 1 2)
(1 . 2)
gosh> (define x (cons 'a 'b)
x
gosh> (car x)
a
gosh> (cdr x)
b
gosh> (cdr (cons 1 (cons 2 (cons 3 4))))
(2 3 . 4)
一個だけあるドットが非常に気になるのさっ という方は次のli...
~
**list [#o2fd0f4e]
ペアのペアのペアのペアノ…などという面倒なことをしなくても...
~
構文
(list 引数1 引数2 … 引数n)
例:
gosh> (list 1 2 3 4)
(1 2 3 4)
gosh> (car (list 1 2 3 4))
1
gosh> (cdr (list 1 2 3 4))
(2 3 4)
gosh> (cdr (list 'a 'b))
(b)
はい、今回は気になるドットがありませんね。さらに最後の例...
(ちなみにこれは「要素がbだけのリスト」が返ってきており、...
とりあえずは気にしないで構いませんが、余力がある人や気に...
~
~
-append
リストはappendを使って結合することができます。
gosh> (append (list 1 2 3) (list 4 5 6))
(1 2 3 4 5 6)
gosh> (append (list 1 2 3) (cons 4 5))
(1 2 3 4 . 5)
consで作ったペアもappendできます。~
「それじゃあペアとリストは同じなの?」とか「4と5の間のド...
(さしあたりは「ドットを表示したくなかったらlistにしとけ...
~
**caar、caaar、caaaar [#n71bdb1e]
(1 2 3 4)というリストから4を取り出すことを考えてみましょ...
gosh> (define x (list 1 2 3 4))
x
gosh> (cdr x)
(2 3 4)
gosh> (cdr (cdr x))
(3 4)
gosh> (cdr (cdr (cdr x)))
(4)
gosh> (car (cdr (cdr (cdr x))))
4
gosh>
ようやく取り出すことができました。~
非常に面倒だし、最後の方はカッコが多くてわけがわかりませ...
~
そこで用意されているのが、caarやcadddr等です。~
caarはcarとcarを合わせたもの、cadddrはcarと3つのcdrを合わ...
(他にも、carとcdr合わせて4つまでの組み合わせなら全て用意...
gosh> (cadddr x)
4
gosh> (cadr (cons (cons 1 2) (cons 3 4)))
3
この組み合わせ演算は、後ろから順番にリストにかかります。~
たとえば、(caddr x)は『「"xのcdr"のcdr」のcar』となります...
~
**null?と空リスト [#qb11319e]
与えられたリストが空かどうかを判定するには、手続きnull?を...
例えば要素が一つしかないリストのcdrは空リストです。~
gosh> (define x (list 1))
x
gosh> (null? (cdr x))
#t
この手続きは、リストを扱う再帰プログラムでの終了判定にと...
(数字で言えば再帰ごとに1減らしていって「0ならば…」という...
~
また、空リストを直接表すには '() を使います。~
(これはlistを引数なし(つまり要素なし)で呼び出した(list...
gosh> (null? '())
#t
gosh> (append '(1 2 3) (list 4 5 6) '())
(1 2 3 4 5 6)
~
**listを使って何か作ってみる [#g7541e94]
今回やったcons、list、append、null?を使って、行列式を求め...
(使っている項目は上に挙げた4つにif、cond、結果を引数とし...
リスト演算の威力が少しでも伝われば幸いです。
(define (det A)
(define (scholor? matrix)
(if (and (null? (cdr matrix)) (null? (cdar matrix))) #...
)
(define (delete-elm j vector)
(define (delete-elm-inner j count top middle end)
(if (= j count)
(append top end)
(delete-elm-inner j (+ count 1) (append top (list mi...
)
)
(delete-elm-inner j 1 '() (car vector) (cdr vector))
)
(define (delete-column j matrix)
(if (null? (cdr matrix))
(list (delete-elm j (car matrix)))
(cons (delete-elm j (car matrix)) (delete-column j (c...
)
)
(define (acum-det vector smallen-matrix)
(define (acum-det-inner vector retu matrix result)
(let ((hugou (if (even? retu) -1 1)))
(cond
((null? vector) result)
((= (car vector) 0) (acum-det-inner (cdr vector) (+...
(else (acum-det-inner (cdr vector) (+ 1 retu) matri...
)
)
)
(acum-det-inner vector 1 smallen-matrix 0)
)
(if (scholor? A)
(caar A)
(acum-det (car A) (cdr A))
)
)
gosh> (det (list (list 1 0) (list 0 1)))
1
gosh> (det (list (list 2 0 0) (list 0 2 0) (list 0 0 2)))
8
引数はリストのリスト(数のリストを行ベクトルだと思い、さ...
delete-elemは与えられたベクトル(リスト)の第m番目を削除...
(行列式の計算には第1行に関する余因子展開を使っています。...
~
**map [#gb9b685f]
mapは(1引数の)関数とリストを引数にとり、「与えられたリ...
つまり、組{f(x),(1 2 3 4)}からリスト(f(1) f(2) f(3) f(4))...
~
構文
(map 関数 リスト)
例1:
gosh> (map inc '(0 1 2))
(1 2 3)
gosh> (map even? '(1 2 3 4))
(#f #t #f #t)
mapをつかって[[初心者用課題]]の素数の問題を解いてみます。~
~
例2:
(define (n-primes n)
(define (make-list n)
(define (make-list-inner n count result)
(if (= count n)
(append result (list count))
(make-list-inner n (+ count 1) (append result (list ...
)
)
(make-list-inner n 1 '())
)
(define (is-prime? n)
(define (is-prime?-inner n count)
(cond
((= n 1) #f)
((> count (sqrt n)) #t)
((integer? (/ n count)) #f)
(else (is-prime?-inner n (+ count 1)))
)
)
(is-prime?-inner n 2)
)
(define (predicate p)
(define (test x)
(if (p x) x #f)
)
test
)
(define (remove-f sequence)
(cond
((null? sequence) '())
((car sequence) (cons (car sequence) (remove-f (cdr s...
(else (remove-f (cdr sequence)))
)
)
(remove-f (map (predicate is-prime?) (make-list n)))
)
gosh> (n-primes 100)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71...
但し、内部手続きmake-listは1以上n以下の自然数を全て含むリ...
~
下から2行目のmapの第2引数が分かりにくいと思うので説明して...
(predicate is-prime?)を評価すると定義からtestになるわけで...
つまり、(predicate is-prime?)を評価すると「1変数の関数tes...
~
ここで「testはpredicateの内部手続きだから、外からは見えな...
~
答えとしては、「mapから直接は見えないが、predicateを通し...
(これによりSchemeでオブジェクト指向のようなこともできま...
~
大切なことは、プログラムが『モジュール化』されているとい...
モジュール化することにより、例えばn以下の偶数を全て返すプ...
(これはSchemeに限らずどの言語にも共通する重要な考え方で...
~
**練習問題 [#m0fd514a]
list演算はSchemeを使いこなす上で非常に重要なので、練習問...
~
問題1(易):
mapのところで作った素数を求めるプログラムは、各モジュー...
(remove-f (map (predicate is-prime?) (make-list n)))
を見ただけですぐに理解できます。
その反面、一度1からnまでのリストを作り出す必要があり、n...
これを解消するために、先にリストを作ってしまわないで、素...
ただし手続きis-prime?はすでに与えられているものとしてよ...
問題2(難):
「エラトステネスの篩」を使って2以上n以下の素数のリスト...
*lambda [#t8f3b1f3]
**関数を引数として渡す [#s198ac53]
前節でmapをやったときに、引数の中に関数が入っていました。~
Schemeでは手続きの名前を書いてやるだけで手続きを引数とし...
Cでは「関数の本体」を値渡ししてやることはできないのでアド...
きに名前が評価されて「本体をあらわす式」になり、その式が...
この「本体をあらわす式」がここでやるlambda式です。~
しかしいきなりlambdaは難しいのでとりあえずは手続きを引数...
~
例1:
(define (double f x)
(f (f x))
)
(define (inc x) (+ x 1))
gosh> (double inc 5)
7
(double f x)は(一般的な書き方で言うと)f(f(x))を返します...
簡単ですね。~
~
**lambda式 [#l159cad7]
上の例で、incを渡してやるのにわざわざ一度incという名前を...
また、例えばmapで素数を表示した時のpredicateのようにわざ...
そこで、lambdaを使って「名前のない、手続きの本体」を表現...
~
構文:
(lambda (引数並び) 手続きの内容を表す式)
この式を評価すると、「手続きの本体」が返ります。~
見てのとおり、手続きの名前はどこにもあらわれません。~
使い方になれるまで若干分かりにくいですので、例で見ていき...
~
例1:
gosh> ((lambda (x) (+ x 1)) 1)
2
(lambda・・・)が手続きの本体で、それを引数に1を設定して呼び...
引数並びに現れているxが内容を表す式のxに対応します。~
~
例2:
gosh> ((lambda () (+ 1 2)))
3
(lambda () (+ 1 2))は(+ 1 2)という無引数の関数を返します。~
外側にもう一組カッコがあるのは、「無引数の関数lambdaを呼...
(Schemeでは関数呼び出しにたとえ引数がなくてもカッコがい...
~
lambdaを使って、先ほどのプログラムは次のように実現できま...
~
例3:
gosh> (double (lambda (x) (+ x 1)) 5)
7
**lambdaとdefineの関係 [#m3637a0d]
lambdaで作り出した手続きの本体にdefineを使って名前をつけ...
~
例1:
(define inc (lambda (x) (+ x 1)))
gosh> (inc 1)
2
処理系はincを丸々(lambda…)で置き換えますから、上のように...
実はこれは、(define (inc x) (+ x 1))と全く同じです。~
(意味的に同じではなく、内部の処理も同じ。後者の書き方はl...
~
**練習問題 [#pdea1525]
*参考サイト [#tbf1f514]
このwikiではSchemeの最低限の使い方をやりました。~
Schemeの応用やScheme的考え方などについては以下のサイトを...
このwikiをマスターすれば大体は読めるハズです。
-[[独習Scheme三週間:http://www.sampou.org/scheme/t-y-sche...
前半は入門を、後半はSchemeにおけるオブジェクト指向などを...
また、最後の方でSICP(次節「参考図書」参照)の第4章にあた...
(後半むずかしめ)
-[[Scheme演習:http://www-ui.is.s.u-tokyo.ac.jp/~hara2001/...
「東京大学理学部情報科学科 2年生 の「Scheme演習」の講義用...
東大とおそるるなかれ、ほとんどはこのwikiの範囲で理解でき...
SICPの中から重要な問題を選んで演習問題としてあるので、実...
-[[Gaucheプログラミング:http://karetta.jp/book-cover/gauc...
Schemeの(このwikiより詳しく難しい)入門と、その応用。~
Schemeを使ったCGIなどの実用プログラミングはこのサイトがど...
このサイトの内容が[[プログラミングGauche:http://www.oreil...
*参考図書 [#abeb8287]
Schemeを学ぶのに参考となりそうな本をあげておきます。~
マイナーすぎて扱った本がほとんどない上に、たいていの書店...
-[[計算機プログラムの構造と解釈:http://www.amazon.co.jp/%...
Schemeを使ってプログラミングの基本的考えを学ぼうという本。~
ものっすごい高いことと日本語訳がイマイチなので、下記の英...
-[[Structure & Interpretation of Computer Programs:http:/...
上の本の原書。通称SICP。英語です。~
なんとMITのサイトで[[全文が無料で公開されている:http://mi...
-[[プログラミング言語Scheme:http://www.amazon.co.jp/%E3%8...
図書館でちらっと見ただけですがなかなかよさそうでした。~
これも[[原書:http://www.amazon.com/Scheme-Programming-Lan...
ページ名: