SE Can't Code

A Tokyo based Software Engineer. Not System Engineer :(

SICP2章、データによる抽象の構築 - 抽象の壁.

SICP2章の途中だが、一度、"抽象の壁"までをまとめようと思う。

1章は手続きの抽象の話だったが、2章からはデータ構造の抽象の話。2章の導入部分から使われている例として、有理数を一個の思考単位とするためのデータの糊付けの仕組みを学ぶ。これは単純な数値データでは立ち向かえない複雑な問題を解くための手助けとなる。計算機は複雑な問題を解くためにモデルを生成するが、モデルを作るためにデータを抽象化(すなはち"抽象の壁"を構築)することで、複雑な問題に対応する。要するにプログラム設計を行う際に、思考レベルを高めることが可能になる。

データ抽象

手続きと同様、基本的なデータオブジェクトを用いて抽象化することを Data Abstraction(データ抽象)と呼ぶ。このデータ抽象はselectors(選択子)とconstructor(構成子)という2つのインターフェースによって構築される。データ抽象を用いて合成データオブジェクトを作ることで、データオブジェクトをどう表現するかに関するプログラムの部分を、データオブジェクトをどう使うかに関するプログラムから隔離することができる。このように抽象化を行い、レイヤーを分けることで、下記のような恩恵を受けることができる。

  • ある場所での変更を局所的なレイヤの変更に封じ込めることができる
  • 実装方法を自由に入れ替えることができる


このデータ抽象の説明には有理数の例を出すといい。有理数を用いた計算を行うためにデータの抽象化を行うことで、必要な思考だけに留めることができる。
最も基本的なデータ構造として、2つの引数を部分として含む、consという対(pair)の合成構造がある。また、その部分の値はそれぞれcarcdrによって取り出すことができる。簡単に表現すると、consに引数となる値2つを渡すと、2つの値が対の組み合わせとなって返され、その対の組み合わせの一つ目の値を取り出す際はcarを、それ以外の値を取り出す際はcdrを用いる。

(define x (cons 1 2))

(car x) -> 1
(cdr x) -> 2

このデータ構造を最も低レイヤとして、抽象化を行う。まずは有理数を表現する。有理数を2つの整数、分子と分母の対で表現するために、構成子make-ratと選択子numer,denomを定義する。

(define (make-rat n d) (cons n d))

(define (numer x) (car x))

(define (denum x) (cdr x))

有理数の表現ができたら、有理数を用いた基本的な演算を行う抽象化を定義する。簡単に有理数の加算、減算、乗算、除算をmake-rat, numer, denomで表現する。このレイヤはcons, car, cdrを意識する必要はない。既に分母と分子を糊付けしてあるため、有理数が表現されている前提で演算を行うことができる。

(define (add-rat x y)
  (make-rat (+ (* (numer x) (denom y)
               (* (numer y) (denom x)))
            (* (denom x) (denom y))))

(define (sub-rat x y)
  (make-rat (- (* (numer x) (denom y)
               (* (numer y) (denom x)))
            (* (denom x) (denom y))))

(define (mul-rat x y)
  (make-rat (* (numer x) (numer y))
            (* (denom x) (numer y))))

(define (div-rat x y)
  (make-rat (* (numer x) (denom y))
            (* (denom x) (numer y))))

(define (equal-rat? x y)
  (= (* (numer x) (denom y))
     (* (numer y) (denom x))))


こんな具合で有理数の演算を行いたい時は、上記のadd-rat, sub-rat, mul-rat, div-rat, equal-rat?を用いてプログラムを実装するだけで良くなる。データ抽象の基本的な考え方はSICPに記載されている内容がしっくりくる。

一般にデータ抽象の基盤となる考えは、データオブジェクトのそれぞれの型に対し、それを使ってその型のデータオブジェクトの全ての操作が表せるような基本の演算の組を見出し、これらの演算だけを使ってデータを操作することである。


あとは表現の依存性を少数のインターフェース手続きに制限すると、すごくプログラム設計に役に立つ。プログラミングをしているとこれらは基本動作に過ぎないが、あまり意識せずに利用しているリストのデータ構造とかもこうした低レイヤーから意識して実装してみるととても面白いし、学びがある。抽象化によって多くの実装レイヤはブラックボックス化されているが、それらを知らずに高い思考レベルだけでプログラムを考えるのはよくないんじゃないかとSICPなり、低レイヤの勉強をしているとよく思う。インフラエンジニアがAWSといったクラウドからスタートしたり、WebエンジニアがRailsといったフレームワークからスタートすることがよくないと言われていることと同じ話で、こうした抽象化の壁の向こうを覗いてみることは、何を理解するにしても大事なことだと実感する。というか本当に技術の面白いところはこういうところだと思う。


しかし2章は進むのが凄まじく遅い。問題が非常に多くて負荷が強いが難儀だ。ここらへんを勉強していると、やっぱし大学でちゃんとコンピュータサイエンスを勉強したかったなぁ、と思う。文系エンジニアがよく思うやつだけど、そんなこと言っても時間は戻ってこないので、社会人はなんとか時間を捻出しましょうと。そんな話。

Remove all ads