SICP

SICP を読んでみる #70 第二章 p.113

仕事の締切やらその後の溜まったタスクの消化やら集中力が続かないやら(これがほとんど)で気づけば一週間ぶりの SICP です。ここで途切れないように粛々と続けていきます。

今やっているあたりはプログラムを書いていれば当たり前な内容なので、問題をこなすためにモロモロの準備をするのが結構ダルいというのもあって微妙に乗り気になれないのがテンション低めな原因です。かと言って、問題をこなしたらこなしたでいろいろ発見があるので飛ばすわけにはいかないんですね。

問題解答

問 2.77
いい加減 put/get が手元のないとどうしようもないので先人の知恵を借りよう。。。。とおもったらひげぽんさんの記事につきあたる。同じ所まで同じ教科書で勉強してみてわかるけど、この人すごいな、、、、数倍のスピードで数倍以上の習得度があるorz。これだけの差をガツンと目の前に突きつけられると凹む。→記事を最後まで読んだら、問題2.74 をやるのに3時間くらいって書いてありました。成果とかかった時間がわかるとすごくいい目安になります。

この問題がおわったら、これまでのふりかえりともあわせてここまでのひげぽんさんの記事と自分の理解した内容を突き合わせよう。

ひげぽんさんの作った put/get はこちら。

(define op-table (make-hash-table))

(define (put op type item)
    (if (not (hash-table-exists? op-table op))
        (hash-table-put! op-table op (make-hash-table)))
    (let ((type-table (hash-table-get op-table op)))
      (hash-table-put! type-table type item)))

(define (get op type)
    (if (not (hash-table-exists? op-table op))
        (hash-table-put! op-table op (make-hash-table)))
    (let ((type-table (hash-table-get op-table op)))
      (hash-table-get type-table type)))

get って、op がなかったら結局 type を取得する時にエラーになるから

(define (get op type)
    (let ((type-table (hash-table-get op-table op)))
      (hash-table-get type-table type)))

これでも同じなんじゃないだろうか?

とにかく、これで put/get が手に入ったので問題にとりかかります。

(put 'real-part '(complex) real-part)

これが get されたときの挙動を確認。”‘(complex)” の部分がよくわからない。
そもそも complex がどこで定義されていたっけ? →単に (complex) というリストを作っているだけだった。このレベルで忘れているとは、、、、

そもそも (make-complex-from-real-imag 3 4) が正常に動かない。調べると op-table に ‘make-from-real-imag は登録されているものの、その先が空っぽいんだけど何故!?

ちょっと時間がかかりすぎたので今日はここまで。明日再チャレンジ。

SICP

SICP を読んでみる #68 第二章 pp.109-110

問題解答

問2.74
パス

本文

メッセージパッシング

手続き名毎に内部で実際に呼び出す処理を振り分ける。

データを生成すると実態は手続きが帰ってきて、そのデータ(手続き)に対して必要な演算の名前を渡すことで必要な結果を得ることができる

問題解答

問2.75
手の運動なのでパス。

問2.76
汎用演算 :
型・演算の追加共にシステム全体に対して影響を及ぼす。

データ主導流 :
新しい型が追加された場合に、その型に対応したパッケージを用意する。新しい演算が追加された場合、全てのパッケージに対して新しい演算を定義する。そのため、型の追加に対しては全体への影響は少ないが演算の追加に対してはシステム全体に影響を及ぼす。

メッセージパッシング流 :
新しい型が追加された場合に、全てのパッケージに対して新しい型を定義する。新しい演算が追加された場合、その演算に対応した手続きを定義する。そのため、型の追加に対してはシステム全体に影響を及ぼすが、演算の追加に対してはシステム全体に影響が少ない。

ここまで来たけど、イマイチ用語が肌に馴染んでいない感がある。構成子とかパッケージとか演算とか、SICP 的な用語の感覚が何だか曖昧。

SICP

SICP を読んでみる #67 第二章 pp.108-109

コードの入力と内容の確認がおわったので問題に戻ります。

問題解答

問2.73
b.
和の微分は (u+v)’ = u’+v’
積の微分は (uv)’ = uv’+u’v

(define (deriv-package)
  (define (deriv-sum exp var)
    (make-sum (deriv (addend exp) var)
              (deriv (augend exp) var)))

  (define (deriv-product exp var)
    (make-sum
     (make-product (multiplier exp)
                   (deriv  (multiplicand exp) var))
     (make-product (deriv (multiplier exp) var)
                   (multiplicand exp))))

  (put 'deriv '+ deriv-sum)
  (put 'deriv '* deriv-product))

こんな感じ? put/get がないのでちゃんと動作確認ができない。
ちょっと(かなり)手抜きで、答えを見ながら完全に動作するコードをコピペして動作を確認。

SICP

SICP を読んでみる #65 第二章 pp.99-105

本文

問題の解答をするためには本文で解説されているコードを一通り打ち込まないとやっぱりダメですね。
めんどくさいけどこれも練習なので今更ながら打ち込み。

汎用複素数システムのぶぶんまでの打ち込みが完了して、今日は終了。

SICP

SICP を読んでみる #64 第二章 p.108

ゴールデンウィークもあっという間におわり、通常営業に戻ります。

問題解答

問2.73
a.
(get ‘deriv…) の部分がわからない → P.106 で解説済だった。
‘deriv の表からオペレータを取得して微分計算をおこなうようにしている。
number? や variable? は微分の計算ではなく、他の処理とは異なるため同列に扱うことができない。

b 以降は明日。全く集中できなかった。。。。

SICP

SICP を読んでみる #63 第二章 pp.98-108

SICP の朝練を始める前にちょっとだけと思って仕事をしてしまって、結果朝練をする時間が無くなってしまうというのがお約束パターンとしてあるようで、ここ数日もそんな感じです。朝に作業をしてしまった分時間をずらして勉強をしようと思っても何だかんだで結局できなくなってしまうので、このパターンにハマらないように気をつけないといけないです。

問題解答

問2.70~2.72 はパス。

本文

2.4 抽象データの多重表現
複素数のように、同一の内容を複数の方法で表現することができる場合、状況によってどちらが最適かは変わってくる。
また、時間とともに要求が変わってきた時にデータ表現も柔軟に対応できるようにする必要がある。
このような場合でもプログラムの一部分だけ手を入れることで対応できるようにする。

2.4.1 複素数の表現
演算によって実座標・虚座標での表現と絶対値・偏角での表現どちらが適しているかかわる。また、どちらの表現を使用していてもプログラマの観点からは任意の表現方法でアクセスした時の値が取得できると有用な場合が多い。

2.4.2 タグつきデータ
データにタグをつけ、どのような形式で表現されたものなのかを判別できるようにする。その上で表現形式毎に適切な手続きを用意して、共通のインターフェースを持つ手続き内で、実際に呼び出す手続きを切り替える。

今ならクラス化をすれば簡単にできる内容。

2.4.3 データ主導プログラミングと加法性
・汎用インターフェースは全ての表現を知らなければいけない
・表現は別々に設計できるが、システム全体で名前が被らないことを保証しなければいけない

→パッケージを定義し、実装をその中に隠蔽する

SICP

SICP を読んでみる #62 第二章 p.98

問題解答

問2.69

(define (successive-merge  trees)
  (if (= (length trees) 1)
      trees
      (letrec* ((l (length trees))
                (carTrees (take trees (- l 2)))
                (cdrTrees (drop trees (- l 2)))
                (leaf (make-code-tree (car cdrTrees) (cadr cdrTrees)))
               (successive-merge (adjoin-set leaf carTrees))
               )
      ))

(define (generate-huffman-tree pairs)
  (successive-merge (make-leaf-set pairs)))

(define pairs (list (list 'A 2)
                    (list 'B 2)
                    (list 'C 1)
                    (list 'D 1)))
(define hTree (generate-huffman-tree pairs))
(define code (encode '(A D A B B C A) hTree))
(decode code hTree)

Lisp への信心が足りなさすぎて、処理をコードに落とすところでものすごく時間がかかっちゃいます。

細かい処理で時間をとられがちなので、letrec* とか take とか drop とか、SICP では言及されてないけど存在する便利機能も今後はある程度調べて使っていこうかと思います。

ちなみに解答例は違うアプローチ。

(define (successive-merge set)
 (if (= (length set) 1) (car set)
     (let ((sorted-set (sort set (lambda (x y) (< (weight x) (weight y))))))
       (successive-merge 
        (cons (make-code-tree (car sorted-set) (cadr sorted-set))
              (cddr sorted-set))))))

ソートして、頭から繋げていくのか。たしかにこっちの方が自然ですよね、、、、

SICP

SICP を読んでみる #61 第二章 pp.94-98

一日空いてしまったので復習から。

問題解答

問2.68

(define (encode-symbol symbol tree)
  (define (it symbol tree code)
    (cond ((null? symbol) '())
          ((leaf? tree) code)
          ((element-of-set? symbol (symbols (left-branch tree)))
           (it symbol (left-branch tree) (append code '(0))))
          ((element-of-set? symbol (symbols (right-branch tree)))
           (it symbol (right-branch tree) (append code '(1))))
          (else (error "bad symbol" symbol))))

  (it symbol tree '()))

(define (encode message tree)
  (if (null? message)
      '()
      (append (encode-symbol (car message) tree)
              (encode (cdr message) tree))))

(encode '(A D A B B C A) sample-tree)

答えはでたけど何かコードが汚い。