<?xml version="1.0" encoding="utf-8" ?>
<rss version='2.0'><channel><title>Gaucheプログラミング(立読み版)</title
><link>http://karetta.jp/book/gauche-hacks</link
><description></description
><lastBuildDate>Fri, 23 May 2008 12:56:19 +0900</lastBuildDate
><item><title>すべて式である</title
><link>http://karetta.jp/book-node/gauche-hacks/008067</link
><pubDate>Tue, 15 May 2007 11:24:02 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;Scheme では計算というのは式の値を求めることです。
&lt;/p
&gt;&lt;p&gt;Scheme の式は以下のように表現されているものです。
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;ひとつ塊(シンボルおよびリテラル)として表現されるもの(この構文のこと
をアトムということがあります)
&lt;/li
&gt;&lt;li&gt;複数の式を括弧(&lt;strong&gt;(&lt;/strong
&gt;と&lt;strong&gt;)&lt;/strong
&gt;)で囲って表現されるもの(この構文のこ
とをリストということがあります)
&lt;/li
&gt;&lt;/ol
&gt;&lt;ul&gt;&lt;li&gt;1. により、&lt;strong&gt;map&lt;/strong
&gt;、&lt;strong&gt;string=?&lt;/strong
&gt;、&lt;strong&gt;*&lt;/strong
&gt;、&lt;strong&gt;-5&lt;/strong
&gt;、&lt;strong&gt;3.14&lt;/strong
&gt;、
&lt;strong&gt;&amp;quot;The Wizard Book&amp;quot;&lt;/strong
&gt;、&lt;strong&gt;#\a&lt;/strong
&gt; などはどれも式です。
&lt;/li
&gt;&lt;li&gt;2. により、&lt;strong&gt;(- 1 (* 2 3))&lt;/strong
&gt;、
&lt;strong&gt;(string-&amp;gt;list (string-map char-upcase &amp;quot;wikiwiki&amp;quot;))&lt;/strong
&gt;
なども式です。
&lt;/li
&gt;&lt;/ul
&gt;&lt;p&gt;式が表す値に名前をつけることを定義といいます。Schemeでは式が表す値に
名前を付けることによって、複雑な構成の式をひとつのものとして抽象化して
表現することができます。
&lt;/p
&gt;&lt;p&gt;計算機に計算をさせるには、対話型のインタプリタを使うのが簡単です。
式が表す値はその式を評価(&lt;em&gt;eval&lt;/em
&gt;)すること、すなち計算することによって
得られます。式が表す値を確認するために対話型のインタプリタを起動して計
算をさせてみましょう。
&lt;/p
&gt;&lt;p&gt;コマンドラインで&lt;strong&gt;gosh&lt;/strong
&gt;とタイプするとインタプリタが起動します。
&lt;/p
&gt;&lt;pre&gt; $ &lt;strong&gt;gosh&lt;/strong
&gt;
 &amp;gt; _
&lt;/pre
&gt;&lt;p&gt;起動するとインタプリタは&lt;em&gt;&amp;gt; &lt;/em
&gt;という入力促進記号(プロンプト)を出力して、
ユーザからの入力待ち状態になります。ここで式をタイプして最後にリターン
キーを押すとインタプリタはユーザがタイプした式を読み(&lt;em&gt;read&lt;/em
&gt;)、
それを評価(&lt;em&gt;eval&lt;/em
&gt;)し、結果を印字(&lt;em&gt;print&lt;/em
&gt;)し、再びプロンプトを出し
て入力待ち状態になります。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; &lt;strong&gt;1&lt;/strong
&gt;
 &lt;em&gt;1&lt;/em
&gt;
 gosh&amp;gt; _
&lt;/pre
&gt;&lt;p&gt;他にも試してみましょう。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; &lt;strong&gt;&amp;quot;hello&amp;quot;&lt;/strong
&gt;
 &amp;quot;hello&amp;quot;
 gosh&amp;gt; &lt;strong&gt;(+ 1 3)&lt;/strong
&gt;
 &lt;em&gt;4&lt;/em
&gt;
 gosh&amp;gt; &lt;strong&gt;(let1 abs (lambda (n)&lt;/strong
&gt;
                      &lt;strong&gt;(if (&amp;lt;= 0 n)&lt;/strong
&gt;
                          &lt;strong&gt;n&lt;/strong
&gt;
                          &lt;strong&gt;(- n)))&lt;/strong
&gt;
            &lt;strong&gt;(abs -1))&lt;/strong
&gt;
 &lt;em&gt;1&lt;/em
&gt;
&lt;/pre
&gt;&lt;/node-set
&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-yydqit&#39;&gt;すべて式とはどういうことか
&lt;/h2
&gt;&lt;p&gt;たとえば、Scheme では if は式なので&lt;strong&gt;値を返します&lt;/strong
&gt;。
&lt;/p
&gt;&lt;p&gt;a に「bの絶対値」を加算するプログラムを考えて見ましょう。
絶対値を求める手続き abs を使わないとすると、
&lt;/p
&gt;&lt;pre&gt; (define (a-plus-abs-b a b)
   &lt;strong&gt;(if (&amp;gt;= b 0)&lt;/strong
&gt;
        &lt;strong&gt;(+ a b)&lt;/strong
&gt;
        &lt;strong&gt;(- a b))&lt;/strong
&gt;) 
&lt;/pre
&gt;&lt;p&gt;と書けます。これは if 式全体の値が手続き適用したときの値として返されま
す。また、if 式は値を返すので、その値そのものとみなすことができるので、
&lt;/p
&gt;&lt;pre&gt; (define (a-plus-abs-b a b)
   (+ a &lt;strong&gt;(if (&amp;gt;= b 0)&lt;/strong
&gt;
            &lt;strong&gt;b&lt;/strong
&gt;
            &lt;strong&gt;(- b))&lt;/strong
&gt; ))
&lt;/pre
&gt;&lt;p&gt;のように b の値の正負によって，b か -b を返すように書いています。
さらに Scheme では手続きそのものも値ですので、手続きを値として返す
式を書けます。以下の定義で if 式は b が正の値なら + 手続きを、
負の値なら - 手続きを返しています。
&lt;/p
&gt;&lt;pre&gt; (define (a-plus-abs-b a b)
   (&lt;strong&gt;(if (&amp;gt; b 0)&lt;/strong
&gt;
          &lt;strong&gt;+&lt;/strong
&gt;
          &lt;strong&gt;-)&lt;/strong
&gt; a b))
&lt;/pre
&gt;&lt;p&gt;例えば a が 23、b が 16 ならば、
&lt;/p
&gt;&lt;pre&gt;((if (&amp;gt; 16 0) + -) 23 16)
↓
(+ 23 16)
↓
39
&lt;/pre
&gt;&lt;p&gt;aが12、bが-74ならば、
&lt;/p
&gt;&lt;pre&gt;((if (&amp;gt; -74 0) + -) 12 -74)
↓
(- 12 -74)
↓
86
&lt;/pre
&gt;&lt;p&gt;と計算できます。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-seqnh1&#39;&gt;代入がなくても計算はできる
&lt;/h2
&gt;&lt;p&gt;Schemeにおいて計算する対象はすべて式であるなら、代入文はないということ
になります。代入文がなくてプログラムが書けるのでしょうか。
&lt;/p
&gt;&lt;p&gt;書けます。
&lt;/p
&gt;&lt;p&gt;1 から指定した上限 n までの整数の和をもとめる計算を考えてみましょう。
&lt;/p
&gt;&lt;pre&gt; 1 + 2 + ... + (n-1) + n
&lt;/pre
&gt;&lt;p&gt;このプログラムを書くのに代入文が欲しいですか。
こんな風に計算させようとしていますか。
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;s に 0 を代入
&lt;/li
&gt;&lt;li&gt;i に 1 を代入
&lt;/li
&gt;&lt;li&gt;i が n を超えない間 4 〜 5 を実行、n を超えたら 6 へ
&lt;/li
&gt;&lt;li&gt;s + i を計算してその結果を s に代入
&lt;/li
&gt;&lt;li&gt;i + 1 を計算してその結果を i に代入
&lt;/li
&gt;&lt;li&gt;s の値を印字
&lt;/li
&gt;&lt;/ol
&gt;&lt;p&gt;なるほど。これで計算できるような気がします。でもこれで本当に欲しい
ものが手にはいるかどうかは、よくよく読んで検討しないと分りませんね。
&lt;/p
&gt;&lt;p&gt;こんな風に欲しいものを「どのように」得るかの手順を考えるのではなくてもっと、
単純に考えることもできます。これを行う関数を sum としましょう。
こんな風に考えます。
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;n が  1 のとき、&lt;em&gt;(sum n)&lt;/em
&gt; の値は 1
&lt;/li
&gt;&lt;li&gt;n が  1 より大きいとき、&lt;em&gt;(sum n)&lt;/em
&gt; の値は、ひとつ手前 (n-1) までの和
(すなわち &lt;em&gt;(sum (- n 1))&lt;/em
&gt;) と n との和
&lt;/li
&gt;&lt;/ol
&gt;&lt;p&gt;これを書くと、
&lt;/p
&gt;&lt;pre&gt;  (define (sum n)
    (if (= n 1)
        1
        (+ (sum (- n 1)) n)))
&lt;/pre
&gt;&lt;p&gt;欲しいものが「何か」を書いただけです。でも欲しいものはちゃんと書けてい
るということは分りますよね。どこにも代入は必要ありませんでしたね。
&lt;/p
&gt;&lt;p&gt;ではなぜ、最初は代入が必要だと思ったのでしょう。
「プログラムは計算の&lt;strong&gt;手順&lt;/strong
&gt;を書いたもの」という先入観がどこかに
ありませんでしたか。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>「Lisp脳」の謎に迫る - Schemeプログラマの発想</title
><link>http://karetta.jp/book-node/gauche-hacks/023107</link
><pubDate>Fri, 23 May 2008 12:56:19 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;blockquote&gt;&lt;h2 id=&#39;H-1uofzn5&#39;&gt;この原稿の最新版について
&lt;/h2
&gt;&lt;p&gt;この原稿に加筆した最新版が書籍「プログラミングGauche」に収録されています。
引用や紹介をされる方はなるべく書籍収録版を参照してください。
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;他の言語のプログラマがSchemeプログラムを書くとき、
どうしても発想が&lt;strong&gt;手続き的&lt;/strong
&gt;(procedural)になりがちです。
&lt;/p
&gt;&lt;p&gt;LispプログラマやSchemeプログラマの発想は&lt;strong&gt;手続き的な発想とはどうも違うらしい&lt;/strong
&gt;、
ということは分かるのですが、具体的に何が違うのでしょうか?
&lt;/p
&gt;&lt;p&gt;ここではこの謎に迫ってみましょう。
&lt;/p
&gt;&lt;h2 id=&#39;H-41hvtu&#39;&gt;実例
&lt;/h2
&gt;&lt;p&gt;例えばこんな例題があります。
&lt;/p
&gt;&lt;blockquote&gt;&lt;p&gt;1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。
&lt;/p
&gt;&lt;p&gt;&lt;em&gt;どうしてプログラマに・・・プログラムが書けないのか?&lt;/em
&gt;&lt;br /&gt;
&lt;em&gt;(原題: Why Can&amp;#39;t Programmers.. Program?)&lt;/em
&gt;&lt;br /&gt;
Jeff Atwood / 青木靖 訳&lt;br /&gt;
&lt;a href=&#39;http://www.aoky.net/articles/jeff_atwood/why_cant_programmers_program.htm&#39;&gt;http://www.aoky.net/articles/jeff_atwood/why_cant_programmers_program.htm&lt;/a
&gt;
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;「プリントすること」と書かれているので、
手続き的プログラマこんな手順を考えるのではないでしょうか。
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;1から100までの数を繰り返し処理で処理する
&lt;/li
&gt;&lt;li&gt;繰り返し毎にその回の数を判定して条件分岐する
&lt;/li
&gt;&lt;li&gt;15で割りれるなら&amp;quot;FizzBuzz&amp;quot;を、5で割り切れるなら&amp;quot;Buzz&amp;quot;を、3で割り切れるなら&amp;quot;Fizz&amp;quot;を、さもなければ数のまま印字する
&lt;/li
&gt;&lt;/ol
&gt;&lt;p&gt;これを考えていくと、
&lt;/p
&gt;&lt;blockquote&gt;&lt;ul&gt;&lt;li&gt;「プリントってことは入出力を伴うよなあ」
&lt;/li
&gt;&lt;li&gt;「print手続きを毎回呼ぶんだよなあ」
&lt;/li
&gt;&lt;li&gt;「繰り返し処理ならforだけどSchemeにはないからdoかなあ」
&lt;/li
&gt;&lt;li&gt;「回数ごとに1づつ加算するにはGaucheだとinc!が使えるらしい」
&lt;/li
&gt;&lt;li&gt;「Schemeでは条件分岐にはcondが使えるのか」
&lt;/li
&gt;&lt;li&gt;「...」
&lt;/li
&gt;&lt;/ul
&gt;&lt;/blockquote
&gt;&lt;p&gt;などと連想が広がっていくことでしょう。
&lt;/p
&gt;&lt;p&gt;この発想を素直に書いたのが次のコードです。
&lt;/p
&gt;&lt;pre&gt;(do ((x 1 (inc! x)))
    ((&amp;gt;= x 100) x)
  (cond ((= (modulo x 15) 0)
         (print &amp;quot;FizzBuzz&amp;quot;))
        ((= (modulo x 5) 0)
         (print &amp;quot;Buzz&amp;quot;))
        ((= (modulo x 3) 0)
         (print &amp;quot;Fizz&amp;quot;))
        (else
         (print (x-&amp;gt;string x)))))
&lt;/pre
&gt;&lt;p&gt;実行してみると1から100までの数を印字改行し、
3や5の倍数には仕様どおりに&amp;quot;Fizz&amp;quot; &amp;quot;Buzz&amp;quot; &amp;quot;FizzBuzz&amp;quot;が印字改行されています。
めでたしめでたし。
&lt;/p
&gt;&lt;p&gt;Schemeプロラマがこんな風に考えないのだとしたら、
いったい何が違うのでしょうか?
&lt;/p
&gt;&lt;h2 id=&#39;H-p7y6pm&#39;&gt;Schemeプログラマの発想
&lt;/h2
&gt;&lt;p&gt;Schemeプログラマはたとえばこんな風に発想します。
&lt;/p
&gt;&lt;blockquote&gt;&lt;p&gt;「とりあえず1から100までのリストを作れば良さそうだ」
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;と考えて、まずこんな風に書いてみます。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (use srfi-1)
 &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
 gosh&amp;gt; (iota 100 1)
 &lt;em&gt;(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20&lt;/em
&gt;
 &lt;em&gt;21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40&lt;/em
&gt;
 &lt;em&gt;41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60&lt;/em
&gt;
 &lt;em&gt;61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80&lt;/em
&gt;
 &lt;em&gt;81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;つぎに、
&lt;/p
&gt;&lt;blockquote&gt;&lt;p&gt;「mapを使ってこのリストを加工して、別のリストを返せばいいじゃん」
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;と考えます。
&lt;/p
&gt;&lt;pre&gt; (map
  &lt;strong&gt;何らかの加工&lt;/strong
&gt;
  (iota 100 1))
&lt;/pre
&gt;&lt;p&gt;map式では元のリストの要素すべてに「何らかの加工」を行うと新たなリストが返って来ます。
あとはこの「何らかの加工」の中身を考えれば良さそうです。
&lt;/p
&gt;&lt;p&gt;「何らかの加工」の中身ですが、
Schemeプログラマは「プリントせよ」という文を読んだからといって素直に「印字せよ」とは考えません。
(このへん若干誇張されている恐れあり)
&lt;/p
&gt;&lt;blockquote&gt;&lt;p&gt;「その要素を置き換えておけば、出力は後からどうにでもなるじゃないか」
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;と考えて、
&lt;/p
&gt;&lt;pre&gt;  (cond ((= (modulo x 15) 0)
         &lt;strong&gt;(print &amp;quot;FizzBuzz&amp;quot;)&lt;/strong
&gt;)
        ((= (modulo x 5) 0)
         &lt;strong&gt;(print &amp;quot;Buzz&amp;quot;)&lt;/strong
&gt;)
        ((= (modulo x 3) 0)
         &lt;strong&gt;(print &amp;quot;Fizz&amp;quot;)&lt;/strong
&gt;)
        (else
         &lt;strong&gt;(print (x-&amp;gt;string x))&lt;/strong
&gt;))
&lt;/pre
&gt;&lt;p&gt;とprint式を書く代わりに、
&lt;/p
&gt;&lt;pre&gt;  (cond ((= (modulo x 15) 0)
         &lt;strong&gt;&amp;quot;FizzBuzz&amp;quot;&lt;/strong
&gt;)
        ((= (modulo x 5) 0)
         &lt;strong&gt;&amp;quot;Buzz&amp;quot;&lt;/strong
&gt;)
        ((= (modulo x 3) 0)
         &lt;strong&gt;&amp;quot;Fizz&amp;quot;&lt;/strong
&gt;)
        (else
         &lt;strong&gt;x&lt;/strong
&gt;))
&lt;/pre
&gt;&lt;p&gt;と値を返すだけで満足してしまいます。
&lt;/p
&gt;&lt;pre&gt; (map
  (lambda (x) (cond ((= (modulo x 15) 0) &amp;quot;FizzBuzz&amp;quot;)
                    ((= (modulo x 5) 0) &amp;quot;Buzz&amp;quot;)
                    ((= (modulo x 3) 0) &amp;quot;Fizz&amp;quot;)
                    (else x)))
  (iota 100 1))
&lt;/pre
&gt;&lt;p&gt;実行してみると確かに問題の仕様どおりのリストが返るので、
&lt;/p
&gt;&lt;blockquote&gt;&lt;p&gt;「プリントせよ? 対話型インタプリタで実行すれば結果のリストはインタプリタが印字してくれるじゃないか」
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;とも考えるのですが、
&lt;/p
&gt;&lt;blockquote&gt;&lt;p&gt;「まあ結果のリストをprintしてみても良いか」
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;とも考えて全体をprint式で包んでみたりもするかも知れません。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (print
        (map
         (lambda (x) (cond ((= (modulo x 15) 0) &amp;quot;FizzBuzz&amp;quot;)
                          ((= (modulo x 5) 0) &amp;quot;Buzz&amp;quot;)
                          ((= (modulo x 3) 0) &amp;quot;Fizz&amp;quot;)
                          (else x)))
         (iota 100 1)))
 &lt;em&gt;(1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19&lt;/em
&gt;
 &lt;em&gt;Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz&lt;/em
&gt;
 &lt;em&gt;37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz&lt;/em
&gt;
 &lt;em&gt;Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71&lt;/em
&gt;
 &lt;em&gt;Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89&lt;/em
&gt;
 &lt;em&gt;FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz)&lt;/em
&gt;
 &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;もちろん要素ごとに印字して改行したければ、
&lt;/p
&gt;&lt;pre&gt;(for-each print
          (map
           (lambda (x) (cond ((= (modulo x 15) 0) &amp;quot;FizzBuzz&amp;quot;)
                             ((= (modulo x 5) 0) &amp;quot;Buzz&amp;quot;)
                             ((= (modulo x 3) 0) &amp;quot;Fizz&amp;quot;)
                             (else x)))
           (iota 100 1)))
&lt;/pre
&gt;&lt;p&gt;for-eachを使ってリストの要素それぞれに対してprintを適用しても良いでしょう。
&lt;/p
&gt;&lt;h2 id=&#39;H-u34pei&#39;&gt;結局何が違うのか?
&lt;/h2
&gt;&lt;p&gt;手続き的な発想では、&lt;strong&gt;毎回特殊な処理を行い&lt;/strong
&gt;それを繰り返すという発想でプログラミングしていました。
&lt;/p
&gt;&lt;p&gt;Schemeプログラマはそうは考えません。
&lt;/p
&gt;&lt;p&gt;&lt;strong&gt;データからデータへの変換を考えれば良く、出力は後からどうにでもなる&lt;/strong
&gt;、
と考えています。
&lt;/p
&gt;&lt;p&gt;別の側面から見てみましょう。
&lt;/p
&gt;&lt;pre&gt; (map
  &lt;strong&gt;何らかの加工&lt;/strong
&gt;
  (iota 100 1))
&lt;/pre
&gt;&lt;p&gt;データからデータへの変換を行っている「何らかの加工」の部分は、
例えば問題の条件が増えたとしても、いくらでも差し替えが可能です。
&lt;/p
&gt;&lt;p&gt;これを、「Schemeプログラマの発想はモジュール性が高い」と理解しても良いのですが、
Schemeプログラマは最初から「モジュール性が高くなるように設計しよう」と意図して発想しているのではないのです。
&lt;/p
&gt;&lt;p&gt;むしろ「データからデータへの変換をしておけばいいじゃん」とだけ考えていたら&lt;strong&gt;結果としてモジュール性が高くなってしまった&lt;/strong
&gt;と理解するべきです。
&lt;/p
&gt;&lt;p&gt;Schemeプログラマの発想の一端がお分かりいただけたでしょうか? 
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>リスト要素の破壊的変更</title
><link>http://karetta.jp/book-node/gauche-hacks/007242</link
><pubDate>Tue, 08 May 2007 15:02:30 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;空リスト以外のリストは対(pair)で構成されています。
対の要素の値はcarとcdrで参照することができます。
&lt;/p
&gt;&lt;pre&gt;  gosh&amp;gt; (define lst (cons &amp;quot;abc&amp;quot; (cons 3 &amp;#39;())))
  &lt;em&gt;lst&lt;/em
&gt;
  gosh&amp;gt; lst
  &lt;em&gt;(&amp;quot;abc&amp;quot; 3)&lt;/em
&gt;
  gosh&amp;gt; (car lst)
  &lt;em&gt;&amp;quot;abc&amp;quot;&lt;/em
&gt;
  gosh&amp;gt; (cdr lst)
  &lt;em&gt;(3)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;&lt;img src=&#39;http://karetta.jp/images/3/cons-abc-3.png&#39; /&gt;
&lt;/p
&gt;&lt;p&gt;carとcdrで参照できる要素の値を破壊的に変更する手続きが&lt;strong&gt;set-car!&lt;/strong
&gt;と&lt;strong&gt;set-cdr!&lt;/strong
&gt;です。
&lt;/p
&gt;&lt;pre&gt;  gosh&amp;gt; (set-car! lst #\r)
  &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
  gosh&amp;gt; lst
  &lt;em&gt;(#\r 3)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;&lt;img src=&#39;http://karetta.jp/images/3/set-car.png&#39; /&gt;
&lt;/p
&gt;&lt;p&gt;まず対のcar要素を文字#\rに置き換えました。
&lt;/p
&gt;&lt;pre&gt;  gosh&amp;gt; (set-cdr! lst &amp;#39;(a b c))
  &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
  gosh&amp;gt; lst
  &lt;em&gt;(#\r a b c)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;&lt;img src=&#39;http://karetta.jp/images/3/set-crd.png&#39; /&gt;
&lt;/p
&gt;&lt;p&gt;つぎに対のcdr要素をリスト(a b c)に置き換えました。
&lt;/p
&gt;&lt;p&gt;面白い使い方としては対の要素に自分自身を代入することもできます。
&lt;/p
&gt;&lt;pre&gt;  gosh&amp;gt; (define lst (list 2 &amp;quot;hello&amp;quot; 3 a #\b))
  &lt;em&gt;lst&lt;/em
&gt;
  gosh&amp;gt; lst
  &lt;em&gt;(2 &amp;quot;hello&amp;quot; 3 a #\b)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;&lt;img src=&#39;http://karetta.jp/images/3/lst.png&#39; /&gt;
&lt;/p
&gt;&lt;pre&gt;  gosh&amp;gt; (set-car! lst lst)
  &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
  gosh&amp;gt; lst
  &lt;em&gt;#0=(#0# &amp;quot;hello&amp;quot; 3 a #\b)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;ここではリスト(2 &amp;quot;hello&amp;quot; 3 a #\b)のcarにこのリスト自身を代入しました。
&lt;/p
&gt;&lt;p&gt;&lt;img src=&#39;http://karetta.jp/images/3/set-car-lst-lst.png&#39; /&gt;
&lt;/p
&gt;&lt;p&gt;carを取り出してみると
&lt;/p
&gt;&lt;pre&gt;  gosh&amp;gt; (car lst)
  &lt;em&gt;#0=(#0# &amp;quot;hello&amp;quot; 3 a #\b)&lt;/em
&gt;
  gosh&amp;gt; (car (car lst))
  &lt;em&gt;#0=(#0# &amp;quot;hello&amp;quot; 3 a #\b)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;同じ値が返されます。ここで、
&lt;/p
&gt;&lt;pre&gt;  #数=値
&lt;/pre
&gt;&lt;p&gt;は値に&lt;em&gt;#数&lt;/em
&gt;という名前をつけ、
&lt;/p
&gt;&lt;pre&gt;  #数#
&lt;/pre
&gt;&lt;p&gt;で、#数の名前が指している値を参照するということになっています。この機能を使うと循環したリストを作成できます。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;node-set&gt;&lt;blockquote&gt;&lt;h2 id=&#39;H-1x41d1e&#39;&gt;コラム: リストリテラルは破壊的に変更してはいけない
&lt;/h2
&gt;&lt;p&gt;Schemeで&lt;strong&gt;リストリテラルを破壊的に変更するのはエラー&lt;/strong
&gt;(誤り)です。
破壊的に変更することが可能なのはconsやlistで構築されたリストだけです。
&lt;/p
&gt;&lt;p&gt;例えばリストリテラルに束縛されたlstを定義したとします。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (define lst &amp;#39;(1 2 3))
 &lt;em&gt;lst&lt;/em
&gt;
 gosh&amp;gt; lst
 &lt;em&gt;(1 2 3)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;set-car!でlstの要素を破壊的に変更してみます。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (set-car! lst 100)
 &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
 gosh&amp;gt; lst
 &lt;em&gt;(100 2 3)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;このコードは現状のGaucheバージョン0.8.10で動作してしまいますが、
Schemeの仕様上はエラーです。
&lt;/p
&gt;&lt;blockquote&gt;&lt;h2 id=&#39;H-15x4kfr&#39;&gt;コラム: エラーのときのScheme処理系の振舞い
&lt;/h2
&gt;&lt;p&gt;Schemeの現在の仕様であるR5RSでは、
リスト要素が破壊的に変更されたときをはじめ、
エラーのとき何が起こるべきかについて規定していません。
このためエラーでの動作は処理系ごとに異なります。
&lt;/p
&gt;&lt;p&gt;Schemeの次期仕様であるR6RSではエラー時に何が起きるべきか、
もっと厳密に定められる予定です。
&lt;/p
&gt;&lt;/blockquote
&gt;&lt;p&gt;もしリストリテラルを変更する必要があるなら、
&lt;strong&gt;list-copy&lt;/strong
&gt;手続きでコピーを作成し、コピーの方を破壊的に変更してください。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (define lst &amp;#39;(1 2 3))
 &lt;em&gt;lst&lt;/em
&gt;
 gosh&amp;gt; lst
 &lt;em&gt;(1 2 3)&lt;/em
&gt;
 gosh&amp;gt; (define lst-cp (&lt;strong&gt;list-copy&lt;/strong
&gt; lst))
 &lt;em&gt;lst-cp&lt;/em
&gt;
 gosh&amp;gt; lst-cp
 &lt;em&gt;(1 2 3)&lt;/em
&gt;
 gosh&amp;gt; (set-car! lst-cp 100)
 &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
 gosh&amp;gt; lst
 &lt;em&gt;(1 2 3)&lt;/em
&gt;
 gosh&amp;gt; lst-cp
 &lt;em&gt;(100 2 3)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;リテラル値、つまり定数値を破壊的に変更してはならない原則は、
例えば文字列リテラルについても同様です。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (string-set! &amp;quot;abc&amp;quot; 0 #\x)
 &lt;em&gt;*** ERROR: attempted to modify an immutable string: &amp;quot;abc&amp;quot;&lt;/em
&gt;
 &lt;em&gt;Stack Trace:&lt;/em
&gt;
 &lt;em&gt;_______________________________________&lt;/em
&gt;
&lt;/pre
&gt;&lt;/blockquote
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>執筆時点でのGaucheバージョン</title
><link>http://karetta.jp/book-node/gauche-hacks/021627</link
><pubDate>Wed, 02 May 2007 11:22:20 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;本書執筆時点(2007年5月)でのGaucheバージョンは&lt;strong&gt;0.8.10&lt;/strong
&gt;です。
&lt;/p
&gt;&lt;p&gt;本書執筆時点でのKahuaバージョンは&lt;strong&gt;1.0.3&lt;/strong
&gt;です。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>本書の使い方</title
><link>http://karetta.jp/book-node/gauche-hacks/018233</link
><pubDate>Wed, 02 May 2007 11:18:00 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-2lgpij&#39;&gt;本書の読み方
&lt;/h2
&gt;&lt;p&gt;Gaucheの設計思想や開発の背景を手早く知りたい読者はまず「第1部 思想」を読んでください。
&lt;/p
&gt;&lt;p&gt;「第2部 文法」は最初から通して読まずに、
どこからでも読み始められるように書いています。
興味のあるトピックから読んでください。
&lt;/p
&gt;&lt;p&gt;「第3部 実用」はチュートリアル形式で少しづつ改良しながらCGIプログラムを完成させます。他の言語の開発経験があるなら「第1部 思想」の後に「第3部 実用」を読んでも良いでしょう。
&lt;/p
&gt;&lt;h2 id=&#39;H-qj1163&#39;&gt;本書のソースコードや実行例の使い方
&lt;/h2
&gt;&lt;p&gt;対話型インタプリタの実行例が示された箇所については、
読者もまた対話型インタプリタを起動して、
実際に入力して動作を確かめてみると理解が早いでしょう。
&lt;/p
&gt;&lt;p&gt;対話型インタプリタを使うためにはまずGaucheをインストールする必要がありますが、
Gaucheのインストールについては「付録」で説明しています。
&lt;/p
&gt;&lt;p&gt;対話型インタプリタの起動方法や使い方については「第2部 文法」の「対話型インタプリタgosh」で説明しています。
&lt;/p
&gt;&lt;p&gt;対話型インタプリタを単独で起動するほか、対話型インタプリタをEmacsエディタ内部で起動し、ソースコードを編集しながら部分的に評価させることができます。
&lt;/p
&gt;&lt;p&gt;この技法はScheme言語プログラムの開発で頻繁に使われ、しかも強力です。
&lt;/p
&gt;&lt;p&gt;Emacsから対話型インタプリタを使う方法については「第2部 文法」の「EmacsからGaucheを使う」で説明しています。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>本書の表記</title
><link>http://karetta.jp/book-node/gauche-hacks/018223</link
><pubDate>Wed, 02 May 2007 10:53:10 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-1s8u68k&#39;&gt;ソースコード
&lt;/h2
&gt;&lt;p&gt;ソースコードは固定幅フォントで表記します。
&lt;/p
&gt;&lt;p&gt;例:
&lt;/p
&gt;&lt;pre&gt; (lambda (x) (* x x))
&lt;/pre
&gt;&lt;h2 id=&#39;H-963hkg&#39;&gt;対話型インタプリタ
&lt;/h2
&gt;&lt;p&gt;Gaucheの対話型インタプリタでの実行例は gosh&amp;gt; と書かれた入力プロンプトと供に表記します。
&lt;/p
&gt;&lt;p&gt;例:
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (list 0 1 2 3)
 &lt;em&gt;(0 1 2 3)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;上記の例でプロンプトに続く(list 0 1 2 3)はユーザー入力をあらわし、
斜体になった&lt;em&gt;(0 1 2 3)&lt;/em
&gt;はインタプリタが印字した評価結果をあらわします。
&lt;/p
&gt;&lt;h2 id=&#39;H-vksoin&#39;&gt;コメント(注釈)
&lt;/h2
&gt;&lt;p&gt;ソースコード中の;(セミコロン)で始まる箇所はコメント(注釈)を表します。
&lt;/p
&gt;&lt;p&gt;例:
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (exact? 100) ;; 整数は正確数である
 &lt;em&gt;#t&lt;/em
&gt;
&lt;/pre
&gt;&lt;h2 id=&#39;H-10mpsyp&#39;&gt;引数名の強調
&lt;/h2
&gt;&lt;p&gt;手続きの説明で引数名を強調したいとき斜体や太字を使用することがあります。
&lt;/p
&gt;&lt;p&gt;例:
&lt;/p
&gt;&lt;pre&gt; (define (weekday-name &lt;em&gt;index&lt;/em
&gt;)
   (let ((day-names ((list &amp;quot;日&amp;quot; &amp;quot;月&amp;quot; &amp;quot;火&amp;quot; &amp;quot;水&amp;quot; &amp;quot;木&amp;quot; &amp;quot;金&amp;quot; &amp;quot;土&amp;quot;)))
    (if (or (&amp;lt; &lt;em&gt;index&lt;/em
&gt; 0)
            (&amp;gt; &lt;em&gt;index&lt;/em
&gt; 6))
        #f
        (list-ref day-names &lt;em&gt;index&lt;/em
&gt;))))
&lt;/pre
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>はじめに</title
><link>http://karetta.jp/book-node/gauche-hacks/000239</link
><pubDate>Wed, 02 May 2007 10:27:50 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;GaucheはScheme言語処理系のなかでもきわめて実用的で軽快な処理系です。豊富なライブラリが用意され、スクリプト言語処理系として手軽に使うことができます。
&lt;/p
&gt;&lt;p&gt;本書ではScheme言語の基本概念をはじめ、Gaucheの設計思想や開発の背景、Gaucheでのプログラミングの実際を実例とともに紹介して行きます。
&lt;/p
&gt;&lt;p&gt;長い歴史を持つLisp系言語がなぜハッカーたちに愛され続けるのか、多くの読者がその理由の一端に触れられますように。
&lt;/p
&gt;&lt;p&gt;2007年5月 Kahuaプロジェクト一同
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>謝辞</title
><link>http://karetta.jp/book-node/gauche-hacks/018225</link
><pubDate>Sat, 07 Apr 2007 17:43:54 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;査読に協力していただいた2ちゃんねるプログラム板「Lisp Scheme」スレのみなさんに感謝します。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>CGIのユニットテストを書く</title
><link>http://karetta.jp/book-node/gauche-hacks/017585</link
><pubDate>Sun, 01 Apr 2007 16:15:40 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-hg61gl&#39;&gt;CGIのユニットテストを書く
&lt;/h2
&gt;&lt;p&gt;www.cgi-testライブラリを使ってCGIスクリプトを実際に動作させるテストが作れます。
&lt;/p
&gt;&lt;p&gt;CGIに含まれる関数群はモジュール化してgauche.testの単体テストを行い、CGIのファンクショナルテストにwww.cgi-testを使いましょう。
&lt;/p
&gt;&lt;p&gt;「www.cgiライブラリを利用する」で書いたhello.cgiのテストを書いてみましょう。
&lt;/p
&gt;&lt;p&gt;テストコードはtest-hello.scmというファイル名にして、hello.cgiと同じフォルダに置いてください。
&lt;/p
&gt;&lt;pre&gt;(use gauche.test)
(use www.cgi-test)

(test-start &amp;quot;CGI Test&amp;quot;)

(test* &amp;quot;Hello Gauche&amp;quot;
       &amp;quot;&amp;lt;!DOCTYPE HTML PUBLIC \&amp;quot;-//W3C//DTD HTML 4.01//EN\&amp;quot;\n       \&amp;quot;http://www.w3.org/TR/html4/strict.dtd\&amp;quot;&amp;gt;\n&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;Hello, Gauche!&amp;lt;/p\n&amp;gt;&amp;lt;/body\n&amp;gt;&amp;lt;/html\n&amp;gt;&amp;quot;
       (values-ref (run-cgi-script-&amp;gt;string
                    &amp;quot;./hello.scm&amp;quot;
                    :environment &amp;#39;((REQUEST_METHOD . &amp;quot;GET&amp;quot;)))
                   1)
       )

(test-end)
&lt;/pre
&gt;&lt;p&gt;通常のユニットテストと違い、gauche.testに加えてwww.cgi-testを読み込みます。
&lt;/p
&gt;&lt;p&gt;次の箇所でwww.cgi-testの関数を使っています。
&lt;/p
&gt;&lt;pre&gt;       (values-ref (run-cgi-script-&amp;gt;string
                    &amp;quot;./hello.cgi&amp;quot;
                    :environment &amp;#39;((REQUEST_METHOD . &amp;quot;GET&amp;quot;)))
                   1)
&lt;/pre
&gt;&lt;p&gt;run-cgi-script-&amp;gt;stringはCGIスクリプトを実行し、その結果をHTTPヘッダのリストと文字列の二つの値(多値)にして返す関数です。このテストではヘッダーの値はテストしないので、values-refで二つ目の値(0から始まるので二つ目は1)になる文字列だけを取得しています。
ここで得られる文字列はhello.cgiが返すHTMLそのものなので、test*で比較する値はブラウザが受けとるHTMLをそのまま書いています。
&lt;/p
&gt;&lt;p&gt;run-cgi-script-&amp;gt;stringの引数は左から順にCGIスクリプトのパス、オプション引数で環境変数とCGIに渡すパラメータを指定できます。
&lt;/p
&gt;&lt;p&gt;パラメータを渡す例:
&lt;/p
&gt;&lt;pre&gt;; hello.cgi?name=TAROU&amp;amp;age=12とアクセスした場合
(run-cgi-script-&amp;gt;string
        &amp;quot;./hello.cgi&amp;quot;
        :environment &amp;#39;((REQUEST_METHOD . &amp;quot;GET&amp;quot;))
        :parameters &amp;#39;((name . &amp;quot;TAROU&amp;quot;)
                      (age . 12)))
&lt;/pre
&gt;&lt;p&gt;以上のようにしてCGIスクリプトをHTTPサーバを使わずに実行してテストすることができます。
&lt;/p
&gt;&lt;p&gt;www.cgi-testには、run-cgi-script-&amp;gt;stringと返り値の種類が違う同種の関数が用意されています。
&lt;/p
&gt;&lt;p&gt;run-cgi-script-&amp;gt;string-listは行単位の文字列のリストにして返します。
run-cgi-script-&amp;gt;sxmlはHTMLやXMLをS式で表わすSXMLにして返します。実際のアプリケーションは複雑で大きなHTMLを返します。そのようなHTMLから一部分を抽出して比較したい場合にrun-cgi-script-&amp;gt;stringは不便です。SXMLにするとHTMLがSchemeのリストになるため、高度な処理が簡単に記述できるようになります。Gacuhe作者の川合氏が作っているWikiエンジンのWiLiKiにはSXMLのパターンマッチを使ったテスト用ライブラリsxml.xml-testがあります。是非参考にしてください。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>ユニットテストを書く</title
><link>http://karetta.jp/book-node/gauche-hacks/017203</link
><pubDate>Fri, 30 Mar 2007 21:29:12 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-gew5fy&#39;&gt;ユニットテストを書く
&lt;/h2
&gt;&lt;p&gt;gauche.testライブラリを使ってユニットテストを作れます。
&lt;/p
&gt;&lt;p&gt;例えば、二乗を計算する関数squareのテストを書いてみましょう。
&lt;/p
&gt;&lt;p&gt;squareの定義(実はバグがあります):
&lt;/p
&gt;&lt;pre&gt;(define (square n) (+ n n))
&lt;/pre
&gt;&lt;p&gt;squareのユニットテスト:
&lt;/p
&gt;&lt;pre&gt;(use gauche.test)

(test-start &amp;quot;square test&amp;quot;)

;;一つ目のテスト群
(test-section &amp;quot;test group 1&amp;quot;)
(test* &amp;quot;square 3&amp;quot; 9 (square 3))
(test* &amp;quot;square -2&amp;quot; 4 (square -2))

;;二つ目のテスト群
(test-section &amp;quot;test group 2&amp;quot;)
(test* &amp;quot;pass string argument&amp;quot; *test-error* (square &amp;quot;a&amp;quot;))

(test-end)
&lt;/pre
&gt;&lt;p&gt;このユニットテストでは三つのテストを用意しています。
一つ目は3の二乗が9であることを、二つ目は-2の二乗が4であることをテストしています。
三つ目は、引数に文字列を渡したときにエラーが起きることをテストしています。
&lt;/p
&gt;&lt;p&gt;テストの始まりにはtest-start、終わりにはtest-endを必ず書きます。
test-startの引数に書いた文字列はテスト実行時にプリントされるテストログに使われます。
test-sectionを使ってテストを分類するとたくさんのテストがあるときにテストログを分かりやすくできます。
&lt;/p
&gt;&lt;p&gt;test*はテストを行うマクロです。引数は左から順に、テスト名、期待する結果、テストする式です。
テストする式の結果と期待する結果を比較し、結果が#tなら成功、#fなら失敗します。
オプション引数compareで比較を行う手続きを指定できます。compareを指定しないとequal?を使って比較が行われます。
このマクロはtest関数に展開されます。test関数はテストする式をthunk(引数無しの手続き)の形で書かなければいけないので、test*マクロを使ったほうが楽に書けます。
&lt;/p
&gt;&lt;p&gt;test関数でテストを書いた場合はこのようになります。
&lt;/p
&gt;&lt;pre&gt;(test &amp;quot;square 3&amp;quot; 9 (lambda () (square 3)))
&lt;/pre
&gt;&lt;p&gt;期待する結果に*test-error*変数を使うと、エラーが起きることを確認するテストを作れます。
&lt;/p
&gt;&lt;p&gt;それでは、実際にユニットテストを行うスクリプトファイルを作りましょう。
&lt;/p
&gt;&lt;p&gt;テスト対象のsquareの定義をsquare.scm、ユニットテストをtest-square.scmに書くことにします。二つのファイルは同じフォルダ内に置いてください。
&lt;/p
&gt;&lt;p&gt;test-square.scm:
&lt;/p
&gt;&lt;pre&gt;(use gauche.test)

(add-load-path &amp;quot;.&amp;quot;)
(load &amp;quot;square&amp;quot;)

(test-start &amp;quot;square test&amp;quot;)

;;一つ目のテスト群
(test-section &amp;quot;test group 1&amp;quot;)
(test* &amp;quot;square 3&amp;quot; 9 (square 3))
(test* &amp;quot;square -2&amp;quot; 4 (square -2))

;;二つ目のテスト群
(test-section &amp;quot;test group 2&amp;quot;)
(test* &amp;quot;pass string argument&amp;quot; *test-error* (square &amp;quot;a&amp;quot;))

(test-end)
&lt;/pre
&gt;&lt;p&gt;テストを始める前に、テスト対象のsquare関数をloadしています。
add-load-pathでユニットテストファイルと同じ場所にあるsquare.scmをロー
ドできるようにしています。ファイルをloadするとsquareの定義が読み込まれます。テスト対象のファイルがモジュール化されているなら、loadの代わりにuseを使うといいでしょう。
&lt;/p
&gt;&lt;p&gt;それではユニットテストファイルを実行してみましょう。テスト結果がプリントされます。
&lt;/p
&gt;&lt;p&gt;テスト結果:
&lt;/p
&gt;&lt;pre&gt;$ gosh test-square.scm
Testing square test ...                                          
&amp;lt;test group 1&amp;gt;-----------------------------------------------------------------
test square 3, expects 9 ==&amp;gt; ERROR: GOT 6
&amp;lt;test group 2&amp;gt;-----------------------------------------------------------------
test square -2, expects 4 ==&amp;gt; ERROR: GOT -4
test pass string argument, expects #&amp;lt;error&amp;gt; ==&amp;gt; ok
failed.
discrepancies found.  Errors are:
test square 3: expects 9 =&amp;gt; got 6
test square -2: expects 4 =&amp;gt; got -4
&lt;/pre
&gt;&lt;p&gt;二つのテストに失敗してしまいました。プリントされたログをみると、3の二乗が6、-2の二乗が-4になっています。squareにバグがあるので修正しましょう。
&lt;/p
&gt;&lt;p&gt;squareの定義(バグを修正済み):
&lt;/p
&gt;&lt;pre&gt;(define (square n) (* n n))
&lt;/pre
&gt;&lt;p&gt;squareを修正したので、もう一回テストを実行してみましょう。
&lt;/p
&gt;&lt;p&gt;テスト結果:
&lt;/p
&gt;&lt;pre&gt;$ gosh test-square.scm
Testing square test ...                                          
&amp;lt;test group 1&amp;gt;-----------------------------------------------------------------
test square 3, expects 9 ==&amp;gt; ok
&amp;lt;test group 2&amp;gt;-----------------------------------------------------------------
test square -2, expects 4 ==&amp;gt; ok
test pass string argument, expects #&amp;lt;error&amp;gt; ==&amp;gt; ok
passed.
&lt;/pre
&gt;&lt;p&gt;テストに成功しました。
&lt;/p
&gt;&lt;p&gt;複数のユニットテストをまとめて実行したいときにはMakefileを使うといいでしょう。
ログファイルを一つにしておくと、確認するときに便利です。
&lt;/p
&gt;&lt;pre&gt;test :
     gosh my-feature-test1.scm &amp;gt;&amp;gt; test.log
     gosh my-feature-test2.scm &amp;gt;&amp;gt; test.log
     gosh my-feature-test3.scm &amp;gt;&amp;gt; test.log
&lt;/pre
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>SRFI</title
><link>http://karetta.jp/book-node/gauche-hacks/004724</link
><pubDate>Fri, 30 Mar 2007 17:01:44 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;SRFIとは&lt;strong&gt;S&lt;/strong
&gt;chmeme &lt;strong&gt;R&lt;/strong
&gt;equest &lt;strong&gt;f&lt;/strong
&gt;or &lt;strong&gt;I&lt;/strong
&gt;mprementanionの略です。
SRFIは、Schemeの言語仕様であるR5RSには書かれていない多くの機能を提供するライブラリです。
&lt;/p
&gt;&lt;p&gt;&lt;a href=&#39;http://srfi.schemers.org/&#39;&gt;http://srfi.schemers.org/&lt;/a
&gt;
&lt;/p
&gt;&lt;p&gt;SRFIについての情報は上記URLで管理されています。
&lt;/p
&gt;&lt;p&gt;SRFIはScheme処理系に依存しない共通ライブラリ集です。SRFIを使うことでSchemeプログラムの移植性を高めることができます。
&lt;/p
&gt;&lt;p&gt;GaucheではいくつかのSRFIライブラリを、Gaucheに付属するモジュールとしてサポートしています。
&lt;/p
&gt;&lt;table class=&#39;inbody&#39; border=&#39;1&#39; cellspacing=&#39;0&#39;&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-0
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;機能条件式
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-1
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;リストライブラリ
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-4
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;単一型のベクタ
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-5
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;シグネチャとrest引数に互換性のあるlet形式
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-7
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;機能ベースプログラム設定言語
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-9
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;レコード型
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-11
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;Let-values
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-13
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;文字列ライブラリ
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-14
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;文字集合ライブラリ
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-19
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;時間のデータ型と手続き
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-27
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;ランダムビットのソース
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-29
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;地域化
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-37
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;args-fold プログラム引数処理
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-42
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;先行評価的内包表記
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-43
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;ベクタライブラリ
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;srfi-55
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;require の拡張
&lt;/td
&gt;&lt;/tr
&gt;&lt;/table
&gt;&lt;p&gt;これらのうち特によく使われるのはsrfi-1のリストライブラリ、srfi-13の文字列ライブラリです。
本書では第1部「すべて再帰である」でsrfi-1を、第2部「文字集合」でsrfi-14を、「多値」でsrfi-11を、第3部「スケジュールCGIを書こう」でsrfi-1、srfi-13、srfi-19を使用しています。
&lt;/p
&gt;&lt;p&gt;詳しい内容についてはGaucheのリファレンスマニュアルやSRFIの情報サイトを参照してください。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-3b7opk&#39;&gt;組み込みでサポートされるSRFIライブラリ
&lt;/h2
&gt;&lt;p&gt;モジュールとして提供される以外に、GaucheではいくつかのSRFIライブラリを組み込みでサポートしています。代表的なものは以下です。
&lt;/p
&gt;&lt;table class=&#39;inbody&#39; border=&#39;1&#39; cellspacing=&#39;0&#39;&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-2
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;AND-LET*: 局所束縛をともなう AND、ガード付 LET* 特殊フォーム
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-6
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;基本文字列ポート
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-7
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;機能ベースプログラム設定言語
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-8
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;receive: 多値束縛
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-10
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;Sharp-comma外部フォーム
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-16
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;可変長引数手続き構文 (case-lambda)
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-17
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;一般化された set!
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-18
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;マルチスレッドのサポート
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-22
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;UNIX 上の Scheme スクリプトの実行
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-23
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;エラー報告機構
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-26
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;カリー化をともなわないパラメータの特殊化記法
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-30
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;ネストした複数行コメント
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-31
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;再帰評価用の特殊フォーム rec
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-34
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;プログラムの例外処理
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-35
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;コンディション
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-38
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;共有されるデータの外部表現
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-45
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;反復的 Lazy アルゴリズムのための基本関数
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-55
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;require-extension
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-61
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;より汎用的なcond節
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-62
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;S式コメント
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;SRFI-61
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;case節での=&amp;gt;
&lt;/td
&gt;&lt;/tr
&gt;&lt;/table
&gt;&lt;p&gt;詳しくはGaucheリファレンスマニュアルの&lt;a href=&#39;http://practical-scheme.net/gauche/man/gauche-refj_5.html#SEC5&#39;&gt;標準への準拠&lt;/a
&gt;を参照してください。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>eval</title
><link>http://karetta.jp/book-node/gauche-hacks/017186</link
><pubDate>Tue, 03 Apr 2007 14:03:45 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;evalは与えられたデータをSchemeの式として解釈し、実行する手続きです。
&lt;/p
&gt;&lt;pre&gt;eval expr env
&lt;/pre
&gt;&lt;p&gt;exprは、Schemeの式として解釈可能な(妥当な)データを表す式であり、envはその式を解釈・実行する際の環境を表すオブジェクトです。これだけだとわかりにくいので、例を挙げてみましょう。
&lt;/p
&gt;&lt;pre&gt;gosh&amp;gt; (eval &amp;#39;(+ 1 2) (interaction-environment))
3
gosh&amp;gt;
&lt;/pre
&gt;&lt;p&gt;ここで、第1引数に(+ 1 2)を&amp;#39;(+ 1 2)を渡していることに注目して下さい。
evalは手続きですから、引数は必ず評価されてからevalに渡されます。
従って
&lt;/p
&gt;&lt;pre&gt;gosh&amp;gt; (eval (+ 1 2) (interaction-environment))
3
gosh&amp;gt;
&lt;/pre
&gt;&lt;p&gt;というのは、
&lt;/p
&gt;&lt;pre&gt;gosh&amp;gt; (eval 3 (interaction-environment))
3
gosh&amp;gt;
&lt;/pre
&gt;&lt;p&gt;と同じことになってしまいます。
&lt;/p
&gt;&lt;p&gt;第2引数には、R5RSに以下の手続きが返す値を渡すことが定められています。
&lt;/p
&gt;&lt;table class=&#39;inbody&#39; border=&#39;1&#39; cellspacing=&#39;0&#39;&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;(interaction-environment)
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;実装定義の束縛を含む環境を返す
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;(scheme-report-environment 5)
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;R5RSで規定されている束縛のみを含む環境を返す
&lt;/td
&gt;&lt;/tr
&gt;&lt;tr class=&#39;inbody&#39;&gt;&lt;td class=&#39;inbody&#39;&gt;(null-environment 5)
&lt;/td
&gt;&lt;td class=&#39;inbody&#39;&gt;R5RSで規定されている構文キーワードのみを含む環境を返す
&lt;/td
&gt;&lt;/tr
&gt;&lt;/table
&gt;&lt;p&gt;ただし、Gaucheにおいてはこれらの手続きが返す「環境」は単にモジュールなので、
第2引数に任意のモジュールを渡すことができます。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>Gaucheインストール</title
><link>http://karetta.jp/book-node/gauche-hacks/004680</link
><pubDate>Wed, 28 Mar 2007 13:58:59 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-a9fet6&#39;&gt;本書執筆時点でのバージョン
&lt;/h2
&gt;&lt;p&gt;本書執筆時点(2007年3月現在)でのGaucheのバージョンは&lt;strong&gt;0.8.9&lt;/strong
&gt;です。
&lt;/p
&gt;&lt;h2 id=&#39;H-11x7if8&#39;&gt;動作環境
&lt;/h2
&gt;&lt;p&gt;本書執筆時点(2007年3月現在)でGaucheはLinux、FreeBSD、Mac OS XをはじめとするいくつかのUNIX互換OSで動作しています。
&lt;/p
&gt;&lt;h2 id=&#39;H-1p7agki&#39;&gt;Kahuaを動作させるためのGauche configureオプション
&lt;/h2
&gt;&lt;p&gt;Kahuaを動作させるためには以下のオプションでGaucheのconfigureを実行してください。
&lt;/p
&gt;&lt;pre&gt; ./configure --enable-threads=pthreads --enable-multibyte=utf-8
&lt;/pre
&gt;&lt;p&gt;Kahuaが動作するためにはマルチスレッドが必須です。
--enable-threads=pthreadsオプションを必ず指定してください。
&lt;/p
&gt;&lt;p&gt;Kahuaで日本語を含むマルチバイトを扱う場合、
特別な理由がない限り--enable-multibyte=utf-8オプションを指定し、
Gaucheの内部エンコーディングをUTF-8にしてください。
&lt;/p
&gt;&lt;p&gt;Gaucheは開発中のシステムであり、頻繁に更新されています。
動作環境、インストールをはじめとする最新の情報については、
常に次のURLを参照してください。
&lt;a href=&#39;http://practical-scheme.net/gauche/&#39;&gt;http://practical-scheme.net/gauche/&lt;/a
&gt;
&lt;/p
&gt;&lt;h2 id=&#39;H-ljlufu&#39;&gt;簡単な方法
&lt;/h2
&gt;&lt;pre&gt;   % ./configure [--enable-threads=pthreads]
   % make
   % make test
   % make install
&lt;/pre
&gt;&lt;p&gt;これで、Gaucheシステムが /usr/local/bin, /usr/local/lib/gauche及び
/usr/local/share/gauche以下にインストールされます。
マルチスレッドサポートを使うためにはconfigureに--enable-threads=pthreadsを
渡して下さい。このオプションはいくつかのプラットフォームでサポートされています。
&lt;/p
&gt;&lt;p&gt;既にGauche 0.5以降がインストールされているシステムで、
全く同じconfigurationオプションで新しいバージョンのGaucheをインストール
する場合、&amp;#39;configure&amp;#39; の代わりに次のコマンドを使うこともできます。
&lt;/p
&gt;&lt;pre&gt;    % gauche-config --reconfigure | sh
&lt;/pre
&gt;&lt;p&gt;&amp;#39;--reconfigure&amp;#39; オプションが与えられると、gauche-configは現在
インストールされているGaucheがconfigureされた時のコマンドラインを
標準出力に書き出します。それをシェルに評価させれば、同じオプションで
configureすることができます。
&lt;/p
&gt;&lt;h2 id=&#39;H-1pamsz8&#39;&gt;文字エンコーディングの選択
&lt;/h2
&gt;&lt;p&gt;デフォルトでは、Gaucheは内部文字エンコーディングとしてutf-8を使います。
--enable-multibyte=ENCODING というオプションをconfigureに渡すことで、
内部文字エンコーディングを変えることができます。
&lt;/p
&gt;&lt;pre&gt;  ./configure --enable-multibyte=utf-8   ;; UTF-8 (default)
  ./configure --enable-multibyte=euc-jp  ;; EUC-JP
  ./configure --enable-multibyte=sjis    ;; Shift JIS
  ./configure --enable-multibyte=no      ;; No multibyte string
&lt;/pre
&gt;&lt;p&gt;Gaucheは代表的な日本語文字エンコーディングを認識し変換することが
できますが、それ以外のエンコーディングに関しては、iconvが利用可能で
あればそれを利用して変換を行います。
iconvが標準でインストールされていないシステムでは、
--with-iconvオプションで外部のiconvライブラリを利用することができます。
&lt;/p
&gt;&lt;pre&gt;  ./configure --with-iconv=DIR
&lt;/pre
&gt;&lt;p&gt;例えば --with-iconv=/usr/local とすれば、Gaucheはiconv.hを/usr/local/include
から、libiconvを/usr/local/libから探します。もしあなたのiconvライブラリが
libiconv以外の名前 (libmyconvとか) だったとしたら、
--with-iconv-lib=myconv というオプションも指定して下さい。
&lt;/p
&gt;&lt;p&gt;外部のiconvライブラリとしては、Bruno Haible氏のlibiconvが以下から入手可能です。
&lt;a href=&#39;http://www.gnu.org/software/libiconv/&#39;&gt;http://www.gnu.org/software/libiconv/&lt;/a
&gt;
&lt;/p
&gt;&lt;h2 id=&#39;H-6atumh&#39;&gt;ローカルライブラリパス
&lt;/h2
&gt;&lt;p&gt;Gaucheのビルドに、標準でない場所にインストールされているライブラリを
使用したい場合、次のオプションでその場所を指定することができます。
&lt;/p
&gt;&lt;pre&gt;  --with-local=PATH:PATH2:...
&lt;/pre
&gt;&lt;p&gt;これで、PATH/include、PATH2/include、... がインクルードパスに、
PATH/lib、PATH2/lib、... がライブラリサーチパスに追加されます。
&lt;/p
&gt;&lt;p&gt;例えば、/usr/localにインストールされたgdbmライブラリを使用したい
場合は次のようにします。
&lt;/p
&gt;&lt;pre&gt;  --with-local=/usr/local
&lt;/pre
&gt;&lt;h2 id=&#39;H-ccnfwd&#39;&gt;IPv6サポート
&lt;/h2
&gt;&lt;p&gt;Gaucheは実験的にIPv6をサポートしています。デフォルトではこの機能はoffに
なっていますが、次のconfigureオプションで有効にすることができます。
&lt;/p
&gt;&lt;pre&gt;  --enable-ipv6
&lt;/pre
&gt;&lt;p&gt;もちろん、OSでIPv6がサポートされていなければなりません。
現在の実装はFreeBSD上でテストされています。
詳しい機能はリファレンスマニュアルのgauche.netの項を参照して下さい。
&lt;/p
&gt;&lt;h2 id=&#39;H-bmnd8l&#39;&gt;SLIBの場所
&lt;/h2
&gt;&lt;p&gt;SLIBは、ポータブルなSchemeライブラリで、いろいろ便利な機能が
完全にSchemeで書かれています。GaucheはSLIBがインストールされていれば
その機能を利用することができます。
&lt;/p
&gt;&lt;p&gt;configureスクリプトは、/usr/local/slibなどいくつかの基本的なディレクトリ
からSLIBを自動的に探します。もしあなたのSLIBシステムが標準的ではない場所に
インストールされていたら、次のオプションでその場所を指定してください。
&lt;/p
&gt;&lt;pre&gt;   ./configure --with-slib=PATH
&lt;/pre
&gt;&lt;p&gt;ここで、PATHはSLIBがインストールされたパスです。
&lt;/p
&gt;&lt;p&gt;GauchehはSLIB無しでも動作します。SLIBを使う必要がなければ、このオプションは
気にしなくてよいです。
&lt;/p
&gt;&lt;p&gt;SLIBは最初に使う前にGaucheのライブラリディレクトリにカタログファイルを
作成します。Gaucheのインストール時に既にSLIBが存在すればインストール
スクリプトがカタログファイルを作成しますが、Gaucheインストール後にSLIB
がインストールされた場合、最初にSLIBを使おうとした時点でカタログファイルが
作成されます。この時、Gauche使用者がライブラリディレクトリに書き込み権限を
持っていないとエラーとなります。書き込み権限を持つユーザがgoshを起動して
例えば次のような式を評価すればカタログファイルが正しく作られます。
&lt;/p
&gt;&lt;pre&gt;  (use slib)
  (require &amp;#39;logical)
&lt;/pre
&gt;&lt;h2 id=&#39;H-yrpk49&#39;&gt;実行時のライブラリパス
&lt;/h2
&gt;&lt;p&gt;しばしば、環境のコントロールができない箇所でGaucheを走らせなければならない
場合があります。例えばCGIスクリプトをISPのサーバーで走らせる場合などです。
もし、Gaucheが標準的でない場所にインストールされた共有ライブラリに依存し
ている場合、それが問題となります。
&lt;/p
&gt;&lt;p&gt;例えば、最新のlibiconv.soを自分でコンパイルして /home/yours/lib に
インストールしたとします。--with-iconv=/home/yours/lib としてconfigure
すれば、Gaucheはあなたのiconvを使うようにコンパイルされます。実行時に適切
な環境変数、例えばLD_LIBRARY_PATHなどを設定しておけば、Gaucheの
インタプリタgoshはlibiconv.soを捜し出すことができます。しかし、
CGIスクリプトはWeb serverによって起動され、Web serverはLD_LIBRARY_PATH
を設定してくれないかもしれません。その場合、goshはlibiconv.soが見つけられずに
起動に失敗するかもしれません。
&lt;/p
&gt;&lt;p&gt;コンパイラによっては、プログラムが依存している共有ライブラリのパスをプログラム
本体に書き込んでしまえるオプションを持っています。configureの --with-rpath
オプションはそれを利用します。--with-rpath=DIRとすると、実行時にDIRから
共有ライブラリを探すような設定になります。このオプションは今のところgccでしか
動作しません。
&lt;/p
&gt;&lt;h2 id=&#39;H-1m3po3u&#39;&gt;インストール先の指定
&lt;/h2
&gt;&lt;p&gt;次のようにconfigureスクリプトを起動することにより、インストール先の
ディレクトリを指定できます。
&lt;/p
&gt;&lt;pre&gt;   % ./configure --prefix=$PREFIX  --exec-prefix=$EXEC_PREFIX
&lt;/pre
&gt;&lt;p&gt;--prefix オプションが指定されなければ、 /usr/local が指定されたものとみなされます。
--exec-prefix オプションが指定されなければ、EXEC_PREFIXはPREFIXと同じものに
なります。
&lt;/p
&gt;&lt;p&gt;インストールされるファイルの内訳は以下の通りです。
&lt;/p
&gt;&lt;pre&gt;  $EXEC_PREFIX/bin/*
     インタプリタ (gosh) とコンフィグレーションスクリプト (gauche-config)
&lt;/pre
&gt;&lt;pre&gt;  $EXEC_PREFIX/lib/*
     ライブラリ (libgauche.a).
&lt;/pre
&gt;&lt;pre&gt;  $PREFIX/share/gauche/VERSION/include/*
     libgaucheをリンクするアプリケーションに必要なヘッダーファイル
&lt;/pre
&gt;&lt;pre&gt;  $PREFIX/share/gauche/VERSION/lib/*
     マシンに依存しないSchemeファイル
&lt;/pre
&gt;&lt;pre&gt;  $PREFIX/share/info/*
     infoファイル
&lt;/pre
&gt;&lt;pre&gt;  $EXEC_PREFIX/lib/gauche/VERSION/ARCHITECTURE/*
     マシンに依存するファイル (.so, gosh, gauche-config, libgauche.a)
&lt;/pre
&gt;&lt;pre&gt;  $PREFIX/share/gauche/site/lib/*
  $EXEC_PREFIX/lib/gauche/site/VERSION/ARCHITECTURE/*
     ユーザが独自にインストールするマシン非依存／依存ファイルのデフォルトの置き場所。
&lt;/pre
&gt;&lt;h2 id=&#39;H-ka1csr&#39;&gt;最適化オプション
&lt;/h2
&gt;&lt;p&gt;Make時に、makeマクロOPTFLAGSを用いて追加のコンパイルオプションを指定すること
ができます。configureが設定するOPTFLAGS以外のオプションを試したい場合は
次のようにmakeを走らせて下さい。
&lt;/p
&gt;&lt;pre&gt;  make OPTFLAGS=&amp;quot;--some-compiler-option --other-option&amp;quot;
&lt;/pre
&gt;&lt;h2 id=&#39;H-k17nnm&#39;&gt;アンインストール
&lt;/h2
&gt;&lt;p&gt;インストールされたGaucheソフトウェアを取り除くには、ソースツリーのトップ
ディレクトリで
&lt;/p
&gt;&lt;pre&gt;  make uninstall
&lt;/pre
&gt;&lt;p&gt;として下さい。
&lt;/p
&gt;&lt;h2 id=&#39;H-lyhkor&#39;&gt;機種依存の情報
&lt;/h2
&gt;&lt;h3 id=&#39;H-1kp07gu&#39;&gt;MacOS X
&lt;/h3
&gt;&lt;p&gt;dlcompatライブラリ(libdl)がインストールされていることが必要です。
&lt;a href=&#39;http://fink.sourceforge.net&#39;&gt;http://fink.sourceforge.net&lt;/a
&gt;
からダウンロードできます。
システム標準の場所以外にdlcompatライブラリをインストールした場合は、
configureの--with-localオプションで場所を指定して下さい。
また、GCがpthreadsを要求するようになったので、--enable-threads=pthreads
も必要です。
(例：$HOME/include に dlfcn.h が、 $HOME/lib に libdl.aがある場合は
&lt;/p
&gt;&lt;pre&gt;    ./configure --with-local=$HOME --enable-threads=pthreads
&lt;/pre
&gt;&lt;p&gt;とする)
&lt;/p
&gt;&lt;h3 id=&#39;H-1ajsbvj&#39;&gt;IRIX with 64bit binary
&lt;/h3
&gt;&lt;p&gt;デフォルトの32bit ABIではなく64bit ABIでコンパイル
したい場合は次のようにして下さい。
&lt;/p
&gt;&lt;pre&gt;    CC=&amp;quot;cc -64&amp;quot; AS=&amp;quot;as -64&amp;quot; ./configure
&lt;/pre
&gt;&lt;h2 id=&#39;H-1mt1goh&#39;&gt;Linux/Crusoe TM5800
&lt;/h2
&gt;&lt;p&gt;最近のLinuxでは Crusoe TM5800がi686互換とされる
ようになったようですが、gc中でi686特有のprefetch命令を使っているところ
でコンパイルに失敗するという報告がありました。とりあえずのfixは、
configure後にgc/Makefile中の 
&lt;/p
&gt;&lt;pre&gt;   -DUSE_I686_PREFETCH
&lt;/pre
&gt;&lt;p&gt;という記述を消してからmakeして下さい。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>無限エクステントとガベージコレクション</title
><link>http://karetta.jp/book-node/gauche-hacks/014810</link
><pubDate>Wed, 02 May 2007 15:13:43 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;h2 id=&#39;H-brhm7j&#39;&gt;無限エクステント
&lt;/h2
&gt;&lt;p&gt;Schemeでは、その計算途中で生成される全ての値(より正確には、値を保持する&lt;strong&gt;領域&lt;/strong
&gt;)は無限の寿命を持ちます。値(を保持する領域)の生存期間を&lt;strong&gt;エクステント&lt;/strong
&gt;と呼び、「Schemeの値(を保持する領域)は無限のエクステントを持つ」と表現されます。スコープと混同しないように注意して下さい。Schemeにおいてスコープは、値につけた名前(つまり変数)の有効範囲であり、基本的に有限です。無限エクステントは、Schemeの大きな特徴の一つです。
&lt;/p
&gt;&lt;p&gt;この特徴を利用すると、例えば手続きの中に値(を保持する領域)を閉じ込めることができます。
&lt;/p
&gt;&lt;pre&gt;gosh&amp;gt; (define-values (x-set! x-get)
        (let ((x 0))
          (values (lambda (val) (set! x val))
                  (lambda ()    x))))
#&amp;lt;undef&amp;gt;
gosh&amp;gt; (x-get)
0
gosh&amp;gt; (x-set! 1)
1
gosh&amp;gt; (x-get)
1
&lt;/pre
&gt;&lt;p&gt;この例では、let式でxに束縛された値の領域は、let式を抜けても有効です。一方でxという名前自体は、let式を抜けたところで無効になっています。この領域には、x-set!、x-getという2つの手続きからのみ触ることができることになるわけです。これは、オブジェクト指向プログラミングで言うところの「カプセル化」の原始的な形であり、Schemeプログラミングにおいて比較的頻繁に使われる手法です。
&lt;/p
&gt;&lt;h2 id=&#39;H-19k4nyc&#39;&gt;ガベージコレクション
&lt;/h2
&gt;&lt;p&gt;値(を保持する領域)の生存期間が無制限だと説明しましたが、メモリは有限の資源です。それでも(通常は)メモリを使い切ることはありません。なぜでしょうか。
&lt;/p
&gt;&lt;p&gt;これは、以降の計算において全く参照されないことがわかっている値(を保持する領域)は、システムが回収して再利用するからです。このしくみを「ガベージコレクション(ごみ集め)」といいます。現在では、JavaやC#を始めとして、言語の枠組みにガベージコレクションが組み込まれていることは珍しくなくなりました。この仕組みがあるために、プログラマはメモリ管理にあまり気を使うことなくプログラミングを行うことができるのです。
&lt;/p
&gt;&lt;h2 id=&#39;H-16v9w70&#39;&gt;ガベージコレクションの注意点
&lt;/h2
&gt;&lt;p&gt;ガベージコレクションはプログラマをメモリ管理の煩雑さから解放してはくれますが、万能ではありません。ここでは、プログラマが気をつけるべきことをいくつか挙げておきます。
&lt;/p
&gt;&lt;h3 id=&#39;H-jgok34&#39;&gt;メモリ以外のリソース解放をガベージコレクションに任せない
&lt;/h3
&gt;&lt;p&gt;ガベージコレクションを過信してしまったプログラマがやりがちな間違いは、メモリ以外のリソースの解放をガベージコレクションに任せてしまうことです。
&lt;/p
&gt;&lt;p&gt;例えば、以下のコードを見て下さい。
&lt;/p
&gt;&lt;pre&gt;(define (some-file-op file)
  (let ((in (open-input-file file)))
    ... ;; ここでは明示的に (close-input-port in) していない
    ))
&lt;/pre
&gt;&lt;p&gt;このsome-file-opは、明示的にファイルポートを閉じていません。確かにGaucheの実装では、変数inに束縛されたファイルポートがガベージコレクションによって回収される際、開けっ放しだったポートは閉じられます。しかし、逆に言えばガベージコレクションされるまではファイルポートは閉じられません。たいていのシステムにおいては、メモリよりもファイルポートの方が貴重な資源であり、手続きsome-file-op使い方によっては、ガベージコレクションが起こる前に、開けるファイルの数の上限に達してしまうでしょう。メモリ以外のリソースについては明示的に解放すべきなのです。Gaucheであれば、
&lt;/p
&gt;&lt;pre&gt;(define (some-file-op file)
  (call-with-input-file file
    (lambda (in)
      ... ;; ここでは明示的に (close-input-port in) していない
      )))
&lt;/pre
&gt;&lt;p&gt;とすることで、自動的にファイルポートを閉じることができます(が、これはScheme標準の機能ではありません)。
&lt;/p
&gt;&lt;h3 id=&#39;H-ne8dlp&#39;&gt;ループ内部でのローカル変数を避ける
&lt;/h3
&gt;&lt;p&gt;あまりに頻繁に実行されるループの内側でアロケーションを発生させると、大きく効率を損なう場合があります。例えば、(あまり現実的な例ではありませんが)以下のコードを見て下さい。
&lt;/p
&gt;&lt;pre&gt; ...
 (let loop ((num 1000000000))
   (let ((vec (make-vector 100000)))
     (if (&amp;lt;= 0 num)
         ... ;; vecを使って計算した結果を返す
         (begin
           ... ;; vecを使って計算する
           (loop (- num 1))))))
 ...
&lt;/pre
&gt;&lt;p&gt;ループを繰り返すたびに大きなベクタがアロケートされ、
ループを終了したところで捨てられます(が、回収されるのは
通常ずっと後です)。もちろん、計算の内容や値の返し方にも
よりますが、単なる計算用のバッファとしてvecを使っている
のであれば、次のようにした方が効率はずっと良くなります。
&lt;/p
&gt;&lt;pre&gt; ...
 (let ((vec (make-vector 100000)))
   (let loop ((num 1000000000))
     (if (&amp;lt;= 0 num)
         ... ;; vecを使って計算した結果を返す
         (begin
           ... ;; vecを使って計算する
           (loop (- num 1))))))
 ...
&lt;/pre
&gt;&lt;p&gt;ただし、ここまで極端な場合を除けば、むしろあまり神経質にならないことをお薦めします。こうした効率化は一種の最適化と考えるべきです。早すぎる効率化はしばしば失敗に繋がります。特に効率が大きな問題になるまでは、あくまでもロジックに集中すべきです。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>仮想マシン</title
><link>http://karetta.jp/book-node/gauche-hacks/014806</link
><pubDate>Fri, 23 Mar 2007 16:22:27 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;Gaucheの仮想マシン(Virtual Machine, VM)はGauche処理系に組み込まれています。Gaucheの仮想マシンを単独で動作させることはできません。
&lt;/p
&gt;&lt;p&gt;例えば、コンパイル後の内部コードをGauche仮想マシンに外から与えて実行させることは(今のところ)できません。
&lt;/p
&gt;&lt;p&gt;Gaucheのコンパイルと実行について知っておくべきことは以下です。
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;プログラムの実行(eval)＝compile + executionである
&lt;/li
&gt;&lt;li&gt;Gaucheでは通常はトップレベルフォームごとにコンパイルと実行を行っている
&lt;/li
&gt;&lt;li&gt;特殊形式の解釈とマクロの展開はコンパイルの段階で行われる
&lt;/li
&gt;&lt;li&gt;コンパイル時の束縛によって最適化される手続きがある
&lt;/li
&gt;&lt;/ol
&gt;&lt;h2 id=&#39;H-1yeerqq&#39;&gt;プログラムの実行(eval)＝compile + executionである
&lt;/h2
&gt;&lt;p&gt;Gaucheでは、プログラムの評価(eval)とはコンパイル(compile)+実行(execution)です。
&lt;/p
&gt;&lt;pre&gt; eval = compile + execution
&lt;/pre
&gt;&lt;p&gt;コンパイルはコンパイラが行い、実行は仮想マシンが行います。
&lt;/p
&gt;&lt;h2 id=&#39;H-a2s7rs&#39;&gt;Gaucheではトップレベルフォームごとにcompileとexecutionを行っている
&lt;/h2
&gt;&lt;p&gt;Schemeのプログラムは複数の式の並びから成ります。この式ひとつとつのことをトップレベルフォームと呼びます。
&lt;/p
&gt;&lt;p&gt;Gaucheでは通常このトップレベルフォームごとにコンパイルと実行を行っています。
&lt;/p
&gt;&lt;h2 id=&#39;H-12an7u2&#39;&gt;特殊形式の解釈とマクロの展開はコンパイルの段階で行われる
&lt;/h2
&gt;&lt;p&gt;ある条件のときだけモジュールを使用したいと思って、
&lt;/p
&gt;&lt;pre&gt; (if 条件式 (use モジュール名)) 
&lt;/pre
&gt;&lt;p&gt;上記のif式を書いても意図どおりの結果にはなりません。
特殊形式の解釈とマクロの展開はコンパイル時に行われるからです。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (if #f (use gauche.threads))
 &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;useマクロはif式の判定にかかわらず実行されています。
&lt;/p
&gt;&lt;h2 id=&#39;H-1wiwcqd&#39;&gt;コンパイル時の束縛によって最適化される手続きがある
&lt;/h2
&gt;&lt;pre&gt; gosh&amp;gt; (define (f x) (car x))
 &lt;em&gt;f&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;リストにcarを適用する手続きfを定義しています。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (set! car #f)
 &lt;em&gt;#f&lt;/em
&gt;
 gosh&amp;gt; car
 &lt;em&gt;#f&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;手続きfの定義後にcarを書き換え、
car手続きを使えなくしてしまったらどうなるでしょうか?
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (f (list 1 2 3))
 &lt;em&gt;1&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;f手続きで参照されているのはコンパイル時のcarなので、
その後carが書き換えられても正常に動作しています。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>コンパイラ</title
><link>http://karetta.jp/book-node/gauche-hacks/014804</link
><pubDate>Fri, 23 Mar 2007 14:50:29 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;Gaucheはインタプリタですが、単なるインタプリタではなくいったんコンパイルしています。コンパイルされたコードはGaucheの&lt;strong&gt;仮想マシン&lt;/strong
&gt;で実行されます。
&lt;/p
&gt;&lt;p&gt;Gaucheのコンパイラは&lt;strong&gt;仮想マシンコンパイラ&lt;/strong
&gt;です。コンパイル後のコードは仮想マシンで実行されます。
&lt;/p
&gt;&lt;p&gt;Gaucheがどんな内部コードにコンパイルするかを確かめるには&lt;strong&gt;disasm&lt;/strong
&gt;手続きが使えます。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; disasm
 &lt;em&gt;#&amp;lt;closure disasm&amp;gt;&lt;/em
&gt;
 gosh&amp;gt; (disasm (lambda (x) (* x x)))
 &amp;#39;&amp;#39;main_code (name=#f, code=0x81eecf0, size=4, const=0, stack=1):
 &lt;em&gt;args: #f&lt;/em
&gt;
      &lt;em&gt;0 LREF0-PUSH               ; x&lt;/em
&gt;
      &lt;em&gt;1 LREF0                    ; x&lt;/em
&gt;
      &lt;em&gt;2 NUMMUL2                  ; (* x x)&lt;/em
&gt;
      &lt;em&gt;3 RET&lt;/em
&gt;
 &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
&lt;/pre
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>REPL</title
><link>http://karetta.jp/book-node/gauche-hacks/007590</link
><pubDate>Fri, 23 Mar 2007 13:54:50 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;REPLとはRead Eval Print Loopの略です。つまり、読み込み(read)、評価(eval)、印字(print)を繰り返す(loop)ということです。
&lt;/p
&gt;&lt;p&gt;ほとんどのScheme言語処理系では、REPLを対話型インタプリタで提供しています。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>処理系</title
><link>http://karetta.jp/book-node/gauche-hacks/014474</link
><pubDate>Fri, 23 Mar 2007 13:52:18 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;ここではGaucheの言語処理系や動作環境についてとりあげます。
&lt;/p
&gt;&lt;p&gt;対話型インタプリタ(REPL)の使い方や、Gaucheがどのように動作しているか(コンパイラ、仮想マシン)、入出力をどう行うか、ライブラリを構築し利用するための仕組み(モジュールシステム)について説明します。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>継続渡しスタイル</title
><link>http://karetta.jp/book-node/gauche-hacks/007565</link
><pubDate>Wed, 16 Jan 2008 08:53:10 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;h3 id=&#39;H-olcqk8&#39;&gt;継続渡し形式
&lt;/h3
&gt;&lt;p&gt;階乗factから始めましょう。
&lt;/p
&gt;&lt;pre&gt;;; 階乗の普通の定義
(define (fact n)
  (if (= n 0)
      1
      (* n (fact (- n 1)))))
&lt;/pre
&gt;&lt;p&gt;上記の階乗factの定義は
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;引数nが0なら1を返す(基底条件)
&lt;/li
&gt;&lt;li&gt;それ以外ならn-1の階乗にnを掛けたものを返す(再帰条件)
&lt;/li
&gt;&lt;/ol
&gt;&lt;p&gt;となっています。&lt;br /&gt;
これをfactから直接値を返すのではなく、
返すべき値を継続手続きと呼ばれるものに渡します。
継続手続き自体は特別な関数ではありません。
単に、本来返り値を返すところで、その手続きに「あとはよろしく」と
残りの処理を委ねてしまいます。
そういう使い方をされる手続きのことです。
&lt;/p
&gt;&lt;pre&gt;;; 継続渡し形式の階乗の定義
(define (fact/cps n cont)
  (if (= n 0)
      (cont 1)
      (fact/cps (- n 1) (lambda (a) (cont (* n a))))))
&lt;/pre
&gt;&lt;p&gt;factとfact/cpsは何が違うのでしょうか?
あるいは、どういう関係にあるのでしょう?
&lt;/p
&gt;&lt;p&gt;例えば、
&lt;/p
&gt;&lt;pre&gt;(* 2 (fact 10))
&lt;/pre
&gt;&lt;p&gt;という計算は、fact/cpsを使えば次の式と等価です。
&lt;/p
&gt;&lt;pre&gt;(fact/cps 10 (lambda (a) (* 2 a)))
&lt;/pre
&gt;&lt;p&gt;最初のfact式の例で(fact 10)式の返り値を待ち受けている処理(続きの計算)は
(lambda (a) (* 2 a))で表現できるもの(返り値aをもらって2倍する処理)で、
それが、fact/cpsにとっての継続手続きになっています。&lt;br /&gt;
なんだ、やってることは同じじゃないか?と思われれば、
継続渡し形式はとりあえず理解できたと思って良いでしょう。
&lt;/p
&gt;&lt;p&gt;ただ、継続渡し形式では続きの計算を引数として渡しているので、
cont1 cont2 ... という具合に複数の続きの計算を渡すことが可能です。&lt;br /&gt;
例えば、
&lt;/p
&gt;&lt;pre&gt;;; 除算
(define (divide num den)
  (/ num den))
&lt;/pre
&gt;&lt;p&gt;この様に除算を定義するところを、以下の様に複数の継続手続きを渡せます。
&lt;/p
&gt;&lt;pre&gt;;; 継続渡し形式の除算
(define (divide/cps num den pass fail)
  (if (= den 0)
      (fail num den pass fail)
      (pass (/ num den))))
&lt;/pre
&gt;&lt;p&gt;passとfailは共に継続手続きです。
次の様な使用方法が考えられるでしょう。
&lt;/p
&gt;&lt;pre&gt;;; 非零の分母要求する除算
(divide/cps 10 0
            (lambda (a) (format #t &amp;quot;answer is ~a~%&amp;quot; a))
            (lambda (n d p f)
              (format #t &amp;quot;&amp;gt; &amp;quot;)
              (flush)
              (divide/cps n (read) p f)))
&lt;/pre
&gt;&lt;p&gt;これは分母が0である内はプロンプトを出して入力を促し、
非零の入力を受けとると除算を行って、
答を&amp;quot;answer is ...&amp;quot;という形式で表示してくれます。
passを成功継続、failを失敗継続と呼ぶことがありますが、
3つ以上の継続手続きを渡すことも可能なのはあきらかです。
&lt;/p
&gt;&lt;p&gt;それともう一つ、継続渡し形式で書けば、現在実装中の関数(今の場合階乗fact)が、
どういう形で結果を返すのが良いかの設計を遅らせることが出来ます。
最終的に印字させることにしたのなら、
&lt;/p
&gt;&lt;pre&gt;;; 結果の印字出力
(fact/cps 10 (lambda (a) (format #t &amp;quot;answer is ~a~%&amp;quot; a)))
&lt;/pre
&gt;&lt;p&gt;とすればいいし、そのまま返すだけであれば、
継続手続きとしてidentity(つまり(lambda (a) a))を渡すだけです。
&lt;/p
&gt;&lt;pre&gt;;; fact/cpsでfactを定義
(define (fact n)
  (fact/cps n identity))
&lt;/pre
&gt;&lt;h3 id=&#39;H-pvcm0v&#39;&gt;call/ccは普通の関数
&lt;/h3
&gt;&lt;p&gt;[[Scheme]]にはcall/ccという呪文があって、
大域脱出だの例外機構だのマルチスレッドさえ実装できる。
とんでもなく柔軟な制御を可能にするらしい。
&lt;/p
&gt;&lt;p&gt;そう聞いてネット上のドキュメントを漁った方も多いのではないでしょうか。&lt;br /&gt;
しかし、call/ccは決して怪しい動作をするものではなく、
ある意味、とても普通の関数です。&lt;br /&gt;
setjmp/longjmpのように、
ちょっと奇妙なジャンプをするようなものという先入観もこの際捨ててしまいましょう。
&lt;/p
&gt;&lt;p&gt;まず、このことを理解してもらうために、call/ccなどと省略せずに、
call-with-current-continuationという正式名称に注目しましょう。
きっと意識してなかった人も居るんじゃないでしょうか。
[[Scheme]]でファイルの入出力をしたことのある人なら
この名前の先頭についているcall-withというprefixを見てピンとくるでしょう。&lt;br /&gt;
そう、call/ccは他に類を見ない孤独で特殊な存在なんかではなく、
良く見かけるcall-with系関数の仲間なんです。
&lt;/p
&gt;&lt;h3 id=&#39;H-1ugaqxl&#39;&gt;call-with系関数
&lt;/h3
&gt;&lt;p&gt;いくつか具体的な関数を紹介して感覚をつかんでもらえればよいでしょう。
まず、あるファイルから読み込みを行う時に使う関数です。
&lt;/p
&gt;&lt;pre&gt;;; call-with-input-fileの使用例
(define (count-chars file-name)
  (call-with-input-file file-name
    (lambda (in)
      (let loop ((c (read-char in))
                 (count 0))
        (cond ((eof-object? c) count)
              (else (loop (read-char in) (+ count 1))))))))
&lt;/pre
&gt;&lt;p&gt;count-charsは引数として渡された文字列をファイル名とみなして、
そのファイルをオープンして一文字ずつ読み込み、文字数をカウントしています。&lt;br /&gt;
&lt;/p
&gt;&lt;p&gt;注目してもらいたい関数はcall-with-input-fileです。&lt;br /&gt;
この関数は、
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;第一引数のファイルをオープンして、入力ポートを作り出す。
&lt;/li
&gt;&lt;li&gt;入力ポートを引数にして第二引数の手続きを呼び出す。
&lt;/li
&gt;&lt;/ol
&gt;&lt;p&gt;という処理を行っています。
このcall-with-input-fileの処理はキモだけを抽出すると、次のコードになります。
&lt;/p
&gt;&lt;pre&gt;;; とんでもなく杜撰なcall-with-input-fileの実装(コアのみ)
(define (call-with-input-file file-name proc)
  (proc (open-input-file file-name)))
&lt;/pre
&gt;&lt;p&gt;つまり、call-with-input-fileは
&lt;strong&gt;ファイルから入力ポートを作り出し、それを渡してprocをcallする&lt;/strong
&gt;
という関数なのです。
call procedure with input fileという感じでしょうか。&lt;br /&gt;
実際にはclose-input-portで使い終わった入力ポートの後始末をする必要がありますが、
ここでは省略しています。
call-with-input-fileが別のprocedureをcallする、
という流れだけ理解してください。
要はfileをopenしたりcloseしたりのお膳立て(あるいは環境整備)はするけど、
自分では何も処理せずに、procedureにやらせるだけです。
&lt;/p
&gt;&lt;p&gt;では、出力の場合も見ておきましょう。
&lt;/p
&gt;&lt;pre&gt;;; call-with-output-fileの使用例
(define (write-message file-name)
  (call-with-output-file file-name
    (lambda (out)
      (format out &amp;quot;I&amp;#39;m Schemer&amp;quot;)
      (newline out)
      (flush out))))
&lt;/pre
&gt;&lt;p&gt;やはり注目してもらいたいのは、call-with-output-fileの使い方です。
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;第一引数のファイルをオープンして出力ポートを作り出す。
&lt;/li
&gt;&lt;li&gt;出力ポートを引数にして第二引数の手続きを呼び出す。
&lt;/li
&gt;&lt;/ol
&gt;&lt;p&gt;すでに説明したcall-with-input-fileと見比べてください。
&lt;/p
&gt;&lt;p&gt;もちろん処理の方もほとんど同じで次の様になります。
&lt;/p
&gt;&lt;pre&gt;;; とんでもなく杜撰なcall-with-output-fileの実装(コアのみ)
(define (call-with-output-file file-name proc)
  (proc (open-output-file file-name)))
&lt;/pre
&gt;&lt;p&gt;call-with-output-fileもcall procedure with output fileと読めるでしょう。&lt;br /&gt;
これも実際にはclose-output-portで使用済みのポートを後始末するべきです。
ここでは代わりに、write-message中でflushすることで書き出していますが、
あくまで簡単にしてキモだけ見てもらうためです。
やはり、call-with-output-fileが別のprocedureをcallしているという
流れだけ理解してもらえれば十分です。
&lt;/p
&gt;&lt;p&gt;処理系によっては、これ以外にも文字列ポートやソケットなど
多くのcall-with系の関数が標準で提供されているものもありますが、
基本はどれも同じです。&lt;br /&gt;
[[Gauche]]であれば、replに対して次の様にすれば仲間が確認できます。
&lt;/p
&gt;&lt;pre&gt;;; call-withのゆかいな仲間たち
gosh&amp;gt; (apropos &amp;#39;call-with)
call-with-current-continuation (scheme)
call-with-input-file           (scheme)
call-with-input-file           (user)
call-with-input-string         (gauche)
call-with-output-file          (scheme)
call-with-output-file          (user)
call-with-output-string        (gauche)
call-with-string-io            (gauche)
call-with-values               (scheme)
gosh&amp;gt; 
&lt;/pre
&gt;&lt;p&gt;call-with-string-ioの様にprocに入力と出力の2つのポートを渡すものもありますが、
call-with系の関数はprocをcallする関数であるという点では変わりません。&lt;br /&gt;
その時に&lt;strong&gt;procに何を渡すかによってバリエーションがあるだけ&lt;/strong
&gt;なのです。
&lt;/p
&gt;&lt;h3 id=&#39;H-1a5ullu&#39;&gt;call-with-procedure
&lt;/h3
&gt;&lt;p&gt;call-with系の関数は、一般化して言うなら、
&lt;strong&gt;fooの素からfooを生成し、procにfooを渡して呼び出す&lt;/strong
&gt;
関数ということになるでしょう。&lt;br /&gt;
call-with-input-fileやcall-with-output-fileでは、
fooは入力ポートや出力ポートであり、
「fooの素」は文字列で与えられたファイル名ということになります。
call-with-string-ioならfooは&lt;em&gt;入力ポートと出力ポートの組&lt;/em
&gt;です。
&lt;/p
&gt;&lt;p&gt;では、仮にcall-with-procedureという関数を作るとしたら、
どんな風になるかを考えてみましょう。&lt;br /&gt;
call-with-procedureという名前からすれば、
&lt;strong&gt;procedureの素からprocedureを生成し、procにprocedureを渡して呼び出す&lt;/strong
&gt;
という関数になりますね。
まぎらわしいがprocはcall-with-procedureの第二引数で与えられる手続きです。
procedureは、そのcallされるprocという手続きに渡される引数で、fooにあたります。
ちゃんと区別してください。&lt;br /&gt;
&lt;/p
&gt;&lt;p&gt;まず、procedureの素とは何で表現されるべきでしょうか?&lt;br /&gt;
そう。lambda式を使うのが妥当でしょう。
そうすると、次の様に言い換えられそうです。
&lt;strong&gt;lambda式からprocedureを生成し、procにprocedureを渡して呼び出す&lt;/strong
&gt;
&lt;/p
&gt;&lt;pre&gt;;; call-with-procedureの実装
(define (call-with-procedure lambda-expr proc)
  (proc lambda-expr))
&lt;/pre
&gt;&lt;p&gt;例えばこんな風に使えるようになるでしょう。
&lt;/p
&gt;&lt;pre&gt;;; (* 2 (fact 10))
(call-with-procedure (lambda (a) (* 2 a)) (lambda (p) (p (fact 10))))
&lt;/pre
&gt;&lt;p&gt;もちろん、こんな風に渡されたprocedureそのものを返してもいいですし、
&lt;/p
&gt;&lt;pre&gt;;; 継続手続きを返すのみ
(call-with-procedure (lambda (a) (* 2 a)) (lambda (p) p))
&lt;/pre
&gt;&lt;p&gt;新たに関数合成したものを返してもかまいません。
&lt;/p
&gt;&lt;pre&gt;;; 継続手続きを関数合成して、出来た関数を返す。
(call-with-procedure (lambda (a) (* 2 a)) (lambda (p) (compose p p)))
&lt;/pre
&gt;&lt;p&gt;渡されたprocedureを手続きとして使わなければならない法などありません。
単にリストにして返すことだって一向にさしつかえありません、
&lt;/p
&gt;&lt;pre&gt;;; 継続手続きをリストにして返す
(call-with-procedure (lambda (a) (* 2 a)) (lambda (p) (list p p)))
&lt;/pre
&gt;&lt;p&gt;それどころか、そもそも引数を使わなくても別に文句を言われる筋合いはありません。
&lt;/p
&gt;&lt;pre&gt;;; 継続手続きを使わない
(call-with-procedure (lambda (a) (* 2 a)) (lambda (p) &amp;quot;Hello World!&amp;quot;))
&lt;/pre
&gt;&lt;p&gt;これはcall-with-input-fileやcall-with-output-fileでも言えることで、
渡されたinやoutなどのポートを使わなくたって一向にかまいません。
まるで意味はないですが。
&lt;/p
&gt;&lt;h3 id=&#39;H-mm3ac&#39;&gt;call-with-continuation-procedure
&lt;/h3
&gt;&lt;p&gt;では、もう一歩進めてcall-with-continuation-procedureを定義してみましょう。
&lt;/p
&gt;&lt;pre&gt;;; call-with-continuation-procedureの定義
(define (call-with-continuation-procedure cont proc)
  (proc cont))
&lt;/pre
&gt;&lt;p&gt;どうでしょうか?&lt;br /&gt;
call-with-procedureと変わらないじゃないかって?&lt;br /&gt;
それはその通りです。
すでに説明した通り、継続手続きなんてのはただの関数なんだから当然です。
せいぜいcall-with-continuation-procedureという名前から、
contというlambda式によって生成される手続きが
(リストのメンバや関数合成の素などでなく)
&lt;strong&gt;procの中で継続手続きとして&lt;/strong
&gt;使われるんだろう、
と期待できる程度のことでしかないのです。
&lt;/p
&gt;&lt;h3 id=&#39;H-slndt6&#39;&gt;call-with-current-continuation
&lt;/h3
&gt;&lt;p&gt;では、本題のcall/cc、つまりcall-with-current-continuationに話を移すことにしましょう。
といっても、もうほとんどゴールに辿り着いていますが。
&lt;/p
&gt;&lt;p&gt;先程説明したcall-with-continuation-procedureは、
継続手続きをプログラマが自分でlambda式として書いています。
&lt;/p
&gt;&lt;pre&gt;(* 2 (fact 10))
&lt;/pre
&gt;&lt;p&gt;この式の部分式である(fact 10)から見ると、(fact 10)式の続きの計算は
(lambda (a) (* 2 a))になります。
それを継続手続きとして渡してやると考えれば、
&lt;/p
&gt;&lt;pre&gt;;; (* 2 (fact 10))
(call-with-continuation-procedure (lambda (a) (* 2 a))
                                  (lambda (cont) (cont (fact 10))))
&lt;/pre
&gt;&lt;p&gt;こんな風に書けることになります。
(fact 10)の続きの計算を手動で作成している点が面倒ですが、
このレベルならまだ書けなくはないでしょう。
仮に、factがすでに定義済みなら、この式はfact/cpsの定義にできます。
&lt;/p
&gt;&lt;pre&gt;;; fact/cpsをcall-with-continuation-procedureを使って定義する
(define (fact/cps n cont)
  (call-with-continuation-procedure cont (lambda (c) (c (fact 10)))))
&lt;/pre
&gt;&lt;p&gt;ここまでは分かってもらえたでしょうか?
&lt;/p
&gt;&lt;p&gt;さて、続きの計算はいたるところにあります。
あらゆる評価の時点において、その続きの計算というものが存在します。
しかし、その&lt;em&gt;時点&lt;/em
&gt;を特定すれば、
続きの計算は自動的に(一意に)決まることになります。&lt;br /&gt;
今の例で言えば、(fact 10)式の評価の時点において、
その続きの計算、すなわち継続は(lambda (a) (* 2 a))となります。
だとすれば、現在の継続に限定するなら、
プログラマが第一引数のlambda式を書く必要などないはずです。
&lt;/p
&gt;&lt;pre&gt;(* 2 (call-with-continuation-procedure (lambda (* 2 a))
                                       (lambda (cont) (cont (fact 10)))))
&lt;/pre
&gt;&lt;p&gt;と書く代わりに、call-with-continuation-procedure式の返りを待っている
続きの計算、つまり(lambda (a) (* 2 a))を自動的に取り出し、
call-with-continuation-procedureの第一引数に渡してもらえれば簡単になります。
それが、call-with-current-continuation、いわゆるcall/ccなのです。
&lt;/p
&gt;&lt;p&gt;つまり、call/ccではこうなります。
&lt;/p
&gt;&lt;pre&gt;(* 2 (call/cc (lambda (cont) (cont (fact 10)))))
&lt;/pre
&gt;&lt;p&gt;contには(lambda (a) (* 2 a))が渡ってくると考えられますね。
fooの素に相当する現在の継続の素を改めて書く必要はなくなりました。
&lt;/p
&gt;&lt;pre&gt;;; call/ccの継続渡し形式への書き換えを試みてみる
(* 2 ((lambda (cont) (cont (fact 10)))
      (lambda (a) (* 2 a))))
&lt;/pre
&gt;&lt;p&gt;しかし、よくよく考えると、(* 2 ...)式の続きの計算はどうなるのでしょう?&lt;br /&gt;
厳密に考えれば、read-eval-print-loopがあるはずですから、
次のread-eval-printが延々待ってるハズです。
これは以降の説明でも重要な点で、無視できないので仕方がありません。
PRINT-AND-NEXT-REPLというものを便宜的に持ち込むことにしましょう。
というわけで、次のようにすることにします。
&lt;/p
&gt;&lt;pre&gt;;; call/ccの継続渡し形式への書き換えを試みてみる(PRINT-AND-NEXT-REPL導入版)
(* 2 ((lambda (cont) (cont (fact 10)))
      (lambda (a) (PRINT-AND-NEXT-REPL (* 2 a)))))
&lt;/pre
&gt;&lt;p&gt;call/cc式の外側にある2倍を行う処理もそのまま残留しています。
もし、
&lt;/p
&gt;&lt;pre&gt;;; 継続渡し形式への変換と混同しちゃった版
((lambda (cont) (cont (fact 10)))
 (lambda (a) (PRINT-AND-NEXT-REPL (* 2 a))))
&lt;/pre
&gt;&lt;p&gt;という風な継続渡し形式への変換を予想された方は注意してください。
call/ccは継続渡し形式に変換する関数ではなく、
あくまで、call/cc式自身の継続(自分の値を待っている計算)を自動的に作り、
それをcall/ccの引数であるprocedureに渡して、
そのprocedureをcallするという関数です。
(分からなくなったら、call-with-input-fileに戻ってください。
入力ポートの生成が継続の生成に置き換わっただけです。)
&lt;/p
&gt;&lt;p&gt;contに(lambda (a) (PRINT-AND-NEXT-REPL (* 2 a)))を束縛して適用すれば、
&lt;/p
&gt;&lt;pre&gt;(* 2 ((lambda (a) (PRINT-AND-NEXT-REPL (* 2 a))) (fact 10)))
&lt;/pre
&gt;&lt;p&gt;となり、さらに評価すれば以下の様になります。
&lt;/p
&gt;&lt;pre&gt;(* 2 (PRINT-AND-NEXT-REPL (* 2 (fact 10))))
&lt;/pre
&gt;&lt;p&gt;この処理は(* 2 (fact 10))の結果を印字したら次のread-eval-print-loopに行きます。
一番外の2倍をする処理は行われません。
もし、PRINT-AND-NEXT-REPLが無ければ、処理されてしまい結果は違ってきます。
継続渡し形式への変換では導入してなかったものを、
このタイミングで導入した理由が分かってもらえたでしょうか。
&lt;/p
&gt;&lt;p&gt;ここで振り返ってもらいたいのですが、決っしてcall/cc式自体は勝手にジャンプしたり、
妙な制御構文になっていたりしていないのです。
他のcall-with系の関数となんら変わらない評価をおこなっています。&lt;br /&gt;
ただ、callされた手続きに継続が渡ってきて、その継続が手続きとして使われたら、
(そう、使わなくたって一向に構わないってことに注意しましょう。
使われなければ、call/cc式は単に値を返すだけです。
それはcall-with-input-fileなど他の全ての関数となんら変わりません。)
その継続の呼び出しによりジャンプするように見えるかもしれません。
しかし、それだってcall/ccに限った話ではなく、
他の関数を呼び出すところでは常に起こっていることなのです。
call/cc式自体はどこにもジャンプなどしません。
&lt;/p
&gt;&lt;p&gt;今度はその渡ってきた継続を使わない例で動作を追ってみましょう。
&lt;/p
&gt;&lt;pre&gt;(+ 1 (call/cc (lambda (cont) 2)) 3)
&lt;/pre
&gt;&lt;p&gt;この式でcontは(lambda (a) (PRINT-AND-NEXT-REPL (+ 1 a 3)))になります。
call/cc式はこの継続を渡して(lambda (cont) 2)をcallします。
&lt;/p
&gt;&lt;pre&gt;(+ 1 ((lambda (cont) 2) (lambda (a) (PRINT-AND-NEXT-REPL (+ 1 a 3)))) 3)
&lt;/pre
&gt;&lt;p&gt;やはり機械的に変換され、contは使われないので
&lt;/p
&gt;&lt;pre&gt;;; 結局、継続を使わなかった
(+ 1 ((lambda (_) 2) _) 3)
&lt;/pre
&gt;&lt;p&gt;となり、結果としては(+ 1 2 3)から6が返ります。
&lt;/p
&gt;&lt;h3 id=&#39;H-12i8nmk&#39;&gt;評価順序と継続
&lt;/h3
&gt;&lt;p&gt;継続を保存しておいて、あとからその継続を呼び出すまでの間に、
環境の中の値を書き換えた場合はどうなるのでしょうか?&lt;br /&gt;
継続がなんとなく分かってくると、今度はそういう疑問を持つようになるようです。
&lt;/p
&gt;&lt;p&gt;結論から言えば、当然環境の値が変わってれば変わっていますし、
その環境のどこにも過去の値の履歴なんかは残っていません。
[[Gauche]]の様にCLOS系のオブジェクトシステムを持つ処理系で、
オブジェクトのスロット値を書き換えた場合も当然同じです。
&lt;/p
&gt;&lt;p&gt;しかし、(変更前の)過去の値が取得できたんだけどなぁ?
と首を捻るような経験をした人もいるでしょう。&lt;br /&gt;
その謎を解くには、今まで説明してきた評価の流れを
もう少し注意深く考えて修正する必要があります。
&lt;/p
&gt;&lt;pre&gt;;; 環境破壊実験の準備

(define k #f)  ;; 継続保存用

(define x 1)   ;; (大域)環境に定義

(+ x (call/cc (lambda (cont) (set! k cont) 2)) 3)
&lt;/pre
&gt;&lt;p&gt;ここでcontに渡る継続はどう表現されるものになるでしょうか?
ここまでの話についてこれた方なら機械的に変換してほとんど即答できますね。
(lambda (a) (PRINT-AND-NEXT-REPL (+ x a 3)))です。
(当然kにも同じ継続が保存されています)&lt;br /&gt;
保存した継続はこんな風に使えます。
&lt;/p
&gt;&lt;pre&gt;;; まず動作確認
(k 2) =&amp;gt; (+ 1 2 3) =&amp;gt; 6
(k 20) =&amp;gt; (+ 1 20 3) =&amp;gt; 24
&lt;/pre
&gt;&lt;p&gt;kが(lambda (a) (PRINT-AND-NEXT-REPL (+ x a 3)))なのですから
この評価結果は理解できますね。
&lt;/p
&gt;&lt;p&gt;では、xの値を書き換えてみましょう。
&lt;/p
&gt;&lt;pre&gt;;; いざ環境破壊
(set! x 10)
&lt;/pre
&gt;&lt;p&gt;さて、準備が出来ました。
この状態で(k 2)などとするとどうなるでしょう?
&lt;/p
&gt;&lt;pre&gt;;; 環境破壊の影響を観測してみる
(k 2)
&lt;/pre
&gt;&lt;p&gt;実は結果は処理系によって違うかもしれないのですが、
現在の[[Gauche]]では6になります。
&lt;/p
&gt;&lt;p&gt;ちょっと奇妙な現象ですね。
これは(+ 1 2 3)の結果です。
つまり、xの値は書き換え前の値1のままで、
なんだか過去に戻ったかの様な錯覚に陥いります。
この経験がおそらく最初の疑問になるのでしょう。
&lt;/p
&gt;&lt;p&gt;ではもう少し変更して、振る舞い調べてみましょう。
&lt;/p
&gt;&lt;pre&gt;;; 環境破壊実験の準備2
(define k #f)  ;; 継続保存用

(define x 1)   ;; (大域)環境に定義
(define z 3)   ;; (大域)環境に定義

(+ x (call/cc (lambda (cont) (set! k cont) 2)) z)
&lt;/pre
&gt;&lt;p&gt;この後で、今度はxとzを変更してみることにします。
&lt;/p
&gt;&lt;pre&gt;;; 環境破壊
(set! x 10)
(set! z 30)
&lt;/pre
&gt;&lt;p&gt;これならどうでしょう?&lt;br /&gt;
&lt;/p
&gt;&lt;pre&gt;;; 環境破壊の結果は?
(k 20)
&lt;/pre
&gt;&lt;p&gt;これも結果は処理系によって違ってきますが、現在の[[Gauche]]では51を返します。
これは(+ 1 20 30)の結果です。
つまり、xは変更前の過去の値が、zは変更後の新しい値が得られていることになります。
どうでしょう、これなら分かるのではないでしょうか。
&lt;/p
&gt;&lt;p&gt;保存した継続kは今までの説明のまま機械的に変換すると、
(lambda (a) (PRINT-AND-NEXT-REPL (+ x a z)))になるはずなのですが、
これは正確ではなかったということです。&lt;br /&gt;
&lt;/p
&gt;&lt;pre&gt;;; 実は不正確だった継続
(lambda (a) (PRINT-AND-NEXT-REPL (+ x a z)))
&lt;/pre
&gt;&lt;p&gt;正しくは、(今の[[Gauche]]では)継続kは
(lambda (a) (PRINT-AND-NEXT-REPL (+ &lt;strong&gt;1&lt;/strong
&gt; a z)))だったのです。
なぜなら、call/cc式を評価する前にxは評価されて1を得ていたからです。
&lt;/p
&gt;&lt;pre&gt;;; より正確な継続
(lambda (a) (PRINT-AND-NEXT-REPL (+ 1 a z)))
&lt;/pre
&gt;&lt;p&gt;つまり、[[Gauche]]では左から順に引数を評価するために、
&lt;/p
&gt;&lt;pre&gt;;; 引数の評価順とcall/ccの位置
;; (1) xが評価される
;; (2) call/cc式が評価される
;; (3) zが評価される
;; (4) +が3つの値に適用される

(+ x call/cc式 z)
&lt;/pre
&gt;&lt;p&gt;のcall/cc式を評価する前に、すでにxの値が環境から取り出されて1になっていて、
call/cc式の継続(続きの計算)には
「環境からxの値を取り出す」という計算は含まれてないのです。
だから継続kにはxは関係ないし、1がxの値だったということも知らないと思っていいでしょう。
逆に「zの値を環境から取り出す」という計算は含まれていますから、
継続を呼び出すとその時点でのzの値である30を取り出すわけです。
&lt;/p
&gt;&lt;p&gt;もっと言えば、(lambda (a) (PRINT-AND-NEXT-REPL (&lt;strong&gt;#&amp;lt;subr +&amp;gt;&lt;/strong
&gt; 1 a z)))で、
もし、継続を保存後に(set! + *)とか(set! + max)などと変更しても、
やはり加算を行ないます。
いまや+は#&amp;lt;subr *&amp;gt;や#&amp;lt;subr max&amp;gt;になっているにもかかわらずです。
(つまり、すでに加算を行う気マンマンでcall/cc式の評価に突入してたのです。
同じ様に言えば、継続は「1とaとzを加算する気マンマン」なのであって、
「xとaとzを+するつもり」だったわけではありません。)
&lt;/p
&gt;&lt;p&gt;結局環境を書き換えれば、当然変わっているのですが、
「環境から値を取り出すという計算」がcall/cc式の評価前に済んでしまっているか、
それとも継続(続きの計算)に含まれていて、
継続起動後に処理されるかが差を生むだけのことなのです。
&lt;/p
&gt;&lt;p&gt;当然オブジェクトのスロット値を書き換えている場合も同様です。
そのスロットの値を取り出す前にcall/cc式が評価されるのか、
それともcall/cc式の継続に含まれるのかが鍵を握ります。
&lt;/p
&gt;&lt;p&gt;くどいようですが、継続とは続きの計算のことで、
重要なのは&lt;strong&gt;どの時点の&lt;/strong
&gt;継続かということです。
ある時点の続きにはどういう計算が含まれるかを理解せずに
継続を使いこなすのは難しいと思ってください。
&lt;/p
&gt;&lt;p&gt;実は[[Scheme]]では引数の評価順序は未定義なので、
そもそも上の例のような場所でcall/ccを使うのは正しくありません。
(実際、処理系によっては違う結果を得てしまった人も居るはずです。
これは移植性のないコードを書いていることの証拠です。)
しかし、どういう順序でどんな計算が進むかの知識が重要であることは変わりません。
&lt;/p
&gt;&lt;p&gt;その意味では、[[Scheme]]は比較的評価規則は単純で、
演算の優先順位などもないので動作を読みやすくなってます。
継続を学ぶにはもってこいの言語なのです。
しかし、一旦マクロなどで評価規則が複雑なものを導入して、
それと併せて使うと、その動作を予測するのは急に困難になります。
&lt;/p
&gt;&lt;p&gt;環境を書き換えずに継続を呼んでも、
なんら変化の無い計算を繰り返すだけなのであまり意味がありません。&lt;br /&gt;
(継続に渡す引数を変えれば、それなりの変化は楽しめますが。)&lt;br /&gt;
とすれば、継続を使う局面では環境の書き換えと無縁ではいられませんし、
実際処理系が値の書き換えを許している以上は、評価順序に詳しくなければ
使いこなせないということになります。
(生成した継続自身を外側の環境に保存したり、
新しく生成した継続で書き換えるような環境の変更も良く見かけます。)
そう考えると、恐らく複雑な構文や演算子の優先順などがある言語処理系で
継続を導入しても、結局誰にも使いこなせずに不要になってしまうかもしれません。
&lt;/p
&gt;&lt;p&gt;では、練習問題です。次はどうなるか確認してみてください。
&lt;/p
&gt;&lt;pre&gt;;; 環境破壊実験の準備3
(define k #f)  ;; 継続保存用

(define x 1)   ;; 環境に定義

((identity +) x (call/cc (lambda (cont) (set! k cont) 2)) x)
&lt;/pre
&gt;&lt;p&gt;あまり変わっていないですが、
&lt;/p
&gt;&lt;pre&gt;;; 環境破壊
(set! x 10)
(set! + max)

;; さて結果は?
(k 2)
&lt;/pre
&gt;&lt;p&gt;(k 2)を評価したらどうなるでしょう？&lt;br /&gt;
(lambda (a) (PRINT-AND-NEXT-REPL (#&amp;lt;subr +&amp;gt; 1 a x)))ですから13?&lt;br /&gt;
実は今の[[Gauche]]では
(lambda (a) (PRINT-AND-NEXT-REPL (&lt;strong&gt;(identity +)&lt;/strong
&gt; 1 a x)))なので10が返ってきます。
最初の(identity +)が引数の評価より後なのです。
実は+などの様な組込み関数を使わずに
ユーザ定義関数を使う場合にも最後に評価されます。
というか、むしろ実際には逆で組込み関数の場合に最適化のために
引数が評価されるより前に#&amp;lt;subr +&amp;gt;に評価済みになっているのです。
なお、この挙動は-fno-inlineオプションを付けてgoshを起動しても変わってきます。
&lt;/p
&gt;&lt;p&gt;継続は続きの計算です。
ならば、続きの計算が何であるかを知ることが
継続を使いこなす上での必須のスキルと言って良いでしょう。
仮に[[Scheme]]で継続を使いこなせても、別の言語処理系の上で使いこなすには、
その言語の評価順などの知識が必要になります。
もちろん継続についての理解はどこに行っても通用するので学ぶことは意義があります。
&lt;/p
&gt;&lt;h3 id=&#39;H-b7v83v&#39;&gt;call/ccパズル
&lt;/h3
&gt;&lt;p&gt;call/ccパズルというものがあります。
以前comp.lang.schemeというニュースグループで話題になったプログラムです。
このコードが何をするかを予想してみろ、というのですがそう簡単ではありません。
&lt;/p
&gt;&lt;pre&gt;;; call/ccパズルお題
(let* ((yin ((lambda (foo) (newline) foo)
             (call/cc (lambda (bar) bar))))
       (yang ((lambda (foo) (write-char #\*) foo)
              (call/cc (lambda (bar) bar)))))
  (yin yang))
&lt;/pre
&gt;&lt;p&gt;しかし、このパズルの動作を理解するのはちょっとした自信にもなると思いますので
ここでは少し説明してみることにします。
&lt;/p
&gt;&lt;p&gt;まずコードの本質的な部分を剥き出しにするために少しだけ簡略化しましょう。
&lt;/p
&gt;&lt;p&gt;2行目の(lambda (bar) bar)はcall/ccが自動的に作成した継続をそのまま返しています。
いわゆるidentityという関数です。
それが、1行目のlambda式のfooに束縛され、ここでもそのままfooが返されています。
最終的には、それがyinに束縛されます。&lt;br /&gt;
つまりyinにはcall/ccが作成した継続が束縛されます。
従って((lambda (foo) (newline) foo) (call/cc (lambda (bar) bar)))の部分は
(call/cc identity)と置き換えても動作そのものは変わりません。
なぜわざわざこんなややこしいことをしているかと言うと、
一種のデバッグプリントのためです。
&lt;/p
&gt;&lt;p&gt;つまり、このcall/ccで作成した継続がどこかで(あるいはどこであれ)
callされると(newline)を通過するので、
改行が表示されることで継続が起動された瞬間が目に見えるわけです。
このことは、
&lt;/p
&gt;&lt;pre&gt;;; 最初の束縛部分に注目
(EXTERNAL-EXPR ((lambda (foo) (newline) foo) (call/cc (lambda (bar) bar))))
&lt;/pre
&gt;&lt;p&gt;で、call/ccが作成する継続が、
&lt;/p
&gt;&lt;pre&gt;;; 最初のcall/cc式の継続
(lambda (a)
  (EXTERNAL-EXPR ((lambda (foo) (newline) foo) a)))
&lt;/pre
&gt;&lt;p&gt;となることからも分かります。
見ての通り(newline)が続きの計算に含まれています。
&lt;/p
&gt;&lt;p&gt;同じ様にyangの方も4行目のcall/ccで作成された継続がcallされると
&amp;#39;*&amp;#39;という文字が印字されることで継続が起動されたことを教えてくれます。
&lt;/p
&gt;&lt;p&gt;これは継続が起動された瞬間が見えるので、
&lt;strong&gt;call/cc(に限らず大抵の式)のデバッグに使える一般的な手法&lt;/strong
&gt;です。
是非覚えておきましょう。&lt;br /&gt;
(もちろんcall/cc式が素直に値を返した場合にも通過します。)
ともかく、ここを省略するとコードは少し簡単になって次の様になります。
&lt;/p
&gt;&lt;pre&gt;;; STEP 0
(let* ((yin (call/cc identity))
       (yang (call/cc identity)))
  (yin yang))
&lt;/pre
&gt;&lt;p&gt;これならなんとか全体が見通せるでしょう。
&lt;/p
&gt;&lt;p&gt;ポイントは、&lt;strong&gt;let*のローカル束縛の評価順序&lt;/strong
&gt;です。&lt;br /&gt;
yinに束縛される継続l#&amp;lt;continuation yin&amp;gt;は
&lt;/p
&gt;&lt;pre&gt;;; #&amp;lt;continuation yin&amp;gt;
(lambda (a)
  (PRINT-AND-NEXT-REPL
   (let* ((yin a)
          (yang (call/cc identity)))
     (yin yang))))
&lt;/pre
&gt;&lt;p&gt;一方yangに束縛される継続#&amp;lt;continuation yang&amp;gt;は不正確ですが、
&lt;/p
&gt;&lt;pre&gt;;; #&amp;lt;continuation yang&amp;gt;っぽいもの
(lambda (a)
  (PRINT-AND-NEXT-REPL
   (let* ((yin #&amp;lt;continuation yin&amp;gt;)
          (yang a))
     (yin yang))))
&lt;/pre
&gt;&lt;p&gt;正しくするなら、ちょっと形が変わってしまうが仕方がありません。
&lt;/p
&gt;&lt;pre&gt;;; #&amp;lt;continuation yang&amp;gt;
(lambda (a)
  (PRINT-AND-NEXT-REPL
   (let ((yang a))
     (#&amp;lt;continuation yin&amp;gt; yang))))
&lt;/pre
&gt;&lt;p&gt;こうなると思って良さそうです。
&lt;/p
&gt;&lt;p&gt;yinに束縛される継続には
「新たに継続を作成してyangに束縛するという計算」が含まれていますが、
yangに束縛される継続ではyinはすでに無く、作成済みの継続が見えます。
その意味でyinとyangは対等ではありません。&lt;br /&gt;
この立場の違いは、そのまま評価順序からきています。
&lt;/p
&gt;&lt;p&gt;では、(yin yang)を評価してみましょう。
おっと、その前に確認しておきます。
そもそも、ここに至るまでに、yinのためのcall/ccが評価されるので、
この段階で&lt;strong&gt;改行が一発&lt;/strong
&gt;入ります。
yangのためのcall/ccも評価されるので、&lt;strong&gt;*が一発&lt;/strong
&gt;入っているのでお忘れなく。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*I
&lt;/pre
&gt;&lt;p&gt;では、気を取り直して(yin yang)の評価です。
&lt;/p
&gt;&lt;pre&gt;;; STEP 1
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yan&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;では、#&amp;lt;continuation yin&amp;gt;だけ上記の継続手続きで置き換えてみると次式になります。
&lt;/p
&gt;&lt;pre&gt;;; STEP 1&amp;#39;
((lambda (a)
   (PRINT-AND-NEXT-REPL
    (let* ((yin a)
           (yang (call/cc identity)))
      (yin yang))))
 #&amp;lt;continuation yang&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;では適用してみましょう。
&lt;/p
&gt;&lt;pre&gt;;; STEP 2
(PRINT-AND-NEXT-REPL
 (let* ((yin #&amp;lt;continuation yang&amp;gt;)
        (yang (call/cc identity)))
   (yin yang)))
&lt;/pre
&gt;&lt;p&gt;今度はこの評価をすることになります。
yinには先程と同じ#&amp;lt;continuation yang&amp;gt;が束縛されます。
#&amp;lt;continuation yin&amp;gt;ではないので注意してください。
そして、#&amp;lt;continuation yang&amp;gt;がyinに束縛される所で、&lt;strong&gt;改行が入ります&lt;/strong
&gt;。
元コードでは、((lambda (a) (newline) a) #&amp;lt;continuation yang&amp;gt;)だからです。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*
I
&lt;/pre
&gt;&lt;p&gt;次は、call/cc式が評価されて新たな継続#&amp;lt;continuation yang&amp;#39;&amp;gt;が得られます。
新たに作られるので&amp;#39;(ダッシュ)を付けておきました。
区別さえできるなら、インデックスを振っても良いでしょう。
ともあれ、call/cc式が評価された直後はこうなります。
&lt;/p
&gt;&lt;pre&gt;;; STEP 3
(PRINT-AND-NEXT-REPL
 (let ((yang #&amp;lt;continuation yang&amp;#39;&amp;gt;))
   (#&amp;lt;continuation yang&amp;gt; yang)))
&lt;/pre
&gt;&lt;p&gt;元々のコードで言えば、yangに束縛される時点で&lt;strong&gt;*が表示される&lt;/strong
&gt;はずです。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*
*I
&lt;/pre
&gt;&lt;p&gt;#&amp;lt;continuation yang&amp;#39;&amp;gt;を継続手続き風に書くとどうなるでしょう?
&lt;/p
&gt;&lt;pre&gt;;; #&amp;lt;continuation yang&amp;#39;&amp;gt;っぽいもの
(lambda (a)
  (PRINT-AND-NEXT-REPL
   (let* ((yin #&amp;lt;continuation yang&amp;gt;)
          (yang a))
     (yin yang))))
&lt;/pre
&gt;&lt;p&gt;正確には、
&lt;/p
&gt;&lt;pre&gt;;; #&amp;lt;continuation yang&amp;#39;&amp;gt;っぽいもの
(lambda (a)
  (PRINT-AND-NEXT-REPL
   (let ((yang a))
     (#&amp;lt;continuation yang&amp;gt; yang))))
&lt;/pre
&gt;&lt;p&gt;では、(yin yang)を評価します。
&lt;/p
&gt;&lt;pre&gt;;; STEP 4
(#&amp;lt;continuation yang&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;#&amp;lt;continuation yang&amp;gt;を継続手続き風の式で置き換えます。
&lt;/p
&gt;&lt;pre&gt;;; STEP 4&amp;#39;
((lambda (a)
   (PRINT-AND-NEXT-REPL
    (let ((yang a))
      (#&amp;lt;continuation yin&amp;gt; yang))))
 #&amp;lt;continuation yang&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;これを適用すると、
&lt;/p
&gt;&lt;pre&gt;;; STEP 5
(PRINT-AND-NEXT-REPL
 (let ((yang #&amp;lt;continuation yang&amp;#39;&amp;gt;))
   (#&amp;lt;continuation yin&amp;gt; yang&amp;#39;)))
&lt;/pre
&gt;&lt;p&gt;今度はyangには#&amp;lt;continuation yang&amp;gt;が束縛されますが、ここで&lt;strong&gt;*が入ります&lt;/strong
&gt;。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*
**I
&lt;/pre
&gt;&lt;p&gt;すると、次に評価すべき式は
&lt;/p
&gt;&lt;pre&gt;;; STEP 6
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;となり、同様に#&amp;lt;continuation yin&amp;gt;を継続手続き風の式に置き換えると、
&lt;/p
&gt;&lt;pre&gt;;; STEP 6&amp;#39;
((lambda (a)
   (PRINT-AND-NEXT-REPL
    (let* ((yin a)
           (yang (call/cc identity)))
      (yin yang))))
 #&amp;lt;continuation yang&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;適用すれば、
&lt;/p
&gt;&lt;pre&gt;;; STEP 7
(PRINT-AND-NEXT-REPL
 (let* ((yin #&amp;lt;continuation yang&amp;#39;&amp;gt;)
        (yang (call/cc identity)))
   (yin yang)))
&lt;/pre
&gt;&lt;p&gt;ここで、yinに束縛される瞬間&lt;strong&gt;改行が入り&lt;/strong
&gt;、
さらに新たな継続#&amp;lt;continuatiogn yang&amp;#39;&amp;#39;&amp;gt;を生成してyangに束縛するところで
&lt;strong&gt;*が入ります&lt;/strong
&gt;。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*
**
*I
&lt;/pre
&gt;&lt;p&gt;では、引き続き評価しましょう。
&lt;/p
&gt;&lt;pre&gt;;; STEP 8
(#&amp;lt;continuation yang&amp;#39;&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;繰り返しで、だいぶ退屈してきたかもしれないが続けます。
&lt;/p
&gt;&lt;pre&gt;;; STEP 8&amp;#39;
((lambda (a)
  (PRINT-AND-NEXT-REPL
   (let ((yang a))
     (#&amp;lt;continuation yang&amp;gt; yang))))
 #&amp;lt;continuagion yang&amp;#39;&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;適用すると、
&lt;/p
&gt;&lt;pre&gt;;; STEP 9
(PRINT-AND-NEXT-REPL
 (let ((yang #&amp;lt;continuagion yang&amp;#39;&amp;#39;&amp;gt;))
   (#&amp;lt;continuation yang&amp;gt; yang)))
&lt;/pre
&gt;&lt;p&gt;yangに継続が束縛されるところで&lt;strong&gt;*が入ります&lt;/strong
&gt;。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*
**
**I
&lt;/pre
&gt;&lt;p&gt;次はこの式の評価です。
&lt;/p
&gt;&lt;pre&gt;;; STEP 10
(#&amp;lt;continuagion yang&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;#&amp;lt;continuagion yang&amp;gt;を継続手続き風の式で置換すると、
&lt;/p
&gt;&lt;pre&gt;;; STEP 10&amp;#39;
((lambda (a)
  (PRINT-AND-NEXT-REPL
   (let ((yang a))
     (#&amp;lt;continuation yin&amp;gt; yang))))
 #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;適用すると、
&lt;/p
&gt;&lt;pre&gt;;; STEP 11
(PRINT-AND-NEXT-REPL
 (let ((yang #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;))
   (#&amp;lt;continuation yin&amp;gt; yang)))
&lt;/pre
&gt;&lt;p&gt;yangに束縛されるところで、&amp;#39;&lt;em&gt;*が入ります&lt;/em
&gt;。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*
**
***I
&lt;/pre
&gt;&lt;p&gt;そして、再び、#&amp;lt;continuation yin&amp;gt;のcallになる。ということは改行が期待できますね。
&lt;/p
&gt;&lt;pre&gt;;; STEP 12
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;置き換えると、
&lt;/p
&gt;&lt;pre&gt;;; STEP 12&amp;#39;
((lambda (a)
  (PRINT-AND-NEXT-REPL
   (let* ((yin a)
          (yang (call/cc identity)))
     (yin yang))))
 #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;適用すると、
&lt;/p
&gt;&lt;pre&gt;;; STEP 13
(PRINT-AND-NEXT-REPL
 (let* ((yin #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
        (yang (call/cc identity)))
   (yin yang)))
&lt;/pre
&gt;&lt;p&gt;yinに束縛されるところで、&lt;strong&gt;改行が入り&lt;/strong
&gt;、
call/cc式を評価して新たな継続#&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;gt;が作成され、
yangに束縛されると、&lt;strong&gt;*が入ります&lt;/strong
&gt;。
ここまでで打ち止めとしましょう。
&lt;/p
&gt;&lt;pre&gt;;; ここまでの結果

*
**
***
*I
&lt;/pre
&gt;&lt;p&gt;このまま続ければ、一行毎に*の数が一個ずつ増加していくのが確認できます。
改行はyinへの束縛が起こった瞬間であり、*はyangへの束縛が起こった瞬間です。
上記の結果は、まさにそのデバッグプリントなのです。
&lt;/p
&gt;&lt;pre&gt;;; STEP 0 (再掲)
(let* ((yin (call/cc identity))    ;; call/cc-yin式
       (yang (call/cc identity)))  ;; call/cc-yang式
  (yin yang))
&lt;/pre
&gt;&lt;p&gt;印字出力を見落とさないように動作を追うのは大変ですが、
是非一度手を動かしてやってみてください。
ポイントは、次の点でしょうか。
&lt;/p
&gt;&lt;ol&gt;&lt;li&gt;改行や*が印字されるのは継続が束縛されるタイミングである。(デバッグプリント)
&lt;/li
&gt;&lt;li&gt;最初のcall/cc-yin式はただ一度だけ評価され、#&amp;lt;continuation yin&amp;gt;を生成し、
あとは同じ継続が使い回される。
&lt;/li
&gt;&lt;li&gt;次のcall/cc-yang式は#&amp;lt;continuation yin&amp;gt;がcallされた時にのみ、
評価されて新しい継続を生成する。
&lt;/li
&gt;&lt;/ol
&gt;&lt;p&gt;では、(yin yang)のトレースをまとめて眺めてみましょう。
&lt;/p
&gt;&lt;pre&gt;;; STEP 1
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yang&amp;gt;)
;; STEP 4
(#&amp;lt;continuation yang&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;gt;)
;; STEP 6
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;gt;)
;; STEP 8
(#&amp;lt;continuation yang&amp;#39;&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
;; STEP 10
(#&amp;lt;continuagion yang&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
;; STEP 12
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yang&amp;#39;&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yang&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yang&amp;#39;&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yang&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
;;
(#&amp;lt;continuation yin&amp;gt; #&amp;lt;continuation yang&amp;#39;&amp;#39;&amp;#39;&amp;#39;&amp;gt;)
&lt;/pre
&gt;&lt;p&gt;じっと見て規則性を確認してください。
#&amp;lt;continuation yin&amp;gt;が出てきたらlet*の最初の束縛から呼ばれるところで、
改行が印字されます。
後は、#&amp;lt;continuation yang&amp;gt;や#&amp;lt;continuation yang&amp;#39;&amp;gt;などが
束縛される回数だけ*が印字されます。
&lt;/p
&gt;&lt;p&gt;面倒かもしれませんが、是非一度は手を動かして動作を追ってみて下さい。
&lt;/p
&gt;&lt;h3 id=&#39;H-1l63thw&#39;&gt;お手元マルチスレッド
&lt;/h3
&gt;&lt;p&gt;ちょっと継続を使って遊んでみましょう。
これを通して継続の力の一端に触れた気になっていただければ嬉しいです。
&lt;/p
&gt;&lt;p&gt;まずはファイルから一行読み込むだけのプログラムから始めることにします。
&lt;/p
&gt;&lt;pre&gt;;; サンプルファイルから一行読み込む
(let ((in (open-input-file &amp;quot;sample.txt&amp;quot;)))
  (read-line in))
&lt;/pre
&gt;&lt;p&gt;これを少し改造してみます。
&lt;/p
&gt;&lt;pre&gt;(define k #f)  ;; 継続保存用

(let ((in (open-input-file &amp;quot;sample.txt&amp;quot;)))
  (read-line (call/cc (lambda (cont)
                        (set! k cont)
                        in))))
&lt;/pre
&gt;&lt;p&gt;call/cc式はkに継続を保存しますが、その継続cont自体は使わずに、inだけを返します。
つまり、最初のコードと動作は変わりません。
&lt;/p
&gt;&lt;pre&gt;;; 保存された継続
(lambda (a)
  (PRINT-AND-NEXT-REPL
   (let ((in #&amp;lt;inport&amp;gt;))
     (read-line a))))
&lt;/pre
&gt;&lt;p&gt;保存された継続はこんな風になるでしょう。
正確にはinに束縛するところはすでに完了していますし、
[[Gauche]]ならread-lineも評価済みなので、次式のようになっています。
&lt;/p
&gt;&lt;pre&gt;;; 保存された継続
(lambda (a)
  (PRINT-AND-NEXT-REPL
     (#&amp;lt;subr read-line&amp;gt; a)))
&lt;/pre
&gt;&lt;p&gt;さて、ここで継続を保存しておいたので、
いつでもサンプルファイルから一行読み込むという処理が呼び出せます。
プロンプトから(k in)を何度か呼ぶと次々に行が返されるはずです。
&lt;/p
&gt;&lt;p&gt;途中で(k in)以外の式を評価しても問題ありません。
そんなの当然じゃないか?と思われれば、あなたはすっかり継続に馴染んでいます。
しかし、良く良く考えると、これは結構スゴいことです。
プログラマが手動でスレッド切り換えをしているようなものですからね。
&lt;/p
&gt;&lt;p&gt;では、もう少しその雰囲気を味わっていただきましょう。
&lt;/p
&gt;&lt;pre&gt;;;----------------------------------------
;;                準備
;;----------------------------------------
;; バッファの用意
(define buf &amp;quot;&amp;quot;)
;; 読み込み処理の継続保存用
(define kin #f)
;; 書き出し処理の継続保存用
(define kout #f)

;; 入力ポート保存用
(define inp #f)
;; 別の入力ポート
(define in2 (open-input-file &amp;quot;Another-in.txt&amp;quot;))

;; 出力ポート保存用
(define outp #f)
;; 別の出力ポート
(define out2 (open-output-file &amp;quot;sample-out2.txt&amp;quot;))

;; 読み込みのための継続捕捉
(let* ((in (open-input-file &amp;quot;Continuation.txt&amp;quot;))
       (line (read-line (call/cc (lambda (cont)
                                   (set! inp in)
                                   (set! kin cont)
                                   in)))))
  (set! buf (string-append buf &amp;quot;\n&amp;quot; line)))

;; 書き出しのための継続捕捉
(let ((out (call/cc (lambda (cont)
                      (let ((out (open-output-file &amp;quot;sample-out.txt&amp;quot;)))
                        (set! outp out)
                        (set! kout cont)
                        out)))))
  (display buf out)
  (flush out)
  (set! buf &amp;quot;&amp;quot;)
  (display &amp;quot;write and buf clear.&amp;quot;))

;;----------------------------------------
;;                評価
;;----------------------------------------
;; (1) 読み込みを何度か適当に評価して
(kin inp)

;; (2) たまに気が向いたら書き出ししよう
(kout outp)

;; (3) さらにたまに気が向いたら別のポートを読み込めばファイルのマージもできる
(kin in2)

;; (4) さらにたまに気が向いたら別のポートに書き出せはファイルの分割もできる
(kout out2)
&lt;/pre
&gt;&lt;p&gt;最初の方の準備が済んだら、(1)(2)(3)(4)を適当な順序で複数回実行してみてください。
作業した順でファイルを(行単位でですが)マージしたり分割したりできるようになりました。
そしてたまに(kout outp)とか(kout out2)とすると
バッファリングした情報を書き出せているので、そのタイミングで
sample-out.txtやsample-out2.txtを確認しながら評価してみてください。&lt;br /&gt;
これはタスクの切り換えを自分でやっていると思えば、ごくごく当たり前の結果なんですが、
&lt;strong&gt;継続は保存してから呼び出されるまでの間、完全に休眠しており、&lt;/strong
&gt;
&lt;strong&gt;その間にいかなる式の評価でもはさむことが出来ます。&lt;/strong
&gt;&lt;br /&gt;
そして、kinやkoutなどの継続を起動すると、
あたかもずっとそれをやっていたかの様に続きの計算を粛々と行うのです。
&lt;/p
&gt;&lt;p&gt;すでに、手動でのスレッド切替は体感できたと思いますので、
キューを用意しておき、(lambda () (kin inp))などをキューイングして、
順次デキューして評価したり、新たに(lambda () (kout outp))などを生成して
エンキューすれば、マルチスレッドもどきは簡単に作れてしまいます。
スケジューリングの実験も手軽にできると思うので、
どんどんコードを書き換えて試してみてください。
&lt;/p
&gt;&lt;p&gt;これが、いかなる制御構造の実現も可能にする継続の力です。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
><item><title>ストリーム</title
><link>http://karetta.jp/book-node/gauche-hacks/014529</link
><pubDate>Sat, 07 Apr 2007 23:09:52 +0900</pubDate
><description>&lt;div&gt;&lt;node-set&gt;&lt;p&gt;ストリームは遅延評価のためのデータ構造です。ストリームは&lt;strong&gt;遅延リスト&lt;/strong
&gt;とも呼ばれることがあります。
&lt;/p
&gt;&lt;p&gt;遅延評価とは&lt;strong&gt;必要になったときだけ計算する&lt;/strong
&gt;という意味です。
必要になったときだけ計算できれば、&lt;strong&gt;無限の長さのデータ構造&lt;/strong
&gt;すらも扱うことができます。
&lt;/p
&gt;&lt;p&gt;Gaucheではutil.streamライブラリで遅延評価を実現しています。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (use util.stream)
 &lt;em&gt;#&amp;lt;undef&amp;gt;&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;util.streamライブラリを使うために、あからかじめ式(use util.stream)を評価させておきます。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (define fib (stream-cons 0 (stream-cons 1 (stream-map + fib (stream-cdr  fib)))))
 &lt;em&gt;fib&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;この例はフィボナッチ数列と呼ばれる無限の数列を定義しています。
&lt;/p
&gt;&lt;p&gt;&lt;strong&gt;stream-cons&lt;/strong
&gt;手続きは、リストに対するcons手続きと同様にストリーム(遅延リスト)構造を構築します。
&lt;/p
&gt;&lt;p&gt;&lt;strong&gt;stream-car&lt;/strong
&gt;と&lt;strong&gt;stream-cdr&lt;/strong
&gt;はcar手続き、cdr手続きと同様に、ストリームから要素の値を取り出します。
&lt;/p
&gt;&lt;p&gt;&lt;strong&gt;stream-map&lt;/strong
&gt;手続きは、リストに対するmap手続きと同様にすべての要素に手続き適用を行います。上記の例では&lt;strong&gt;停止条件なし&lt;/strong
&gt;にfibを再帰的に呼び出しています。したがってfibは&lt;strong&gt;無限リスト&lt;/strong
&gt;を形成します。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (stream-&amp;gt;list (stream-take fib 10))
 &lt;em&gt;(0 1 1 2 3 5 8 13 21 34)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;&lt;strong&gt;stream-take&lt;/strong
&gt;手続きはストリームからある個数だけ要素を取り出します。
&lt;strong&gt;stream-&amp;gt;list&lt;/strong
&gt;手続きはストリームをリストに変換します。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (stream-ref fib 10000)
 &lt;em&gt;33644764876431783266621612005107543310302148460680063906564769974680081442166662368155595513633734025582065332680836159373734790483865268263040892463056431887354544369559827491606602099884183933864652731300088830269235673613135117579297437854413752130520504347701602264758318906527890855154366159582987279682987510631200575428783453215515103870818298969791613127856265033195487140214287532698187962046936097879900350962302291026368131493195275630227837628441540360584402572114334961180023091208287046088923962328835461505776583271252546093591128203925285393434620904245248929403901706233888991085841065183173360437470737908552631764325733993712871937587746897479926305837065742830161637408969178426378624212835258112820516370298089332099905707920064367426202389783111470054074998459250360633560933883831923386783056136435351892133279732908133732642652633989763922723407882928177953580570993691049175470808931841056146322338217465637321248226383092103297701648054726243842374862411453093812206564914032751086643394517512161526545361333111314042436854805106765843493523836959653428071768775328348234345557366719731392746273629108210679280784718035329131176778924659089938635459327894523777674406192240337638674004021330343297496902028328145933418826817683893072003634795623117103101291953169794607632737589253530772552375943788434504067715555779056450443016640119462580972216729758615026968443146952034614932291105970676243268515992834709891284706740862008587135016260312071903172086094081298321581077282076353186624611278245537208532365305775956430072517744315051539600905168603220349163222640885248852433158051534849622434848299380905070483482449327453732624567755879089187190803662058009594743150052402532709746995318770724376825907419939632265984147498193609285223945039707165443156421328157688908058783183404917434556270520223564846495196112460268313970975069382648706613264507665074611512677522748621598642530711298441182622661057163515069260029861704945425047491378115154139941550671256271197133252763631939606902895650288268608362241082050562430701794976171121233066073310059947366875&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;上記の例では&lt;strong&gt;stream-ref&lt;/strong
&gt;手続きで任意の位置にある要素の値を取り出しています。
&lt;/p
&gt;&lt;p&gt;ストリームは、たとえば要素を何個用意すれば良いかが事前に分からなくても気にせずプログラミングすることができます。
&lt;/p
&gt;&lt;pre&gt; gosh&amp;gt; (stream-&amp;gt;list (stream-take-while (lambda (x) (&amp;lt; x 1000)) fib))
 &lt;em&gt;(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987)&lt;/em
&gt;
&lt;/pre
&gt;&lt;p&gt;上記は個数ではなく、ある数未満の数列を得る例です。
&lt;strong&gt;stream-take-while&lt;/strong
&gt;は、&lt;strong&gt;ある条件が真のあいだだけ&lt;/strong
&gt;要素を取り出します。
ここでは無名手続き(lambda (x) (&amp;lt; x 1000))で要素を判定させ、
1000未満の要素だけを取り出しています。
&lt;/p
&gt;&lt;p&gt;ストリームは、必要になったときに必要な数だけ計算すれば良いので効率的です。
&lt;/p
&gt;&lt;/node-set
&gt;&lt;/div
&gt;</description
></item
></channel
></rss
>