May 05, 2005

自由自在 Squeak プログラミング:第7章 Morphic 入門(1)

Squeak Logo
squeakでグラフィカルなプログラムを作る時に、Morphic(モーフ)を利用する。
squeakのデスクトップもMorphicで作られている。
モーフの生成:Morph クラスのnewメッセージで生成する。
例:Morph のインスタンスを生成して、ワールドに表示。
  morph := Morph new.
  morph openInWorld. "do it"
モーフを削除するには、インスタンスに delete メッセージを送る。
  morph delete. "do it"
※:Morph new openInWorld. "do it" でも生成できるが、deleteを送るインスタンスがないので、モーフを右クリックし、ハローを表示して左上の(×)ボタンで閉じる。

ハロー(halo):モーフを右クリックすると、モーフの周りに表示されるアイコンを「ハロー halo:後光」と言う。
halo

  1. 削除(Move to trash):クリックすると、モーフがゴミ箱へ移動される。
  2. 最小化(Collapse):タイトルバーのみの形状で、デスクトップの端に表示される。もう一度クリックすると元に戻る。
  3. ビューアを開く(Open a Viewer for me):タイルスクリプト(Squeak Toys)で操作するための、「ビューア」を表示する。
  4. タイル生成(Make a tile representing this object):タイルスクリプト(Squeak Toys)で使用する、そのモーフのタイルを生成する。
  5. 回転(Rotate):ドラッグする事で、モーフを任意の角度に回転する。
  6. メニュー(Menu):モーフに関するポップアップメニューを表示。
  7. グラブ(Pick up):入れ子になったモーフから、選択したモーフを取り出す。
  8. ムーブ(Move):ドラッグする事で、モーフを移動する。(ハローを使わなくても、モーフは移動可能)
  9. 複製(Duplicate):ドラッグすると、モーフのコピーが作成される。
  10. デバッグ(Debug):インスペクタなど、デバッグ用ポップアップを表示。
  11. スポイト(Change color):カラー選択ツールを表示し、モーフの色を変更できる。
  12. リサイズ(Change size):ドラッグすると、モーフのサイズを変更できる。
※:カッコ内は各ハローにカーソルを合わせた時に表示されるふきだしの内容です。

<インスペクタからの操作>:状態を見るだけでなく、値を変更やメッセージを送る事ができる
生成したモーフを右クリックしてハローを表示し、10.デバッグ(スパナアイコン)をクリックすると、「Morph」メニューが開きますので、一番上の「inspect morph」を選択して、モーフのインスペクタウインドウを表示します。
inspect morph
・インスペクタからのメッセージ送信:インスペクタウインドウのメッセージ送信ペインから、モーフにメッセージを送る事ができる。
 例:self position. "print it" boundsプロパティと同じ、位置 256@128 情報を得られる。
   self position: 100@100. "do it" キーワードメッセージpostion: に位置を渡す事でモーフを移動できる。
・変数ペイン上での直接書き換え:モーフのプロパティを選択して、変数ペインに表示された値を直接変更する事もできる。
 例:プロパティ欄から「color」を選び、変数ペインの内容を「Color blue」に変えて変数ペイン上で中ボタンクリックし"accept"(command - S)します。メッセージ送信ペインでキーワードメッセージ color: を使った時と違い、モーフに変更が連絡されないので、モーフをマウスで一度クリックするか、またはメッセージ送信ペインで self changed. "do it" します。(Morphクラスのインスタンスメッセージcolor: を見ると、最後にself changed が有ります。)

<オブジェクト・エクスプローラ>:複雑なモーフ(オブジェクト)を観察する時に便利な汎用ツール
モーフを右クリックしハローから、10.デバッグ(スパナアイコン)をクリック、「Morph」メニューの上から3つ目「explore morph」メニューを選択します。
横向き三角マークをクリックすると、その項目に関してのより詳しい内容を見る事ができる。

<ビューアからの操作>:モーフ専用のツール
エンドユーザが簡単にモーフの値を見たり、タイルスクリプトに利用するために使用される。
モーフを右クリックし3.ビューア(目アイコン)をクリックすると、画面右側にビューアが表示されます。
※:エンドユーザ用ビューアの位置(Morph's x, y)とbounds プロパティとは、座標系が違う為、値が違っている。


10:18:05 | 3k1 | comments(0) | TrackBacks

April 17, 2005

自由自在 Squeak プログラミング:第6章 基本クラスライブラリ探検─その2(3)

Squeak Logo
<ストリームクラス群>:ストリームは一続きのデータを処理するもので、必ずなんらかのコレクション(クラスのインスタンス)を元に生成するが、殆どの場合は String を扱う時に利用される。

外部ストリーム:ファイルやソケットを介して、Squeakの外とやりとりするためのストリーム
FileStream:ファイルに対して読み書きを行うストリーム。
基本的な使い方は、ファイルストリームオブジェクトをファイル名を指定して生成し、データを書き込み/読み込み後、ファイルストリームを閉じる。
ファイルストリームを閉じ忘れると、Squeakを終了するまでファイルにアクセスできない。
 ファイルへ書き込む例:
  fs := FileStream newFileNamed: 'test.txt'.
  fs nextPutAll: 'テスト', (Character cr) asString, 'です'.
  fs nextPut: Character cr.
  fs close. "do it"
 ※:同じファイル名で書き込もうとすると、上書きか、名前を変えるか、キャンセルするかのダイアログが表示される。
 ※:日本語版Squeakの場合、文字コードはUTF-8、改行コードはCR(Character value: 13)になる。
 ファイルを読み込む例:
  fs := FileStream readOnlyFileNamed: 'test.txt'.
  Transcript cr.
  [fs atEnd] whileFalse: [Transcript show: fs next; space. ].
  fs close. "do it"
 ※:ファイル読み込み時も、最後にファイルストリームを閉じる事を忘れないようにする。
 バイナリファイルを書き込む例:
  fs := FileStream newFileNamed: 'test.bin'.
  fs binary. "ファイルストリームをバイナリモードに設定する"
  fs nextPutAll: #(84 69 83 84 13) asByteArray. "配列をByteArrayにキャストして書き込み"
  32 to: 122 do: [:temp | fs nextPut: temp. ]. "32から122までを書き込む"
  fs close. "do it"
 ※:バイナリファイルへの書き込みは、0から255の整数のみに制限されている。
改行コードの扱い:Mac(Squeak)=CR(Character value: 13), Unix=LF(Character value: 10), Windows=CR, LF(Character value: 13, Character value: 10)
Squeakはマルチプラットフォームで動作するので、改行コードを区切りにしたい場合、FileStreamのサブクラス「CrLfFileStream」クラスを利用する。
 1行毎に読み込む例:
  fs := CrLfFileStream fileNamed: (FileStream fullName: 'test.txt').
  [fs atEnd] whileFalse: [Transcript cr; show: (fs upTo: Character cr).].
  fs close.
 ※:自由自在のサンプルそのままのFileStream fullName: はマルチバイト対応? 無くても動く事は動くが…。
その他の重要なストリーム
(1) RWBinaryOrTextStream:FileStreamの内部メモリ版(WriteStreamのサブクラス)で、大きなデータをいきなりファイルに書かない場合に利用。また、ReadStrem, WriteStreamの様に任意のオブジェクトではなく、バイト列と文字列に最適化されている。
 Stringを指定して生成する例:String new: を ByteArray new: にすると、バイトモードになる。
  ws := RWBinaryOrTextStream on: (String new: 50). "StringでRWBinaryOrTextStreamを生成"
  ws nextPutAll: 'Character value:33, 34, 35, 36, 37 = '. "文字列をセット"
  ws nextPutAll: (#(33 34 35 36 37) asByteArray). "バイト列をセット"
  ws contents. "print it"
 生成時に指定せず、インスタンスに明示的にメッセージを送る例:
  rws := RWBinaryOrTextStream on: (String new: 10).
  rws nextPutAll: 'Squeak!'.
  rws contents. "ここで print it すると、String new:で生成したので文字列"
  rws binary.
  rws contents. "ここで print it すると、binaryメッセージを送ったのでバイト列"
  rws text.
  rws contents. "ここで print it すると、textメッセージを送ったのでまた文字列"
(2) TranscriptStream:WriteStreamのサブクラスで、そのインスタンスは、グローバル変数 Transcript にバインド(代入)されている。(Transcript inspect. すると、インスペクタのタイトルがTranscriptStreamになっている。)
 Transcript cr; show: 'Squeak!'. を分解した例:Transcript browseHierarchy でインスタンスメソッドshow:を参照
  Transcript nextPut: Character cr. "改行を追加"
  Transcript nextPutAll: 'Squeak!'. "文字列を追加"
  Transcript endEntry. "表示を更新"
(3) DataStream, ReferenceStream, SmartRefStream:複雑な構造を持つオブジェクトを、コンパクトなバイト列へ変換し、保存/復元するストリーム群。
 i. DataStream:比較的単純なデータを保存/復元するのに利用。
 ii.ReferenceStream:複雑な参照関係を持ったオブジェクトを保存/復元するのに利用。
 iii.SmartRefStream:ReferenceStreamの拡張版で、インスタンスのクラス情報もバイト列に含まれる。
 DataStreamの例:外部ストリーム
  ds := DataStream fileNamed: 'objects.bin'. "ファイル名を指定して生成"
  ds nextPut: '今日は : '. "String オブジェクトの書き込み"
  ds nextPut: Date today. "Date オブジェクトの書き込み"
  ds close.
  書き出したファイルからバイト列を読み、オブジェクトに戻す例
  ds := DataStream fileNamed: 'objects.bin'. "ファイル名を指定して生成"
  [ds atEnd] whileFalse: [ Transcript cr; show: ds next. ]. "バイト列からオブジェクトに復元"
  ds close.
 DataStreamの例:内部ストリーム(クラスメソッドを使用)
  sb := DataStream streamedRepresentationOf: '日本語'. "バイト列化して保存"
  Transcript cr show: (DataStream unStream: sb). "バイト列からオブジェクトに復元"

ensure: による閉じ(close)忘れの防止:エラー処理構文
書き方:[エラーが起きるかもしれない処理] ensure: [エラーの有無に関わらず実行する処理]
 例:エラー画面の[Abandon]ボタンをクリックしても、ensure: ブロックは必ず実行される。
  [ans := Time now seconds ¥¥ 2.
  Transcript show: '1 / ', ans asString, ' = '.
  ans = 0 ifTrue: [Transcript show:'エラー!! ZeroDivide:[Abandon]をクリック'. 1 / ans.]
  ifFalse:[Transcript show: 1 / ans].
  Transcript show:':計算されました。'.
  ] ensure: [Transcript show:':ensure:を実行'; cr.].


14:33:11 | 3k1 | comments(0) | TrackBacks

March 27, 2005

自由自在 Squeak プログラミング:第6章 基本クラスライブラリ探検─その2(2)

Squeak Logo
<ストリームクラス群>:ストリームは一続きのデータを処理するもので、必ずなんらかのコレクション(クラスのインスタンス)を元に生成するが、殆どの場合は String を扱う時に利用される。

基本(内部)ストリーム:メモリ上のみに存在するストリーム
ReadStream:読み込み専用のストリーム。「on:」クラスメソッドの引数にコレクションクラス(Array, String クラスなど)のインスタンスを渡して生成できる。
 例:rs := ReadStream on: #(a b c d). rs inspect. "do it"
または、コレクションクラス(Array, String クラスなど)のインスタンスに、「readStream」メッセージを送る事でも、生成できる。
 例:rs := #(a b c d) readStream. "inspect it" rs := 'abcd' readStream. "inspect it"
next, next: メッセージ:ストリームに「next」メソッドを送る事で、要素を順に取り出せる。「next:」メッセージで引数分だけ取り出す事もできる。
※:コレクションの最後の要素を超えると「nil(不定)」が返って来る。
 例:rs := '12345' readStream. rs next. "print it" rs next: 4. "print it" rs next. "print it"
定番の書き方:「whileFalse:」メソッドを使い、ストリームに「atEnd」メソッドを送って判定する。
 例:rs := 'Hello Squeak!' readStream. [rs atEnd] whileFalse: [Transcript space; show: rs next]. "do it"
upToEnd メッセージ:next 等で読み込み後、最後まで全て取り出す時に使用する。
 例:rs := 'Hello Squeak!' readStream. rs next:6. rs upToEnd. "print it"
contents メッセージ:next 等で読み込んだ位置に関係なく、最初から最後まで全て取り出す時に使用する。
 例:rs := 'Hello Squeak!' readStream. rs next:6. rs contents. "print it"
position, position: , resetメッセージ:現在の読み取り位置をpositionで確認、positon: で読み取り位置を先頭からの絶対値で設定する事ができる。position: の引数がストリームの要素を超える場合はエラー。 resetでpositionの値をゼロに戻せる。
 例:rs := 'Hello Squeak!' readStream. rs next:6. rs position. "print it" rs position:12. rs next. "print it" rs reset. rs position. "print it"
skip: メッセージ:現在の読み取り位置から相対的に読み取り位置を指定できる。負の値で戻る事もできる。ストリームの要素数を超えたり、ゼロ以下になるとエラー。
 例:rs := 'Hello Squeak!' readStream. rs next:6. rs skip:6. rs next. "print it" rs skip:-6. rs next. "print it"
upTo: メッセージ:引数の要素までを取り出す。positionの値は、一致した要素になる。引数と一致する要素が無ければ最後まで取り出す。
 例:rs := 'Hello Squeak!' readStream. rs upTo:(Character space). "print it" rs upTo:(Character space). "print it"
skipTo: メッセージ:引数の要素までpositionを移動するだけ。upToの様に、要素を返さない。
 例:rs := 'Hello Squeak!' readStream. rs skipTo:(Character space). rs position. "print it"
peek メッセージ:nextメッセージと同じく、次の要素を取り出すが、positionを変化させない。
 例:rs := 'Hello Squeak!' readStream. rs next. rs position. "print it" rs peek. "print it" rs position. "print it"

WriteStream:書き込み専用のストリーム。
on: メッセージ:on: クラスメソッドの引数に、書き込むおおよその要素数を持ったコレクションクラスのインスタンスを指定して、ストリームを作成する。
最初に指定した要素数を超える要素を書き込んだ場合でも、自動で拡張されるので要素数ゼロでも構わないが、拡張作業が必要な為、その分動作が遅くなる。
 例:要素数ゼロで作成:WriteStream on: ''. "inspect it"
 例:要素数を指定して作成:WriteStream on: (Array new:10). "inspect it"
with: メッセージ:on: メッセージと同じく、引数に渡したコレクションでストリームを作成するが、on: ではストリームに要素を追加すると上書きされるのに対し、with: で作成したストリームは最初に渡したコレクションを保持して、その後ろに要素が追加される。
 on: の例:WriteStream on: (Array new:5). "inspect it"
 with: の例:WriteStream on: (Array new:5). "inspect it"
on: と with: で作成したストリームでは、要素が追加される位置を示す position の値が違っている事が分かる。また、on: に渡した引数が意味の有るコレクションでも、取り出す事ができない様になっている(上書きされてしまうから?)。
 on: の例:(WriteStream on: 'Hello Squeak') contents. "print it"
 with: の例:(WriteStream on: 'Hello Squeak') contents. "print it"
nextPut: メッセージ:引数に要素を一つ指定して、ストリームに追加する事ができる。
 例:ws := WriteStream on: ''. ws nextPut: $a. ws nextPut: Character space. ws nextPut: $A. ws contents. "print it"
nextPutAll: メッセージ:引数にコレクション(のインスタンス)を指定して、ストリームに追加する事ができる。
 例:ws := WriteStream on: ''. ws nextPutAll: 'Hello'. ws nextPut: Character space. ws nextPutAll: 'Squeak!'. ws contents. "print it"


17:18:14 | 3k1 | comments(0) | TrackBacks

自由自在 Squeak プログラミング:第6章 基本クラスライブラリ探検─その2(1)

Squeak Logo
文字列クラス群:String, Character
<String>:StringクラスはCollectionクラスのサブクラスの為、イテレート系プロトコルが使用できる。Stringクラスのインスタンスは、シングルクォーテーションで囲んだリテラル「'文字列'」で作成できる。
文字列の連結:「,」カンマメッセージで文字列を連結できる。 例:'abcdef' ,'123456'. "print it"
※:文字(Character)はCollectionクラスのサブクラスではないので、カンマメッセージで連結できない。asStringメッセージでキャストする必要が有る。
文字列の比較:「=」で文字列同士を比較できる。 アルファベットの大文字小文字を区別しないで比較する「sameAs:」メッセージも有る。 例:'Squeak' = 'squeak'. "print it" 'Squeak' sameAs:'squeak'. "print it"
文字列のマッチ:「match:」メッセージで文字列の部分マッチを判断できる。マッチするとtrue、マッチしないとfalseを返す。 判断の際、大文字小文字は区別されず、「*」が0文字以上の任意の文字列、「#」が任意の1文字になる。 例:'h#llo sque*' match:'Hello Squeak!'. "print it"
文字列の検索:「(検索される文字列) findString:(検索する文字列) startingAt:(検索開始位置)」メッセージが使える(大文字小文字は区別される)。 例:'Hello Squeak' findString:'Squeak' startingAt:5. "print it"
区切り文字で分割:「(文字列) findToTokens:'(区切り文字)'」メッセージが使える。 例:'Hello Squeak' findTokens:$e. "print it" 'Hello/Squeak:!' findTokens:'/:'. "print it"
文字列の置換:「(文字列) copyReplaceAll:(検索文字列) with:(置換文字列)」メッセージが使える。戻り値は置換された文字列のインスタンスで、オリジナルのインスタンスは変更されない。 例:'Hello world!' copyReplaceAll:'world' with:'Squeak'. "print it"

<Character>:リテラル「$(文字)」でインスタンスを作成できる。 Characterクラスのクラスメソッドカテゴリ「accessing untypeable characters」に、リテラルでは生成しにくいスペースやタブを生成するメッセージが有る。 例:Character space inspect. "do it"
アスキーコードから文字を生成:クラスメソッド「value:」メッセージにアスキーコードを指定して、文字を生成できる。 例:Character value:65. "print it" Character value:(65+32). "print it"
文字からアスキーコードを得る:インスタンスメソッド「asciiValue」メッセージを使う。 例:$z asciiValue. "print it"
文字列への変換:オブジェクトを文字列に変換するには、「asString」メッセージを使用する。 例:12345 asString. "inspect it" Time now asString. "inspect it"
Squeakの多言語対応:MultiString (AbstractStringのサブクラス) と MultiCharacter (Characterのサブクラス) が有る。
これらのインスタンスは、日本語などのマルチバイト文字列や文字を作成した時に作られる。 例:'日本語' "inspect it" '日本語' last. "inspect it"


13:44:01 | 3k1 | comments(0) | TrackBacks

March 13, 2005

自由自在 Squeak プログラミング:第5章 基本クラスライブラリ探検─その1

Squeak Logo
<ブロック>:ブロックは「BlockContext」クラスから作られるオブジェクトで、単なる記号ではない。
・[ ]と書くだけでインスタンスが作成される。[ ] value と「value」メッセージを送るとブロック内の文が実行される。(valueメッセージが送られるまでブロック内は実行されない。)
例:valueTest := [1 + 2]. Transcript cr; show: valueTest value. 選んで"do it"すると、Transcriptに3が表示される。
・引数付きブロックは、[:引数| ]と書く。[ :引数| 引数] value: 'xxx' とvalue: メッセージで引数に値を渡せる。
例:[:arg | arg + 1] value: 1. を"inspect it"すると、SmallInteger:2が返って来る。
※:Squeakでは「value: value: value: value:」と4つの引数まで用意されている。それ以上の引数の場合は配列を渡すメッセージが用意されている。
例:[:arg1 :arg2 | arg1 + arg2] value: 1 value: 2. を"inspect it"すると、SmallInteger:3が返って来る。
・ブロック内リターン [↑ ] は、ブロックの中で「↑(^キャレットで入力する)」を書くと、どんなにブロックがネスト(入れ子)されていても、そこで処理を終了してブロックから外に抜け出す。(ブロックを一つだけ抜け出す処理はSqueakには無い。)

<日付と時間>:
Dateクラス:日付を表すクラス。標準の書式は DDMMYYYY(日月年) newではなく、Date today などを使用する。
Timeクラス:時間を表すクラス。標準の書式は hour:minute:second am/pm (時:分:秒 午前/午後)。 newではなく、Time now などを使用する。

<コレクションクラス群>:コレクションクラス群のルートは Collection(抽象)クラス
順序無しコレクション:Bag, Set
・Bag:中身にどんなオブジェクトでも入れられる。順序は無関係で、重複もOK。
・Set:Bagと同じでどんなオブジェクトでも入れられるが、重複は不可。
順序付きコレクション:Array, OrderedCollection, SortedCollection
・Array:順序付きで、作成時のサイズが固定のコレクション。個々の要素は変更できるが、要素数を追加/削除できない。リテラル #(要素1 要素2 …) で作成可能。
・OrderedCollection:順序付きでサイズ可変のコレクション。 newでサイズなしを作成して、add: や addFirst:で要素を追加可能。
・SortedCollection:要素の並べ替えを自動的に行うコレクション。 標準のソート順は <= での比較。 sortBlock: [:a :b | a > b]などでソート方法を指定してSortedCollectionのインスタンスを生成できる。
キー付きコレクション:Dictionary, IdentDictionary
・Dictionary:辞書の様に、「キー」と「値」のペアで情報を保持する(順序は保証されない)。Dictionaryは = で「キー」を比較するため、キーには数字や文字列など、= で比較できるものを使用する。
・IdentDictionary:IdentDictionary は、== で「キー」を比較する。 == でオブジェクト(VM内のポインタ)を比較する為、検索は高速だが、キーとして文字列が使い難くなる。(#でシンボルにするなど、工夫が必要。)

コレクションの変換:Collectionクラスのインスタンスメソッドカテゴリ「converting」内に「as<クラス名>」で幾つか変換用メソッドが定義されている。「コレクションクラスのインスタンス as<クラス名>」で、値がコピー・変換され、<クラス名>型の新たなインスタンスが返って来る。

yourself:コレクションクラスのインスタンスメソッド(add:, at:put: など)の多くは、その戻り値がレシーバ(コレクションクラスのインスタンス)では無く、インスタンスメソッド(add:, at:put: など)の引数になっている為、yourself メッセージを(カスケード ; の最後に)送信する事で、明示的にレシーバ自身を返したい時に使用する。
例:
yourselfが無い時:(Bag new) add: 1; add: 2; add: 3.:"inspect it"すると、add: 3の引数 3 が返り、望んだ結果にならない。
yourselfが有る時:(Bag new) add: 1; add: 2; add: 3; yourself.:"inspect it"すると、 a Bag(1 2 3) と望んだものになる。

イテレート(繰り返し)プロトコル:コレクションクラス群の要素を、繰り返し取り出すメソッド。(意味の似通った手続き群をプロトコルと呼ぶ)
・do:<コレクション> do: [:each | <処理>]:コレクションから連続して要素を取り出し、ブロック内の処理を行う。
・select:<コレクション> select: [:each | <比較式>]:コレクションから取り出した要素をブロック内の式で比較し、trueの要素を取り出す。
・reject:selectとは逆に、ブロック内の式で比較した結果が、falseの要素を取り出す。
・detect:ブロック内の式で比較し、最初にtrueになった要素のみを返して、そこで処理を終了する。trueの要素が無ければエラー(error: 'Object is not in the collection.')を返す。 見つからなかった時に処理をさせるには、detect:ifNone: を使う。
・collect:<コレクション> collect: [:each | <加工処理>]:コレクションの各要素に対して加工処理を行う。
・inject:into:<コレクション> inject:<初期オブジェクト> [:obj :each | <処理>]:ブロック内引数 obj にまず初期オブジェクトが入り、eachにコレクションの最初のオブジェクトが入り、処理を行う。次に、処理の結果が obj に入り、コレクションの次の要素がeachに入り、処理を行う…の繰り返しで、コレクションの要素が無くなるまで処理が続く。
・keysDo:<ディクショナリ> keysDo: [:each | <処理>]:Dictionary系コレクションで「キー」を取り出して処理する。
・valuesDo:<ディクショナリ> valuesDo: [:each | <処理>]:ディクショナリから、「値」を取り出して処理する。
・keysAndValuesDo:<ディクショナリ> keysAndValuesDo: [:key :value | <処理>]:ディクショナリから「キー」と「値」をそれぞれ取り出して処理する。
・associationsDo:<ディクショナリ> associationsDo: [:associations | <処理>]:Associationは「キー」と「値」のペアで、「キー -&gt; 値」と【-&gt; 】メッセージで作成できる。 作成されたインスタンスから「キー」を取り出すには「key」メソッドを、「値」を取り出すには「value」メソッドを送る。


16:47:45 | 3k1 | comments(0) | TrackBacks