2008年01月08日

継承と多相性。多様性?多態性?

前回、前々回と何をやったのかを考えて見ましょう。
NiledArrayはArrayからほぼ全ての機能を継承しました。ちょびっとだけ機能を加えて。
ArrayのメソッドはNiledArrayでもそのまま使えます。

こんなの。

st> Smalltalk at: #a put: (NiledArray new: 10) !
st> a at: 5 put: 1234 !
st> a do: [:i| i printNl ] !
nil
nil
nil
nil
1234
nil
nil
nil
nil
nil
st>


継承の強みは、変更分のみ作業できる事です。変更しない部分は、そのまま動作させられます。

複雑なクラスでは、多相性の価値が発揮されます。
Complex numberはnumberを同じメッセージに応答します。
このコードを誰かに渡しても、渡された人はComplex numberでどう計算するか説明の必要がありません。
C言語であれば、使う人がcomplexの加算をするのに、complex_plus()を使うのか、comples_add()なのか、またはadd_complex()、または…なのかを探す必要があるでしょう。

しかしながら、Complexクラスには著しい欠陥があります。
もし普通のnubmerとComplex numberを混ぜてしまったらどうなるのでしょう?
今のところComplexクラスは他のComplex numberとのみ利用されると仮定されています。
これは現実的ではありません。"普通の"数字は単に虚部が0なだけだからです。
Smalltalkはnumberに他のnumberと作業するように強制しています。

システムは賢くて、ほんの少しの追加のコードが必要なだけです。
残念ながら、説明にはその3倍が必要になるでしょう。
もしGNU Smalltalkでどのように強制が働いているか興味があったら、Smalltalkライブラリのソースを探して、'retry:coercing:'メッセージをトラックバックすると良いでしょう。
'generality'メッセージが各numberのタイプで、返却する値を考えてみたくなるでしょう。
最後に、それぞれのnumericクラスで'corerce:'の扱いを試してみる必要があります。



…全然、何言ってるかわからないです。だめな翻訳の見本みたいになっちゃった。
どうやら、corercingは「強制」で間違っていないようです。一方のクラスから別のクラスに強制的に変換することのようです。これがわかるとSmalltalkの理解が深まるそうです。じゅんさんがそのような事を書いておられました。

次はstreamにするか、corecingを見てみるか。どうしましょうか?
posted by ほえ at 18:06| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年12月26日

新しい種類の数

「新しい種類の数」って言ってもなんだかわかりませんね。
元は、
Adding a New Kind of Number
なんですけど。

Complex(複素数)クラスをNumberのサブクラスとして作成します。
複素数ってなんだっけ?実部と虚部があってz = a + biで表される…ふにゃらふにゃら。
で、複素数は<や>で比較はできないそうです。

作成するComplexクラスは数なので、Numberのサブクラスにするのがよさそうです。
でも、<や>で比較はできないので、Numberのメソッドを継承したくありません。
こんな時どうすれば?というのが今回のお話。

どうするかというと、継承したくないメソッドは、エラーにしてしまいます。
放っておくと親のクラスが処理してしまいますから、ちゃんとメソッドを定義してエラーを返すようにします。

realpart(実部)とimagpart(虚部)はIntegerかFloatにするべきでしょうか?
Smalltalkの伝統では?Objectのままにしておくそうです。
そして数値用のメッセージにはそれなりに応答する事を期待するんですと。
数値用のメッセージに応答できないモノが入れられていれば、それなりにエラーを返しますからと、そういうことですね?

Number subclass: #Complex
instanceVariableNames: 'realpart imagpart'
classVariableNames: ''
poolDictionaries: ''
category: nil !
!Complex class methodsFor: 'creating'!
new
^self error: 'use real:imaginary:'
!
new: ignore
^self new
!
real: r imaginary: i
^(super new) setReal: r setImag: i
! !
!Complex methodsFor: 'creating--private'!
setReal: r setImag: i
realpart := r.
imagpart := i.
^self
! !

!Complex methodsFor: 'basic'!
real
^realpart
!
imaginary
^imagpart
! !

!Complex methodsFor: 'math'!
+ val
^Complex real: (realpart + val real)
imaginary: (imagpart + val imaginary)
!
- val
^Complex real: (realpart - val real)
imaginary: (imagpart - val imaginary)
!
* val
^Complex real: (realpart * val real) - (imagpart * val imaginary)
imaginary: (imagpart * val real) + (realpart * val imaginary)
!
/ val
| d r i |
d := (val real * val real) + (val imaginary * val imaginary).
r := ((realpart * val real) + (imagpart * val imaginary)).
i := ((imagpart * val real) - (realpart * val imaginary)).
^Complex real: r / d imaginary: i / d
! !

!Complex methodsFor: 'comparison'!

= val
^self shouldNotImplement
!
> val
^self shouldNotImplement
!
>= val
^self shouldNotImplement
!
< val
^self shouldNotImplement
!
<= val
^self shouldNotImplement
! !

!Complex methodsFor: 'printing'!
printOn: aStream
aStream nextPut: $(.
realpart printOn: aStream.
aStream nextPut: $,.
imagpart printOn: aStream.
aStream nextPut: $)
! !

だいたい、今までに出てきた事なので問題ないでしょう。
'$('はASCIIキャラクタの'('をオブジェクトとして生成します。

試してみます。

st> Smalltalk at: #x put: (Complex real: 1 imaginary: 2)
st> x inspect !
An instance of Complex
realpart: 1
imagpart: 2
st> Smalltalk at: #y put: (Complex real: 3 imaginary: 4)
st> y inspect !
An instance of Complex
realpart: 3
imagpart: 4
st> (x + y) printNl !
(4,6)

ちゃんと+できますね。


比較してみます。
st> (x > y) printNl !
Object: Complex new "<0xb3d46168>" error: should not be implemented in this class
SystemExceptions.ShouldNotImplement(Exception)>>#signal
SystemExceptions.ShouldNotImplement class(Exception class)>>#signal
Complex(Object)>>#shouldNotImplement
Complex>>#>
UndefinedObject>>#executeStatements

ちゃんとエラーになりました。

数値以外で作成してみます。

st> x := Complex real: 'a' imaginary: 'b' !
st> (x + y) printNl !
Object: 'a' error: did not understand #+
String(Object)>>#doesNotUnderstand:
Complex>>#+
UndefinedObject>>#executeStatements

+をするとエラーになります。

こんな感じで。
サブクラスの話は次でおしまい。
posted by ほえ at 17:54| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年12月19日

'or:'と'|'

or:と|はちょっと違います。

前のNiledArrayに新しくメソッドを追加します。

!NiledArray methodsFor: 'bb'!
aa: ii
^(ii > 0) | (0 / 0)
!
bb: ii
^(ii > 0) or: [(0 / 0)]
!
cc: ii
^(ii > 0) or: (0 / 0)
!!

とします。NiledArrayのインスタンスxを作ります。でそれぞれ、

st> Smalltalk at: #x put: (NiledArray new) !
st> x aa: 1 ! <-エラー
st> x bb: 1 ! <-true
st> x cc: 1 ! <-エラー

となります。
or:はコードブロックを取ることができて、なおかつ左側がtrueであればそのコードブロックは実行しないのです。
Cの||と同じと言うことだそうです。うんうん。
posted by ほえ at 16:55| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

もっとサブクラスを!

もっとサブクラスということで、Arrayのサブクラスを作成してみます。
Arrayのindexを範囲を超えて指定してしまうと、エラーになってしまいますが、nilを返すようにしたい。
でも、Arrayを変更してしまうといろいろと…ということで、サブクラスを作ってしまえば良いではないか!

Array variableSubclass: #NiledArray
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: nil !

!NiledArray methodsFor: 'bounds checking'!
boundsCheck: index
^(index < 1) | (index > (self basicSize))
! !

!NiledArray methodsFor: 'basic'!
at: index
^(self boundsCheck: index)
ifTrue: [ nil ]
ifFalse: [ super at: index ]
!
at: index put: val
^(self boundsCheck: index)
ifTrue: [ val ]
ifFalse: [ super at: index put: val ]
! !

で、例えば、

st> Smalltalk at: #x put: (NiledArray new: 3) !
st> (x at: 10) printNl !
nil

とエラーになりませんでした。よしよし。
posted by ほえ at 15:15| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年12月17日

Emacsの設定

emacsの設定をして見ます。

マニュアル(info gst)に、~/.emacsに

(setq auto-mode-alist
(append '(("\\.st\\'" . smalltalk-mode))
auto-mode-alist))

(autoload 'smalltalk-mode "<path>smalltalk-mode.elc" "" t)

を書けとありました。<path>がどこのことだかよくわからなかったので、探してみたら、

/usr/share/emacs/site-lisp/gnu-smalltalk
に、
gst-mode.el
smalltalk-mode.el
がありました。
elcじゃなくて、elでした。

なので、
(autoload 'smalltalk-mode "/usr/share/emacs/site-lisp/gnu-smalltalk/smalltalk-mode.el" "" t)
と書いてみました。
*.stファイルを読み込むと、emacsのステータスライン?に(Smalltalk)と出てきました。よしよし。

C-c mでなにやららしいので、やってみると、
Cannot open load file: /usr/share/emacs/site-lisp/gst-mode.elc
と言われてしまいました。

rootでemacsを起動して、
M-x byte-compile-file /usr/share/emacs/site-lisp/gnu-smalltalk/smalltalk-mode.el

M-x byte-compile-file /usr/share/emacs/site-lisp/gnu-smalltalk/gst-mode.el
としました。バイトコンパイルして*.elcファイルを作成してみたわけです。

.elcをどこに置いていいのかわからずに、結局。

/usr/share/emacs/site-lisp/gnu-smalltalk/smalltalk-mode.elc
/usr/share/emacs/site-lisp/gst-mode.elc
にファイルを置いて、
.emacsには
(autoload 'smalltalk-mode "/usr/share/emacs/site-lisp/gnu-smalltalk/smalltalk-mode.elc" "" t)
と書きました。


こうすると、*.stファイルを読み込んでSmalltalkモードになっている時に、
C-c m
で、ウィンドウが2画面になって、新しいウィンドウでgstが起動します。*gst*となってます。

*.stファイルを開いた方のウィンドウで、
選択範囲の
開始位置で、C-SPC
終了位置で、C-c e
とすると、選択範囲のコードが、*gst*のウィンドウで実行されます。
おお!便利。

選択範囲の最後に!が無いと、勝手につけて、実行してくれます。

C-c d ('M-x smalltalk-doit'と同じ) ! 〜 !を探してくれるようです。
C-u C-c d ???
C-c c メソッドのコンパイル
C-c s スナップショット(ObjectMemory snapshotをやってくれる)
C-c p その場でevaluate?
C-c f イメージの読込
C-c q Smalltalkの終了
C-c m Smalltalkの開始
使い方が良くわかりません。
でも何かができるようです。


posted by ほえ at 17:49| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年12月08日

ソースファイル

今まで

$ gst
GNU Smalltalk ready

st>

と、インタプリタ?で作業をしてきましたが、ソースをファイルに保存して実行できるという事を知りました。
例えば

'Hello world!' printNl !

というファイルをhello.stに保存しておけば、

gst hello.st

で実行できる事を知りました。なんだぁ。
これはGNU Smalltalkならではの機能なのでしょうか?
便利です。

posted by ほえ at 04:25| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年12月07日

デバッグ

・デバッグ

inspectメッセージで、オブジェクトのインスタンス変数をダンプしてくれます。

st> Smalltalk at: #x put: (Interval from: 1 to: 5) !
Interval new "<0xb3d49fe0>"
st> x inspect !
An instance of Interval
start: 1
stop: 5
step: 1
contents: [
[1]: 1
[2]: 2
[3]: 3
[4]: 4
[5]: 5
]
Interval new "<0xb3d49fe0>"
st>

みたいに。

今日はこれだけ。
posted by ほえ at 18:15| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年12月06日

コードブロックその2

・整数ループ

st> 1 to: 20 do: [:x | x printNl ] !
1
2
3
:
:
20

st> 1 to: 20 by: 2 do: [:x | x printNl ] !
1
3
5
:
:
19

st> 20 to: 1 by: -1 do: [:x | x printNl ] !
20
19
18
:
:
1

・Intervals
範囲(range)オブジェクトを作れるということですね。

st> Smalltalk at: #i put: (Interval from: 5 to: 10) !
Interval new "<0xb3cfdce8>"

st> i printNl !
Interval(5 6 ... 10)
Interval new "<0xb3cfdce8>"

st> i do: [:x | x printNl ] !
5
6
7
8
9
10
Interval new "<0xb3cfdce8>"


整数ループで使ったby:も使えます。

st> i := (Interval from: 5 to: 10 by: 2) !
Interval new "<0xb3cff378>"
st> i printNl !
Interval(5 7 10)
Interval new "<0xb3cff378>"
st> i do: [:x| x printNl] !
5
7
9
Interval new "<0xb3cff378>"


・コードブロックを呼び出す

Chckingクラスに、ある金額より多い小切手を探すメソッドを追加します。

st> !Checking methodsFor: 'scanning'!
Checking
st> checksOver: amount do: aBlock
st> history associationsDo: [:assoc|
st> ((assoc value) > amount)
st> ifTrue: [aBlock value: (assoc key)]
st> ]
st> ! !

金額が大きければ、指定したブロックをそのキーをパラメータにして呼び出しています。
value:メッセージをコードブロックが受取ると実行されます。
コードブロックは、value, value: value:value, value:value:value: メッセージを受取れます。0から3個の引数を渡せるという事です。
valueWithArguments:というメッセージも受取れます。これはいくつでも引数を渡せるメッセージです。

試してみます。

st> Smalltalk at: #mycheck put: (Checking new) !
Checking new "<0xb3d06fa0>"
st> mycheck deposit: 250 !
Checking new "<0xb3d06fa0>"
st> mycheck newChecks: 100 count: 40 !
Checking new "<0xb3d06fa0>"
st> mycheck writeCheck: 10 !
100
st> mycheck writeCheck: 52 !
101
st> mycheck writeCheck: 15 !
102
st> mycheck checksOver: 1 do: [:x | x printNl] !
100
101
102
Checking new "<0xb3d06fa0>"
st> mycheck checksOver: 17 do: [:x | x printNl] !
101
Checking new "<0xb3d06fa0>"
st> mycheck checksOver: 200 do: [:x | x printNl] !
Checking new "<0xb3d06fa0>"
st>


checksOverを別の方法で書いてみます。select:メッセージを使います。

st> !Checking methodsFor: 'scanning'!
Checking
st> checksOver: amount do: aBlock
st> | chosen |
st> chosen := history select: [:amt| amt > amount].
st> chosen associationsDo: aBlock
st> ! !
st> mycheck checksOver: 17 do: [:x | x printNl ] !
101->52
Checking new "<0xb3d06fa0>"

前のコードと違い、ユーザの指定したコードブロックにassociationを渡しています。
結果、CheckNum -> CheckValという形で表示することができました。

posted by ほえ at 16:35| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

コードブロックその1 続き

イテレーションとコレクション

小切手の記録を残すために、DictionaryオブジェクトをCheckingクラスに追加します。

なんかparse errorが一杯出るんですけど…。

st> Account subclass: #Checking
st> instanceVariableNames: 'checknum checksleft history'
st> classVariableNames: ''
st> poolDictionaries: ''
st> category: nil !
"Global garbage collection... done"
Recompiling classes...
Recompiling class: Checking
Recompiling selector: #init
a Smalltalk string:1: parse error, unexpected '!', expecting "identifier" or "keyword message"
or "binary message" or '|'
Recompiling selector: #printOn:
a Smalltalk string:1: parse error, unexpected '!', expecting "identifier" or "keyword message"
or "binary message" or '|'
Recompiling selector: #newChecks:count:
a Smalltalk string:1: parse error, unexpected '!', expecting "identifier" or "keyword message"
or "binary message" or '|'
Recompiling selector: #writeCheck:
a Smalltalk string:1: parse error, unexpected '^', expecting "identifier" or "keyword message" or "binary message" or '|'
Checking

もう一回同じことしたら、エラーは出ませんでした。なんでしょうか?
チュートリアルにはエラーは出ないって書いてあるんだけど…。
…そのまま進みます…。

initを修正します。

st> !Checking methodsFor: 'initialization'!
Checking
st> init
st> checksleft := 0.
st> history := Dictionary new.
st> ^ super init
st> !!

historyを初期化してます。

writeCheckも修正します。

st> !Checking methodsFor: 'spending'!
Checking
st> writeCheck: amount
st> | num |
st>
st> "Sanity check that we have checks left in our checkbook"
st> (checksleft < 1)
st> ifTrue: [ ^self error: 'Out of checks' ].
st>
st> "Make sure we've never used this check number before"
st> num := checknum.
st> (history includesKey: num)
st> ifTrue: [ ^self error: 'Duplicate check number' ].
st>
st> "Record the check number and amount"
st> history at: num put: amount.
st>
st> "Update our next checknumber, checks left, and balance"
st> checknum := checknum + 1.
st> checksleft := checksleft - 1.
st> self spend: amount.
st> ^ num
st> ! !

Smalltalkでは'xxx'はString、"xxx"はコメントになります。
Discionaryオブジェクトは、includesKeyメッセージを受取り、
指定されたキーで何か保存していればtrueを返します。
また、at: put: メッセージでDictionaryに保存しています。
小切手番号とその金額になります。

これで動作するチェック機能をそなえたCheckingクラスができました。
後はこれらの情報を表示するメソッドを追加します。

st> !Checking methodsFor: 'printing'!
Checking
st> printOn: stream
st> super printOn: stream.
st> ', checks left: ' printOn: stream.
st> checksleft printOn: stream.
st> ', checks written: ' printOn: stream.
st> (history size) printOn: stream.
st> !
st> check: num
st> | c |
st> c := history
st> at: num
st> ifAbsent: [ ^self error: 'No such check #' ].
st> ^c
st> ! !

小切手の記録を表示するメソッドを作成します。

st> !Checking methodsFor: 'printing'!
Checking
st> printChecks
st> history associationsDo: [ :assoc |
st> (assoc key) print.
st> ' -' print.
st> (assoc value) printNl.
st> ]
st> ! !

historyにassociateionsDoメッセージと、コードブロックを渡しています。
ブロックは引数を受取れます。この例ではSmalltalkでAssociationとして知られる、
キーと値のペアを受取っています。
dictionaryオブジェクトにat:put:メッセージで値を格納する時、
Associationクラスの新しいオブジェクトにパックして入れています。
valueだけが必要な時、do:メッセージを、keyだけ必要な時、keysDo:メッセージを使います。

試しにやってみました。

st> Smalltalk at: #d put: (Dictionary new) !
Dictionary new: 32 "<0xb3cfb5b8>"
st> d printNl !
Dictionary (
)
Dictionary new: 32 "<0xb3cfb5b8>"
st> d at: 'a' put: 'aaa' !
'aaa'
st> d at: 'b' put: 'bbb' !
'bbb'
st> d keysDo: [ :dd| dd printNl ] !
'b'
'a'
Dictionary new: 32 "<0xb3cfb5b8>"
st> d do: [ :dd| dd printNl ] !
'bbb'
'aaa'
Dictionary new: 32 "<0xb3cfb5b8>"
st> d associationsDo: [ :dd| dd printNl ] !
'b'->'bbb'
'a'->'aaa'
Dictionary new: 32 "<0xb3cfb5b8>"

でけた。

つぎはコードブロックその2です。
posted by ほえ at 16:03| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年12月05日

コードブロックその1

コードブロック1

Account/Saving/Checkngの例では、小切手の記録が残りませんし、小切手の数がマイナスになる事もあります。なんとかしましょう。

条件について?
たくさん小切手をかけてしまう件を修正します。
前のメソッドを上書きします。

!Checking methodsFor: 'spending'!
writeCheck: amount
| num |

(checksleft < 1)
ifTrue: [ ^self error: 'Out of checks' ].
num := checknum.
checknum := checknum + 1.
checksleft := checksleft - 1.
self spend: amount.
^ num
! !

※チュートリアルの通り、

self spend: amount
^ num

と書いたら、

stdin:14: parse error, unexpected '^', expecting '!'

っておこられました。

self spend: amount.
^ num

かな?

前の環境を引き続き使っているので、小切手はあと45もありました。
がんばって
c writeChek: 100 !

とかやって、
c inspect !

で、

st> c inspect !
An instance of Checking
balance: -4512
checknum: 150
checksleft: 0
Checking new "<0xb3c88d60>"
st>

としました。
もう一度writeCheckをやれば、エラーになるはずです。
なりました。

st> c writeCheck: 100 !
Object: Checking new "<0xb3c88d60>" error: Out of checks
Checking(Object)>>#error:
Checking>>#writeCheck:
UndefinedObject>>#executeStatements
nil
st> c inspect !
An instance of Checking
balance: -4512
checknum: 150
checksleft: 0
Checking new "<0xb3c88d60>"
st>

残りも0より小さくなりませんでした。
よしよし。


(checksleft < 1)
ifTrue: [ ^self error: 'Out of checks' ].

というところが見慣れませんが、()の部分は、今までどおり<というメッセージを、checksleftに1というパラメータつきで送っているだけです。
ifTrue: より後が新しいところです。

結果のbooleanにifTrueというメッセージを送っています。
そしてこの引数が「コードブロック」です。
コードブロックはオブジェクトです。
ifTrueメッセージを受取ったbooleanは、自分がtrueの場合コードブロックを実行します。
falseの場合は実行しません。

条件式は、booleanオブジェクトで置き換えられるわけです。

この例では、errorメッセージを自分に送っています。
このメッセージは親のObjectクラスによって処理されます。
Smalltalkでは致命的なエラーは、selfにerrorメッセージを送って、Objectクラスから継承したエラーハンドラで処理を行うのが普通です。

ifFalseメッセージもあります。

直接

true ifTrue: [ 'Hello, world!' printNl ] !
false ifTrue: [ 'Hello, world!' printNl ] !
true ifFalse: [ 'Hello, world!' printNl ] !
false ifFalse: [ 'Hello, world!' printNl ] !

と書いて試すことができますよ。

Smalltalkには制御構造?がないです。
Booleanクラスの振る舞いとして定義されるという事です。

面白いですね。
posted by ほえ at 18:11| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年11月30日

サブクラスを作る

サブクラスを作ります。

AccountクラスのサブクラスとしてSavingクラスを作ります。

st> Account subclass: #Savings
st> instanceVariableNames: 'interest'
st> classVariableNames: ''
st> poolDictionaries: ''
st> category: nil !


Accountクラスを継承しているので、「spend:」「deposit:」メッセージが受取ります。
Savingクラスではさらにinterestフィールドを0にするメソッドを定義します。

st> !Savings methodsFor: 'initialization'!
Savings
st> init
st> interest := 0.
st> ^ super init
st> ! !

また、Accountクラスはnewメッセージを受取ると、作成したオブジェクトにinitメッセージを送るように作成されていました。
サブクラスのSavingクラスでもnewメッセージを受取ると、initメッセージを受取ります。そしてinterestフィールドを0に設定し、親クラスにinitメッセージを送ります。

さらにメソッドを2つ追加します。

st> !Savings methodsFor: 'interest'!
Savings
st> interest: amount
st> interest := interest + amount.
st> self deposit: amount
st> !
st> clearInterest
st> | oldinterest |
st>
st> oldinterest := interest.
st> interest := 0.
st> ^oldinterest
st> ! !


もう1つ、Checkingクラスというサブクラスを作成します。

st> Account subclass: #Checking
st> instanceVariableNames: 'checknum checksleft'
st> classVariableNames: ''
st> poolDictionaries: ''
st> category: nil !
Checking
st>


メソッドを1つ定義します。

st> !Checking methodsFor: 'Initialization'!
Checking
st> init
st> checksleft := 0.
st> ^super init
st> ! !



小切手を使うメソッドを定義します。
…小切手の使い方ってわからないです…。

st> !Checking methodsFor: 'spending'!
Checking
st> newChecks: number count: checkcount
st> checknum := number.
st> checksleft := checkcount
st> !
st>
st> writeCheck: amount
st> | num |
st>
st> num := checknum.
st> checknum := checknum + 1.
st> checksleft := checksleft - 1.
st> self spend: amount.
st> ^ num
st> !
st> !

newChecksメソッドは、checknumberを何番から開始するかを設定します。
また、checksの数を設定します。
writeCheckメソッドは、次のchcknumberを設定し、checksleftを1つ減らします。


st> Smalltalk at: #c put: (Checking new ) !
Checking new "<0xb3cfed60>"
st> c printNl !
a Checking with balance: 0
Checking new "<0xb3cfed60>"
st> c deposit: 250 !
Checking new "<0xb3cfed60>"
st> c printNl !
a Checking with balance: 250
Checking new "<0xb3cfed60>"
st> c newChecks: 100 count: 50 !
Checking new "<0xb3cfed60>"
st> c printNl !
a Checking with balance: 250
Checking new "<0xb3cfed60>"
st> (c writeCheck: 32) printNl !
100
100
st> c printNl !
a Checking with balance: 218
Checking new "<0xb3cfed60>"


この時点でm、checknumは101、chcksleftは49になっているのです。

チュートリアルにあるように、このままだとchecknumとchecksleftがどうなっているのか良くわからないので、前と同じ要領で出力するようにしてみます。


st> !Checking methodsFor: 'printing'!
Checking
st> printOn: stream
st> super printOn: stream.
st> stream nextPutAll: ' with checknum: '.
st> checknum printOn: stream.
st> stream nextPutAll: ' with checksleft: '.
st> checksleft printOn: stream
st>
st> ! !
st> c printNl !
101a Checking with balance: 218 with checknum: with checksleft: 49
Checking new "<0xb3cfed60>"


一応出ました。こんなめんどくさい事しなくても、もっと簡単にできるんだと思いますが…。
まあ、一応。

つぎはいよいよコードブロックです。

…定義したメソッドを表示したりできないのかしら?どんな定義だったか参照するのが…。
posted by ほえ at 16:05| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年11月29日

クラスを作る

ObjectMemory snapshot: 'filename' !

で環境が保存できます。
最後の!を忘れて、直後に終了すると悲しい事になりそうです。

読込は
gst -I filename

です。

ObjcetMの後にTABキーで、ObjectMemory、
snapの後にTABキーで、snapshot
のように入力補完してくれます。これ便利。


新しくクラスを作る。

st> Object subclass: #Account
st> instanceVariableNames: 'balance'
st> classVariableNames: ''
st> poolDictionaries: ''
st> category: nil !

で、クラスAccountが作成されます。
全てのオブジェクトはObjectのサブクラスになります。


クラスにコメントをつける。

Account comment: 'I represent a place to deposit and withdraw money' !

へえー。

クラスのコメントを表示する。
(Account comment) printNl !
->
'I represent a place to deposit and withdraw money'

(Integer comment) printNl !
->
'I am the abstract integer class of the GNU Smalltalk system. My
subclasses' instances can represent signed integers of various
sizes (a subclass is picked according to the size), with varying
efficiency.'

'this'じゃなくて'I'なんですね。面白い。


メソッドを定義する。

st> !Account class methodsFor: 'instance creation' !
Account class
st> new
st> | r |
st> r := super new.
st> r init.
st> ^r
st> ! !


Account classはAccountクラス自身に送られるメッセージを定義しますよという事。
methodsFor: 'instance creation' はドキュメント用。
new 以降、 !! までは、メッセージnewを受取った時のアクションを定義しています。

.(ピリオド)は、行の終わりを示します。
| r | は、ローカル変数rを作成。
super は1つ上のクラス。ここではObjectクラスを指します。
r init で作成されたオブジェクトにメッセージinitを送っていますが、initはまだ書いていないのでエラーになります。
^r は、rにアタッチされたものをreturnします。つまり新しいaccountを返却しています。


インスタンスメソッドを定義する。

st> !Account methodsFor: 'instance initialization'!
Account
st> init
st> balance := 0
st> ! !

先ほど定義したのは、クラスメソッドです。
クラスメソッドは
 Account class methodsFor: ...

インスタンスメソッドは
 Account methodsFor: ...

です。
インスタンスメソッドinitは^rのようなreturnするコードは書いていません。
何も返却しない場合、オブジェクト自身が帰ります。


作成したクラスを見てみましょう。

st> Smalltalk at: #a put: (Account new) !
Account new "<0xb3c70080>"
st> a printNl !
an Account
Account new "<0xb3c70080>"

さらにメソッドを追加。

st> !Account methodsFor: 'printing'!
Account
st> printOn: stream
st> super printOn: stream.
st> stream nextPutAll: ' with balance: '.
st> balance printOn: stream
st> ! !
st> a printNl !
an Account with balance: 0

さらに追加。

st> !Account methodsFor: 'moving money' !
Account
st> spend: amount
st> balance := balance - amount
st> !
st> deposit: amount
st> balance := balance + amount
st> ! !
st> a deposit: 125!
Account new "<0xb3c70080>"
st> a printNl !
an Account with balance: 125
Account new "<0xb3c70080>"
st> a spend: 100 !
Account new "<0xb3c70080>"
st> a printNl !
an Account with balance: 25
Account new "<0xb3c70080>"
st>

できた!

posted by ほえ at 15:42| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年11月15日

Dictionaryの件

SmalltalkにはDictionaryがあります。

x := Dictionary new!
x printNl !
-> Dictionary(
)

x at: 'One' put: 1 !
x printNl !
-> Dictionary(
'One' -> 1
)

x at: 'Two' put: 2 !
x at: 1 put: 'One' !
x at: 2 put: 'Two' !

x printNl !
-> Dictionary(
1->'One'
2->'Two'
'Two'->2
'One'->1
)

(x at: 1) printNl !
->'One'

(x at: 'One') printNl !
->1



さて、
Smalltalk at: #x put: 0 !

について。

Smalltalk環境では、名前'Smalltalk'はDictionaryとして存在しているそうです。
変数を使おうとしていきなり
 y := 0 !

とすると、'assignment to undeclared variable y'として怒られます。
Dictionary'Smalltalk'に追加してから使います。

Smalltalk at: #y put: 0 !
y := 100 !
y printNl !
-> 100

(Smalltalk at: #y) printNl !
-> 100


Dictionaryならキーに文字列でも良いはずです。

Smalltalk at: 'y' put: 'yyy' !
(Smalltalk at: 'y') printNl !
-> error: Invalid argument y: key not found

参照しようとするとエラーになっちゃいました。

'y' printNl !
-> 'y'

これは当然ですね。

でも、

(Smalltalk includes: #y) printNl !
-> true
(Smalltalk includes: 'y') printNl !
-> true

となるので、確かに'y'は存在しているのです。


Smalltalk inspect !

で、Dictionaryのキーを一覧することができます。

posted by ほえ at 15:08| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年10月10日

オブジェクトを作る件(ArrayとSet)

Smalltalkでオブジェクトを作成してみます。
なにやらこのようなおまじないが必要だそうです。
st> Smalltalk at: #x put: 0!

何でしょう?気にしないで先に進めってチュートリアルにあるので、先に進みましょう。
※「st> 」はプロンプトです。「Smalltalk at: #x put: 0!」って「Smalltalk at: 」がプロンプトなの?とか一瞬思ったのでプロンプトも書く事にしました。
※小文字の「smalltalk」はエラー。「at :」、「put :」のように「:」をスペースで離したらエラー。「# x」はOKでした。

配列を作ります。
st> x := Array new: 20 !

「gst -V」で起動しているので、
returned value is Array new: 20 "<0xb3cb9ff8>"
って出てきました。
ちなみに先ほどの「Smalltalk at:…」を実行しないで、配列を作ろうとしたら、
stdin:1: assignment to undeclared variable x

って出てきました。変数の宣言が必要って事?

「x := …」は x をオブジェクトに接続しているそうです。x に他のオブジェクトを割り当てないかぎり、そのオブジェクトを参照できると。
配列の要素を変更には、「:=」は使いませんよ、メッセージをオブジェクトに送らないと変更できませんよと。そういう事のようです。
st> (x at: 1) printNl !

で nil が表示されました。「(x at: 0) printNl !」はエラー「Invalid index 0: index out of range」になりました。index は 1 からのようですね。
でもチュートリアルでは slot と書いてあります。最初のスロットに99を設定するって。
st> x at: 1 put: 99 !

これで「(x at: 1) printNl !」で「99」が表示されるようになりました。
st> (x at: 1) put: 98 !

はエラーになりました。「99はput:は解らないよ!」ですと。()でIntegerの99が返ってきていて、それにメッセージ「put:」を送っているというわけですね。

「at:」や「put:」にコロンがついているのは、これらが引数を取るからだそうです。大体の場合そういう風になっているようです。

この後、x に別のオブジェクトを割り当てて、Array が使われなくなると自動的にガベージコレクトされると。
st> x : Set new !

で x に新しいオブジェクトが割り当てられます。
st> x printNl !

で「Set ()」。??なんでしょう??続けて、
st> x add: 5 !
st> x add: 7 !
st> x add: 'foo' !
st> x printNl !

で「Set ('foo' 5 7)」と表示されました。'foo'は一番最後に add したのに。
st> x add: 5; add: 7; add: 'foo' !

とも書けるそうです。最後に「;」をつけるとおこられます…。

st> x add: 5; add: 5; add: 5 !

としても
st> x printNl !

は「Set ('foo' 5 7)」です。

削除するには「remove:」。
st> x remove: 5 !

で「Set ('foo' 7)」になります。この時の返り値?は「5」でした。

値が存在するかは「includes:」
st> (x includes 7) printNl !

はtrue。
st> (x includes 5) printNl !

はfalseでした。
このtrueとfalseもオブジェクト。booleanオブジェクトです。
posted by ほえ at 16:01| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

2007年10月05日

gnu-smalltalkを入れてみた

なんだかいろいろ調べていたらやたらSmalltalkの事が出てくるので、入れてみるか?って感じで入れてみました。

VMware上のdebian etchを使いました。
su
apt-get install gnu-smalltalk*
でインストール完了。
dpkg -L gnu-smalltalk-doc
でインストールされたファイルを見てみたら、
/usr/share/info/gst.info.gz
とかなってたので、
info gst
でマニュアルを見てみました。矢印キーでスクロールしていくと
Tutorial:
* Getting started:: Starting to explore GNU Smalltalk
とあったので、Enterで見てみました。
* Starting Smalltalk:: Starting up Smalltalk
でチュートリアルをはじめてみました。
$ gst -V

で起動。-Vをつけるとなにか実行すると色々メッセージが出てきます。
まずはHello, worldを。
gst> 'Hello, world' printNl!

'Hello, world'につづけてなにやらメッセージが色々と。


Execution begins...
'Hello world'
returned value is 'Hello world'
2063 byte codes executed
which took 0.001 seconds, giving 2063000 bytecodes/sec
97 primitives, percent 4.70
self returns 21, inst var returns 5, literal returns 7
43 method cache lookups since last cleanup, percent 2.08
19 method cache hits, 24 misses, 44.19 percent hits
st>

チュートリアルを読むとSmalltalkインタプリタは'!'が出てくるまでテキストを読んでいって、実行するらしいです。

1.Stringオブジェクト'Hello world'を作成。
2.'printNl'というメッセージを作成したオブジェクトに送る。
という事になるそうです。

(9 + 7) printNl !

では
Integerオブジェクト'9'に'+'というメッセージをIntegerオブジェクト'7'を引数にして送るという事だそうで。
で、Integer'16'というオブジェクトを作成して、それに'printNl'というメッセージを送ると。で、'16'と表示されると。ははぁ、そうですか。

Smalltalkの世界では、「オブジェクトにメッセージを送る」しかできる事がないのだそうです。

posted by ほえ at 14:22| Comment(0) | TrackBack(0) | Smalltalk | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。