:-)



データ隠蔽



カプセル化



ポリモーフィズム



=, <, <=, >=, >

char=?, char<?, char<=?, char>=?, char>?

string=?, string<?, string<=?, string>=?, string>?

eqv?

equal?

null?

number?

symbol?

char?

string?

pair?

not

and

or

eq?





> (eq? 'symbol 'symbol)

#t

> (define sym-a 'symbol)

> (define sym-b 'symbol)

> (eq? sym-a sym-b)

#t

> (eq? "string" "string")

#f

> (define str-a "string")

> (define str-b "string")

> (eq? str-a str-b)

#f

> (eq? str-a str-a)

#t

> (eq? '(a b) '(a b))

#f

> (define lst '(a b))

> lst

(a b)

> (eq? lst lst)

#t

>





eq?

(eq? '() '())

#t

material





(define material

(lambda (super weight)

(lambda (message)

(cond

((eq? message 'hold)

(lambda ()

(display "implement 'hold.")

(newline)))

((eq? message 'drop)

(lambda ()

(display "implement 'drop.")

(newline)))

((eq? message 'eat)

(lambda ()

(display "cannot eat.")

(newline)))

((eq? message 'what-is-the-weight?)

(lambda ()

(display weight)

(display " kg.")

(newline)

weight))

(else (if (null? super)

(lambda () '())

(super message)))))))





lambda

material

super, weight

lambda

lambda

message

lambda

lambda

weight

message

'hold, 'drop, 'eat, 'what-is-the-weight?

cond 式

cond





(cond

(<式11> <式12> <式13>...)

(<式21> <式22> <式23>...)

(<式31> <式32> <式33>...)

...

(else <式o1> <式o2> <式o3>...))





cond

#t

cond

#f

else

cond





> (define foo (material '() 0))

> ((foo 'eat))

cannot eat.

> ((foo 'what-is-the-weight?))

0 kg.

0

> ((foo 'drop))

implement 'drop.

> ((foo 'hold))

implement 'hold.

> ((foo 'unknown))

'()

> (define bar (material '() 1000000))

> ((bar 'what-is-the-weight?))

1000000 kg.

1000000

>





foo, bar

(foo 'eat)

lambda

'what-is-the-weight?

weight

material

food





(define food

(lambda (weight)

(let ((super (material '() weight)))

(lambda (message)

(cond

((eq? message 'hold)

(lambda ()

(display "Okay, you can hold.")

(newline)))

((eq? message 'drop)

(lambda ()

(display "Oh! my God.")

(newline)))

((eq? message 'eat)

(lambda ()

(display "Tasty?");

(newline)))

(else (super message)))))))





lambda

let

food

food

material





> (define baz (food 1.618))

> ((baz 'eat))

Tasty?

> ((baz 'drop))

Oh! my God.

> ((baz 'hold))

Okay, you can hold.

> ((baz 'what-is-the-weight?))

1.618 kg.

1.618

>





'what-is-the-weight?

food

material





(define egg

(lambda (weight color)

(let ((super (food weight))

(color color))

(lambda (message)

(cond

((eq? message 'hold)

(lambda ()

(display "Don't hold on tight.")

(newline)))

((eq? message 'drop)

(lambda ()

((super 'drop))

(display "It has broken.")

(newline)))

((eq? message 'eat)

(lambda ()

(display "Delicious.")

(newline)))

((eq? message 'what-is-the-color?)

(lambda ()

(display color)

(newline)

color))

(else (super message)))))))





food

let

color

'what-is-the-color?





> (define qux (egg 2.718 "white"))

> ((qux 'drop))

Oh! my God.

It has broken.

> ((qux 'what-is-the-weight?))

2.718 kg.

2.718

> ((qux 'what-is-the-color?))

white

"white"

>





cond





((eq? message 'change-the-color!)

(lambda (new-color)

(let ((old-color color))

(set! color new-color)

old-color)))





egg





> (define quux (egg 1.602 "light-gray"))

> ((quux 'change-the-color!) "black")

"light-gray"

> ((quux 'what-is-the-color?))

black

"black"

>





'change-the-color!

南無λ -- 私はλに帰依しますこれら一連の記事は、 numb-lambda インタープリタ から始まる連載企画になっています。今回は、オブジェクト指向を取り扱います。最初にお断りしておきます。(R5RSレベルでは*1) Scheme にはクラスのようなデータ構造はありません。さらに構造体がありません。Scheme 標準でリストの他にある複合データ型は、ベクタ（=配列; array）くらいです。文字列は、ありますが、それを複合データ型と呼べるかは疑問です。それでも、Scheme を使ってオブジェクト指向のプログラミングは可能です。我々には強力なλ（lambda）があります。オブジェクト指向の3つの要件を掲げておきましょう。Scheme では、この要件を満足させることができるのです。ただし、のレベルをコントロールする public, protected, private のような機能はありません。それでは、オブジェクト指向のプログラミングをしてみましょう。ですが、その前に......この連載では、主に整数とシンボルを扱って来ました。hello, world のところで、少しだけ文字列も扱いました。残りは実数（浮動小数点数） *2 と文字があります。それらのデータを比較するには、述語を組み合せます。シンボルの比較 eq?数の比較文字の比較文字列の比較この他、R5RS には、が規定されていますが numb-lambda では実装していません。;空リストかどうかなぜか、numb-lambda では、vector? は実装していません。それと、論理演算ができます。このうち、not だけが関数で、and と or はスペシャルフォームです。and は複数の引数を取りますが、それらを左から順に評価し、#f になった時点で値を決定し残りは評価しません。同様に or も複数の引数を取り、それらを左から順に評価し、#t になった時点で値を決定し残りの評価はしません。今回は、オブジェクトに与えるメッセージとしてシンボルを使います。ですから、が使えれば十分です。他の述語については、また、利用する都度、説明します。ここでは、詳しく説明しませんが、ということだけは強く意識してください。そして、という点も。は、アドレスの比較だと考えれば良いでしょう。ただし、のように、空リストの比較はにならなくてはならないのですが numb-lambda の実装にバグがあり、ABORT してしまいます。*3前置きが長くなってしまいました。オブジェクト指向のプログラミングを始めましょう。以下に基底クラスを示します。ここで、が入れ子になっていますが、が呼ばれると、を引数にとるが動作します。このは、を引数に取る式を戻り値として返します。ここで、戻り値となったここで、と呼ばれるものが、です。はオブジェクトが解釈できるとなっています。ここでは、が解釈可能です。の話をしていませんでした。式の一般形を次に示します。ここで、内の各行の最初の式、 が評価され、が返されると , , ... が順に評価され最後の式が式の値となります。 の評価値としてが帰った場合、次の行がテストされます。どの式も適合しないときは、のある行が逐次実行され、最後の式が返ります。以上で、行と呼んでいるのは、ここで示した一般形にのみ通用します。通常は、一般形の各行が複数行に分けて書かれていることがあります。あくまで、ここで行と言っている単位は、括弧で見分けてください。スペシャルフォームを説明しました。コードは読めるようになったでしょうか。上の関数を評価したらインスタンスを作ってみましょう。ここでは、という2つのインスタンスを作ってメッセージを送っています。括弧が二重になっている意味は解るでしょうか。例えば式を返します。それを呼び出すには、括弧の最初の式とする必要があります。ここでは引数はありません。また、を送ったとき、とくに注意してください。これは、副作用ばかりでなく値も返しています。俗語で言うところのプロパティに対する getter ですね。今度は、このから、を派生させます。ここで、基底クラスは外側のの内側にあるでインスタンスが作られます。つまり、が呼ばれる都度、のインスタンスが作られます。インスタンスを生成してみましょう。各メソッドがオーバーライドされているのが解ると思います。そして、注目すべくは、やはり、メッセージを送ったときで、このハンドラは内にありません。これは、派生元（スーパークラス？）であるが処理しています。それでは、もう一段、派生させてみましょう。から、派生しています。（式）というプロパティをひとつ追加しました。また、対応するメッセージを追加しました。インスタンスを生成してみましょう。（とんでもない重さの玉子ですが）色を塗り変えるには、メッセージを追加しなくていはなりません。式に次の一節を追加してみてください。関数を評価し直したら、早速インスタンスを作ってメッセージを送ってみます。メッセージは、俗に言う setter です。しかし、戻り値は、元の値にしています。ここで、オブジェクトの実装には閉包（クロージャ; closure ）を利用しています。これは以前にも一度紹介していますが、どこだったか覚えていますか。近代的な言語は、ほとんど閉包を利用可能となっています。この手法を使ってオブジェクトを実装しているのを初めて確認したのは或る書籍です。*4今回は以上とします。*1 簡単に紹介しますと Scheme 言語の規格書です。インターネットで公開されています。因みにR6RSも存在するようです。*2 numb-lambda では、実数（浮動小数点数）は最低限、ふつうの人が電卓で利用する程度にしか実装していません。おそらく数学的には情報不足だと思います。*3 近日、修正します。*4 ジェラルド・ジェイ・サスマン、ハロルド・エイブルソン、ジュリー・サスマン共著 和田英一訳「計算機プログラムの構造と解釈 第二版」 翔泳社 ISBN-10: 4798135984, ISBN-13: 978-4798135984私の所有しているものはピアソン・エデュケーションのもので絶版となっていますが、和田先生のツイートによると内容は全く同じだそうです。