結城浩のSICP日記 RSSフィード

2006-04-30

SchemeでCGI SchemeでCGI - 結城浩のSICP日記 を含むブックマーク

shiroさんのCGIを使うための準備を読んで「なるほど。SchemeでCGIもできるわけだよな」といまさらながら納得。Windows+ApacheでPerlのCGIが動く環境はあるので、#!行を変えれば動くかも、とやってみました。以下の通り。

#!/gauche/bin/gosh
(display "Content-type: text/plain\r\n\r\nHello, world.\r\n")

そのまま、動きました。にゃるほど。

[]Gaucheのリファレンス Gaucheのリファレンス - 結城浩のSICP日記 を含むブックマーク

GaucheリファレンスマニュアルからHTML版のリファレンスをダウンロードしてぱらぱらと見る。とにかくよくわかっていないから、ドキュメントやリファレンスはすぐ読めるようにしておこう。今日までで、R5RS, Gauche, SICPのHTML版は参照できるようになった。ふひー。

[]プロンプトを変更する方法 プロンプトを変更する方法 - 結城浩のSICP日記 を含むブックマーク

shiroさんからプロンプトを変更するおまじないをおしえていただきました。ありがとうございます。

; プロンプトを"$ "に変更する
(read-eval-print-loop #f #f #f (lambda () (display "$ ") (flush)))

次のようにすると消せるそうです。

; プロンプトを消す
(read-eval-print-loop #f #f #f (lambda () #f))

さて、教えていただいたread-eval-print-loopとは何か。

その謎を解くために、Gauche/infoにあるドキュメントをごそごそと探します。そうすると、

 -- Function: read-eval-print-loop &optional reader evaluator printer prompter

というのが見つかりました。なるほど。プロンプタを入れ替えたわけですね。

むむむ。ということは、次のようにすれば、コピー&ペーストが出来る画面が出来るんじゃないかしらん。プリンタとプロンプタを入れ替えました。

(read-eval-print-loop #f #f
    (lambda (expr) (display ";=> ")(print expr))
    (lambda () (newline)))

実行結果です。

(read-eval-print-loop #f #f
        (lambda (expr) (display ";=> ")(print expr))
        (lambda () (newline)))

123
;=> 123

456
;=> 456

'Hello
;=> Hello

(car '(123 456))
;=> 123

(exit)

すばらしい!

追記:shiroさんから「多値に注意」というアドバイスをいただきました。

多値って何?といいつつも、valuesというsubrで返せそうなことがわかったので、実験。

gosh> (values 1 2)
1
2
gosh>
(read-eval-print-loop #f #f
    (lambda (expr) (display ";=> ")(print expr))
    (lambda () (newline)))


(values 1 2)
*** ERROR: wrong number of arguments for #<closure #f> (required 1, got 2)
Stack Trace:
_______________________________________
  0  (read-eval-print-loop #f #f (lambda (expr) (display ";=> ") (print ...
        At line 5 of "(stdin)"
  1  (read-eval-print-loop #f #f (lambda (expr) (display ";=> ") (print ...
        At line 5 of "(stdin)"
  2  (read-eval-print-loop #f #f (lambda (expr) (display ";=> ") (print ...
        At line 5 of "(stdin)"
  3  (read-eval-print-loop #f #f (lambda (expr) (display ";=> ") (print ...
        At line 5 of "(stdin)"
  4  (read-eval-print-loop #f #f (lambda (expr) (display ";=> ") (print ...
        At line 5 of "(stdin)"

なるほど。プリンタが(lambda (expr) ...)と一つの仮引数で受けているのでarityが合わなくなるのだと理解。でもどうすればよいかは不明。デフォルトのプリンタの定義を読めればいいのだけれど。

…と他力本願ではよくないので、C:\Gauche\share\gauche\0.8.7\libの下でgrep printer *.scmを敢行。それっぽい定義を見つけました。多値対応のプリンタ作成。


(read-eval-print-loop #f #f
    (lambda args
        (for-each (lambda (expr) (display ";=> ")(print expr)) args))
    (lambda () (newline)))

(values 1 2)

実行結果です。

gosh>
(read-eval-print-loop #f #f
    (lambda args
        (for-each (lambda (expr) (display ";=> ")(print expr)) args))
    (lambda () (newline)))


(values 1 2)
;=> 1
;=> 2

ふふふふ。うまくいきました♪

[]3.4.2: p.179: 並列性の制御機構 3.4.2: p.179: 並列性の制御機構 - 結城浩のSICP日記 を含むブックマーク

先を読む前に直列にする方法を考えると、たとえばJavaのモニタなどは典型的。それからイベントキューを使ってシリアライズするのも基本的。Swingのイベント・ディスパッチング・スレッドに任せる方法ですな。

本では「手続き」と「それを実行するプロセス」が不可分なように書いてあるけれど、もちろんこれは場合によっては分けられる。手続きはまだ(並列性の制約のために)実行できなくても、プロセスは必ずしも待っている必要はない。他の仕事をしていてもよいはず。asyncな手続き。

[]3.4.1: p.176-p.178: 並列システムでの時 3.4.1: p.176-p.178: 並列システムでの時 - 結城浩のSICP日記 を含むブックマーク

読み読み。「逐次的に走ったかのような」というのは、いわゆる"as if serial"というやつでしょうか。ええと、原文では"as if the processes had run sequentially in some order"となっていますね。原文ではこの節全体がconcurrencyだから、並列性というよりも並行性といったほうがしっくりくるかな。まあ、でも用語の細かいところはさておき、書かれていること自体はそれほど難しい話ではなく、マルチスレッドプログラミングをしたことのある人には基本的内容。

[]1.2.1: equal? 1.2.1: equal? - 結城浩のSICP日記 を含むブックマーク

factorialを作ろうと思って目を閉じて(は嘘、テキストを閉じて)書いていたらエラー出しまくりでした。

(define (factorial n)
  (if ((== n 0) 1)
      (else (* n (factorial (- n 1))))))
(factorial 10)

↑ではunbound variable: ==というエラーになる。

(define (factorial n)
  (if ((= n 0) 1)
      (else (* n (factorial (- n 1))))))
(factorial 10)

↑ではinvalid application: (#f 1)というエラーになる。

試行錯誤の末、書いたのが↓のコード。

(define (factorial n)
  (cond ((equal? n 0) 1)
        (else (* n (factorial (- n 1))))))

(factorial 10)

ifを使う場合は↓。condとレベルが少し違う。

(define (factorial n)
  (if (equal? n 0)
      1
      (* n (factorial (- n 1)))))

(factorial 10)
  • condとifが頭で混乱していた。
  • 識別子に?を付けるのがまだ慣れない。
  • equal?よりも=のほうがよいのかな? …と思ったときに何を調べればよいのだろう。R5RSかな?

R5RSを調べてみた。=は数の比較、eq?は厳しく、equal?は甘くという感じかな。

  • R5RSでは「同じく印字されたらequal?は真」とありますね。
  • 厳密には「数やシンボルに対してはeqv?を再帰的に適用したのがequal?」と。
gosh> (eq? 'a 'a)
#t
gosh> (equal? 'a 'a)
#t
gosh> (eq? '(a) '(a))
#f
gosh> (equal? '(a) '(a))
#t
gosh> (= '(a) '(a))
*** ERROR: real number required: (a)
Stack Trace:
_______________________________________
  0  (= '(a) '(a))
        At line 6 of "(stdin)"
  1  (= '(a) '(a))
        At line 6 of "(stdin)"

factorialの場合には数だから=が妥当か。

else else - 結城浩のSICP日記 を含むブックマーク

condの最後はelseが使えるみたい。

gosh>
(define (make-rat n d)
  (cond ((< d 0) (make-rat (- n) (- d)))
        (else (cons (/ n (gcd n d)) (/ d (gcd n d))))))
make-rat
gosh>
(make-rat 2 -10)
(-1 . 5)
  • うーん、インタプリタだから実行の様子をそのまま見せられるんだけれど、そうするとプロンプトgosh>がじゃまになるなあ。→解決しました
  • 本をあちこちつまみぐいして読みたいんだけれど、日本語版と英語版をちゃんぽんにして読むから、ページよりはセクション番号のほうがよいかなあ。どこを飛ばしたか、あとでわかるようにするにはどうしたらいいだろう。

[]Webaroo: オフラインで検索 Webaroo: オフラインで検索 - 結城浩のSICP日記 を含むブックマーク

百式紹介されていたWebarooを使って、SICPのフルテキストを検索可能にしてみました。もしかしたら結構よいかも。

[]3.1.1: p.127-p.128:set!とbegin 3.1.1: p.127-p.128:set!とbegin - 結城浩のSICP日記 を含むブックマーク

大域的な変数balanceを使う例。

gosh> (define balance 100)
balance
gosh>
(define (withdraw amount)
  (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance)
      "Insufficient funds"))
withdraw
gosh> (withdraw 10)
90
gosh> (withdraw 10)
80
gosh> (withdraw 10)
70
gosh> (withdraw 10)
60
gosh> (withdraw 10)
50
gosh> (withdraw 10)
40
gosh> (withdraw 10)
30
gosh> (withdraw 10)
20
gosh> (withdraw 10)
10
gosh> (withdraw 10)
0
gosh> (withdraw 10)
"Insufficient funds"

shiroshiro2006/04/30 17:25> プロンプトが邪魔
一応、このおまじないを打ち込むとカスタマイズできます。
gosh> (read-eval-print-loop #f #f #f (lambda () (display "$ ") (flush)))
$ 'a ;; <-- ここからプロンプトが "$ " になる
a
$ 'b
b
$ 'c
c
プロンプトを消したければ
gosh> (read-eval-print-loop #f #f #f (lambda () #f))
'a
a
'b
b
'c
c

shiroshiro2006/04/30 20:49多値が生成された場合、printerは複数の引数を受け取ることに注意して下さい。SICPの範囲では多値は出てこないと思いますが。

2006-04-29

[]p.045-p.049:make-rat p.045-p.049:make-rat - 結城浩のSICP日記 を含むブックマーク

問題2.1を考えた。

  • trueがエラー(unbound variable)になったので#tを使った。
  • condじゃなくてifってあったっけ。
  • 戻り値がないと#undefって表示されるので、print-ratの最後に#tを戻すようにした。
  • 最初(- n)を-nと書いてエラーになった(unbound variable)。
gosh>
(define (numer x) (car x))
numer
gosh>
(define (denom x) (cdr x))
denom
gosh>
(define (print-rat x)
  (newline)
  (display (numer x))
  (display "/")
  (display (denom x))
  (newline)
  #t)
print-rat
gosh>
(define (make-rat n d)
  (cond ((< d 0) (make-rat (- n) (- d)))
        (#t (cons (/ n (gcd n d)) (/ d (gcd n d))))))
make-rat
gosh>
(print-rat (make-rat 6 12))

1/2
#t
gosh>
(print-rat (make-rat -6 12))

-1/2
#t
gosh>
(print-rat (make-rat 6 -12))

-1/2
#t
gosh>
(print-rat (make-rat -6 -12))

1/2

[]p.007:sum-of-squares p.007:sum-of-squares - 結城浩のSICP日記 を含むブックマーク

gosh> (define (sum-of-squares x y) (+ (* x x) (* y y)))
sum-of-squares
gosh> (sum-of-squares 3 4)
25
gosh> (sqrt 2)
1.4142135623730951
gosh> (define (distance-from-zero x y)
  (sqrt (+ (* x x) (* y y))))
distance-from-zero
gosh> (distance-from-zero 3 4)
5.0

squareを定義してからやる。

gosh> (define (square x) (* x x))
square
gosh> (define (distance-from-zero x y)
  (sqrt (+ (square x) (square y))))
distance-from-zero
gosh> (distance-from-zero 3 4)
5.0

[]p.005:評価の規則は再帰p.005:評価の規則は再帰的 - 結城浩のSICP日記 を含むブックマーク

gosh> (* 5 (* 4 (* 3 (* 2 (* 1 1)))))
120

[]ドキュメント探し(1) ドキュメント探し(1) - 結城浩のSICP日記 を含むブックマーク

疑問: perldocやriみたいなコマンドラインで探せるドキュメントはないのだろうか…。

[]p.004:define p.004:define - 結城浩のSICP日記 を含むブックマーク

名前を付けることは抽象化の一手法ですね。

gosh> (define hello "Hello")
hello
gosh> hello
"Hello"

処理系探し(3) - DrScheme 処理系探し(3) - DrScheme - 結城浩のSICP日記 を含むブックマーク

Welcome to DrScheme, version 301.
Language: Standard (R5RS).
> (+ 1 1)
2
> (+ 3 4)
7
>
  • よしよし動いた。

処理系探し(2) - MIT/GNU Scheme 処理系探し(2) - MIT/GNU Scheme - 結城浩のSICP日記 を含むブックマーク

[]処理系探し(1) - Gauche 処理系探し(1) - Gauche - 結城浩のSICP日記 を含むブックマーク

まずはScheme処理系探しですね。何はともあれPractical Schemeに行きますか。

まずは、Gaucheの「コンパイルWindowsバイナリ (実験中)」というのを試してみようかな。

あとであちこちに書かれているScheme勉強家が何を使っているかを調査することにしよう。

>gosh
gosh> :q
:q
gosh> (quit)
*** ERROR: unbound variable: quit
Stack Trace:
_______________________________________
  0  (quit)
        At line 2 of "(stdin)"
  1  (quit)
        At line 2 of "(stdin)"
gosh> q
*** ERROR: unbound variable: q
Stack Trace:
_______________________________________
  0  q

  1  q

gosh> quit
*** ERROR: unbound variable: quit
Stack Trace:
_______________________________________
  0  quit

  1  quit

gosh> (exit)
>
gosh> (define (fib n)
  (cond ((= n 0) 0)
        ((= n 1) 1)
        (else (+ (fib (- n 1))
                 (fib (- n 2))))))
fib
gosh> (fib 10)
55
gosh> (exit)

[]SICPの勉強をはじめます SICPの勉強をはじめます - 結城浩のSICP日記 を含むブックマーク

こんにちは、結城浩です。

id:rubycoさんの日記を100日書いてみて、「はてな」を利用して勉強をすることがとても楽しいことがわかりました。

そこで味をしめた結城は今度はSICPに挑戦してみようと思いました。

ということで、はてなグループではありますが、実質的には結城日記代わりに使うつもりです。グループに参加したいというお申し込みはお受けできませんのでご了承ください。

もちろん、日記へのコメントツッコミトラックバックなどは大歓迎です。