読者です 読者をやめる 読者になる 読者になる

(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

SICPを読む_(12)2章_データによる抽象の構築(p45-49)

Scheme SICP

間が空きました。なんかVPSといらぬ戰いをしていたので、ちょっと本流に戻ってきます。*1
(GaucheKahuaとか触りたい…うずうず。いつかGaucheソースコード読めるようになるんだ…。)

というわけで2章、入っていきます。年内に終わらせたいと言いつつ本当に終わるのだろうか。。。

2.データによる抽象の構築(p45-p46)

  • 1章ではプログラムの設計における、計算プロセスと手続きの役割について考えてきた
    • 基本的データと基本的演算の使い方
    • 手続きを組み合わせ、結合や条件による合成手続きの構成法
    • パラメタの使い方、defineを使った手続きの抽象化の手法
  • 手続きが実現しているプロセスに共通するパターンを分類し、検討し、単純なアルゴリズム解析をしてきた
  • 高階手続きが計算の一般的方法で操作したり、それらを使って考えたりできるが故に、言語の保つ力を拡大することができた

2章では1章より更に複雑なデータをみていく。

  • データオブジェクトを組み合わせ、合成データ(compound data)を作って抽象を構築する機能を扱う
  • プログラムを一貫した方法で操作出来るような対-合成データオブジェクト(compound data object)−を作ることができたら遥かに良い
  • データオブジェクトをどう表現するかに関するプログラムの部分を、データオブジェクトをどう使うかに関するプログラムの部分から隔離する一般的技法はデータ抽象(data abstraction)という強力な設計手法

線形結合、ax+byを作ることを考えると

(define (linear-combination a b x y)
  (+ (* a x) (* b y)))

数値だけでなく、有理数複素数多項式などの線形結合ができる事を手続き的に表したかった場合、次のように表せる、と。

(define (linear-combination a b x y)
  (add (mul a x) (mul b y)))

addやmulは+や*の基本手続きではなく、データの種類によって演算を実行するもっと複雑なものだとする。
こうすることによって、linear-combinationの視点からはa,b,x,yが何であるかは無関係で、それらが基本的データでどう表せるかも無関係になる。

  • 合成手続きと同じ様に、複雑さに対処する技法としての抽象の問題なので、データ抽象により、プログラムの部分部分で適切な抽象の壁(abstraction barrier)を建てることが可能であることを見ていく。

むぐぐ。。なんだか難しいことが書かれまくっているのである。。。線形結合の例はとても分かりやすい。
というか、既にScheme手習いで少しやってる感じある…気がする?

「公認インターフェース」とか「記号式」とか「汎用演算」「データ主導プログラミング」とかちょっと難しめの言葉が沢山出てるけど、一度棚に上げて後できっと出てくるのを信じて先に進む。加法的に組み合わせられるようにする、っていう話の件はとても大事だなって思った。それは今までの経験からなんとなくだけど、そういう設計力が欲しいとたまに思う。

2.1 データ抽象入門(p46)

データ抽象は、合成データオブジェクトの使い方を、それがより基本的データからどう作られたかの細部から隠す。

  • データ抽象の基本的な考え方は、プログラムを合成データオブジェクトを使うように構成し、「抽象データ」を操作できるようにすることである。
    • プログラムはデータを、当面のしごとを実行するのに本当に必要ではないデータについては、何も仮定しないように使うべき
    • 「具体的」なデータ表現は、データを使うプログラムから独立に定義すべきである
      • こういう2つの部分のインターフェースは選択子(selectors)とか構成子(constructors)という一組の手続きである

2.1.1 例:有理数の算術演算(p46-49)

  • 合成の戦略:希望的思考(wishful thinking)

つまり、make-ratやnumer,denomに関しては、どう実装されているか知らない状態だけど、make-ratを使えば有理数を返してくれるし、numer,denomを使えば有理数の分子、分母が返ってくるという希望があるという感じかな。(希望というか都合の良いように解釈しているとも取れなくはない)

  • データ抽象の具体的レベルが実装出来るよう、Schemeには(Lispには)基本的手続きconsで構成される対(pair)という合成構造がある
    • 対で構成されるデータオブジェクトをリスト構造の(list structured)データという
有理数の表現
  • 有理数を2つの整数:分子と分母の対で表現する、
(define (make-rat n d) (cons n d))

(define (numer x) (car x))

(define (denom x) (cdr x))

あとは計算結果を表示するための手続きprint-ratを定義してあげる。

(define (print-rat x)
  (newline)
  (display (numer x))
  (display "/")
  (display (denom x)))

こんな感じで有理数を表現できたー!やったー!

問題2.1

問題2.1はpositive?手続きを使えば容易に実装できる。

;;make-rat
(define (make-rat n d)
  (let ((g (gcd n d)))
    (if (positive? (/ n d))
	(cons (/ (abs n) g) (/ (abs d) g))
	(cons (/ (- (abs n)) g) (/ (abs d) g)))))

;;test
(print-rat (make-rat 1 3))

(print-rat (make-rat -1 -3))

(print-rat (make-rat 1 -3))

(print-rat (make-rat -1 3))


ということで、そろそろ3時なので切り上げて寝ます><
明日あたりに2.1.2からやりたいなー。やれたらいいなー。

*1:やりたいこと試したいこと沢山あってテンションがちょっと上がったりするんだけど…