放射状

勉強のメモ。

SICP三日目~  問題1.6

社内の某Mに技術習得系の指導はまかせるわー、って全部投げてみたら、

  1. 課員それぞれで技術ブログを書くようにしましょう! 最低でも月一は更新しましょう!
  2. 週一で勉強会しましょう! 関数型ってやってみたいのでF#やりましょう!
  3. 週一でlinuxソースコードリーディングしましょう!
  4. プレゼン練習しましょうね! 自主的に!

とかすっげえ意識高すぎることを言い出し、一任した立場としては嫌とも言えずに誕生したのがこのブログです。始まりはしぶしぶだったわけですが、昔似たようなブログをやっていた経緯もあり、久しぶりに長文を書くのが楽しくなってしまって自分でも意外なペースで更新しているのが若干悔しいところで、かつ、提案した某Mがまだただの一度もブログを更新していない、というのがわりと笑えるところです。
ブログ更新はさておき、他のメニューを全部こなすのは中々きついです。F#とlispC言語(コードリーディング)と同時進行なあたりが。美しい!(lisp) ある意味美しい!(F#) なぜこのような鬼子を神はお造りになられたので…(C言語) の三つを反復横飛びしてるわけですから。いや、最初に触った言語だから愛着があるのはC言語なんですけどね。

問題1.6

今回はまずこのページの上のほうの平方根プログラムを打ち込んで動作をよく確認してから(説明もちゃんと読む)、同じページの問題1.6を考えます。
まあこっちも打ち込んで実行してみる。

(define (new-if predicate then-clause else-clause)
  (cond (predicate then-clause)
        (else else-clause)))

;new-ifは特殊形式ではないので、全ての引数を評価してしまう
(define (sqrtrec guess x)
  (new-if (good-enough? guess x)
      guess
      (sqrtrec (improve guess x) x)
  ))

(define (good-enough? guess x)
  (< (abs (- (square guess) x)) 0.001)
  )

(define (improve guess x)
  (average guess (/ x guess))
  )

(define (average x y)
  (/ (+ x y) 2))

(define (sqrt x)
  (sqrtrec 1.0 x)
  )

(sqrt 9.0)

REPLで実行すると返答が帰ってきません。無限ループが発生しているようです。
問題1.5と同じですね。作用的順序でnew-ifの引数が全て評価されてしまうため、再起関数であるsqrtrecのところで無限ループになってしまいます。
特殊形式であるifは「述語式を最初に評価し, その結果が帰結式と代替式のいずれを評価するかを決める」ため(問題1.5参照)、帰結式と代替式のどちらかは評価されません。ifに関してはこちらのページも参考になります。

再帰関数の呼び出しをトレースしたいよね

脳内キャッシュが非常に貧弱な私にとって、再帰関数を頭に描くことは非常に苦手な部類に入るわけです。
なので、なんか呼び出しを順に追っていけるデバッガみたいなのがあったらいいなあと思い、探してみたら、ありました。SLIB。>404 Blog Not Found:scheme - traceとslib
Gauchewindows版インストールのときにSLIBも入っていたらいいなあと思い、

(use slib)
(require 'trace)

にREPLと打ってみるも、以下のようなエラーが。まあ、一緒にはインストールされないんですな。

*** ERROR: Compile Error: cannot find "C:\\SLIB\\require" to load
"(standard input)":1:(use slib)

Stack Trace:
_______________________________________
  0  (eval expr env)
        At line 179 of "C:\\Program Files (x86)\\Gauche\\share\\gauche-0.9\\0.9.4\\lib/gauche/interactive.scm"
gosh> *** ERROR: Compile Error: require: string expected, but got 'trace

なので、ここからwindowsインストーラをダウンロードしてきて、↑で出てきてる「C:\SLIB\」にインストールします。
で、

(use slib)
(require 'trace)

(define (sqrtrec guess x)
  (if (good-enough? guess x)
      guess
      (sqrtrec (improve guess x) x)
  ))

(define (good-enough? guess x)
  (< (abs (- (square guess) x)) 0.001)
  )

(define (improve guess x)
  (average guess (/ x guess))
  )

(define (average x y)
  (/ (+ x y) 2))

(define (sqrt x)
  (sqrtrec 1.0 x)
  )

;トレース指定 / 再帰関数の挙動を見たいので、sqrtrecを指定
(trace sqrtrec)
;実行
(sqrt 9.0)

を実行すると、

##<undef>
gosh> #t
gosh> sqrtrec
gosh> good-enough?
gosh> improve
gosh> average
gosh> sqrt
gosh> #<closure (debug:trace-procedure debug:trace-procedure)>
gosh> CALL sqrtrec 1.0 9.0
  CALL sqrtrec 5.0 9.0
    CALL sqrtrec 3.4 9.0
      CALL sqrtrec 3.023529411764706 9.0
        CALL sqrtrec 3.00009155413138 9.0
        RETN sqrtrec 3.00009155413138
      RETN sqrtrec 3.00009155413138
    RETN sqrtrec 3.00009155413138
  RETN sqrtrec 3.00009155413138
RETN sqrtrec 3.00009155413138
3.00009155413138

おおー。
これでかなり分かりやすくなりますね!

既に「本当に一章突破できるのかな…」とか思い始めている

日本語が難しいよう…。自分の頭も悪いような…。
しかし問題1.5を解いてから1.6を解くと、1.5でぼやっとしか理解しきれていなかった部分が明確になってちょっと気持ちよかったです。