標準出力に出力する

今まで Gauche のインタラクティブモードで作業をしてきたけど、そろそろコードを打ち込むのが大変になってきた。間違えたりすると最初から打ち直しだし。
なので、これからはスクリプトモードを使うことにする。スクリプトモードでは gosh コマンドの引数にスクリプト名を渡してやると、そのスクリプトを実行してくれる。スクリプトの拡張子は .scm だ。

で、そうすると結果を出力する必要が出てくるんだけど、ちょっと調べたところ、print 関数でいいみたいだ。
試してみよう。

(print "Hello, wold.")

実行:

^o^ > gosh hello.scm
Hello, wold.

multisubst

3章の最後は、multisubst だ。(multisubst new old lat) は lat の中のすべての old を new に置き換える。

(define multisubst
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) old) (cons new (multisubst new old (cdr lat))))
      (else (cons (car lat) (multisubst new old (cdr lat)))))))

実行例:

gosh> (multisubst 'A 'a '(a b c a b c))
(A b c A b c)

うまくいった。

multiinsertRとmultiinsertL

multiinsertR

(multiinsertR new old lat) は lat の中のすべての old の右側に new を挿入する。

(define multiinsertR
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) old)
        (cons old (cons new (multiinsertR new old (cdr lat)))))
      (else
        (cons (car lat) (multiinsertR new old (cdr lat)))))))

実行例:

gosh> (multiinsertR 'fried 'fish '(chips and fish or fish and chips))
(chips and fish fried or fish fried and chips)

multiinsertL

左側に挿入する multiinsertL。

(define multiinsertL
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) old)
        (cons new (cons old (multiinsertL new old (cdr lat)))))
      (else
        (cons (car lat) (multiinsertL new old (cdr lat)))))))

実行例:

gosh> (multiinsertL 'fried 'fish '(chips and fish or fish and chips))
(chips and fried fish or fried fish and chips)

間違った再帰の例

ところで、本文には間違った再帰の例が載っている(p.58)。次の multiinsertL の定義は (eq? (car lat) old) のところの再帰の仕方が間違っている。10行目だ。

(define multiinsertL
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      (else
        (cond
          ((eq? (car lat) old)
            (cons new
              (cons old
                (multiinsertL new old lat))))
          (else (cons (car lat)
            (multiinsertL new old (cdr lat)))))))))

正しくは lat じゃなくて (cdr lat) を使って再帰しなくちゃいけない。
試しに実行してみよう。

gosh> (multiinsertL 'fried 'fish '(chips and fish or fish and chips))
out of memory (32).  aborting...

メモリーエラーになった。lat で再帰していることで無限に再帰してしまっているのだな。

第4の戒律
(仮)
再帰のときは、常に少なくとも1つの引数を変えるべし。必ず終わりへと近づくこと間違いなし。変えた引数は最終条件で必ずテストすべし。すなわち、cdr を用いるときは、null? でテストすべし。

multirember

multirember はアトム a とラット lat を引数に取り、lat の中のすべての a を取り除く。

(define multirember
  (lambda (a lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) a) (multirember a (cdr lat)))
      (else (cons (car lat) (multirember a (cdr lat)))))))

ポイントは、5行目の (eq? (car lat) a) のときにも再帰しているところだな。
実行例:

gosh> (multirember 'cup '(coffee cup tea cup and hick cup))
(coffee tea and hick)

substとsubst2

続いて、subst と subst2。これは答えを見ちゃった。だって隣に書いてあるんだもの。
ただ、二重の cond は冗長だからひとつにまとめたものを書いた。

subst

(subst new old lat) は lat の中の最初の old を new に置き換える。

(define subst
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) old) (cons new (cdr lat)))
      (else (cons (car lat) (subst new old (cdr lat)))))))

実行例:

gosh> (subst 'topping 'fudge '(ice cream with fudge for dessert))
(ice cream with topping for dessert)

subst2

(subst2 new o1 o2 lat) は lat の中の最初の o1 または o2 を new に置き換える。

(define subst2
  (lambda (new o1 o2 lat)
    (cond
      ((null? lat) (quote ()))
      ((or (eq? (car lat) o1) (eq? (car lat) o2)) (cons new (cdr lat)))
      (else (cons (car lat) (subst2 new o1 o2 (cdr lat)))))))

実行例:

gosh> (subst2 'vanilla 'chocolate 'banana '(banana ice cream with chocolate topp
ing))
(vanilla ice cream with chocolate topping)

insertRとinsertL

さあ、今日は3章の残りをやっつけるぞ。まずは insertR から。

insertR

insertR はアトム2つ new、old とラット(アトムのリスト)lat を引数に取り、lat の中の old と同じアトムの右側に new を挿入したラットを返す。
もうこのくらいは答えを見ないでも書ける。

(define insertR
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) old) (cons old (cons new (cdr lat))))
      (else (cons old (insertR new old (cdr lat)))))))

実行例:

gosh> (insertR 'topping 'fudge '(ice cream with fudge for dessert))
(fudge fudge fudge fudge topping for dessert)

あれ、期待したのと違う。
わかった。間違いは最後の行だ。old を cons してどうする。cons するのは (car lat) だ。

(define insertR
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) old) (cons old (cons new (cdr lat))))
      (else (cons (car lat) (insertR new old (cdr lat)))))))

実行例:

gosh> (insertR 'topping 'fudge '(ice cream with fudge for dessert))
(ice cream with fudge topping for dessert)
gosh> (insertR 'jalapeno 'and '(tacos tamales and salsa))
(tacos tamales and jalapeno salsa)

今度はOKだ。jalapeno はハラペーニョかな。

insertL

insertL は insertR と違って new を old の左側に挿入する。これは簡単、new と old の cons する順を入れ替えればいいだけだ。

(define insertL
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) old) (cons new (cons old (cdr lat))))
      (else (cons (car lat) (insertL new old (cdr lat)))))))

実行例:

gosh> (insertL 'topping 'fudge '(ice cream with fudge for dessert))
(ice cream with topping fudge for dessert)
gosh> (insertL 'jalapeno 'and '(tacos tamales and salsa))
(tacos tamales jalapeno and salsa)

うまくいった(註:英語の意味は考えないこと)。
と思ったら、5行目の ((eq? (car lat) old) (cons new (cons old (cdr lat))))((eq? (car lat) old) (cons new lat)) とも書けるとのこと。なるほど、そのとおりだ。