vieweditattachhistoryswikistopchangessearchhelp

「結城浩の『Perl クイズ』」を Smalltalk/Squeak で

Perl の勉強を兼ねて、結城さんの「結城浩の『Perl クイズ』」(バックナンバー)を Smalltalk/Squeak 向けにちょっとアレンジしてみることを実験的に試みてみました。Perl と Smalltalk では、問題領域がかけ離れるにもほどがあるほどかけ離れすぎているような気もするのですが、Smalltalk 言語は“Smalltalk システム”と呼ぶべきある種の GUI ベースの OS 専用のネイティブ言語として、Perl が UNIX という ファイルシステムをよりどころとした OS において果たすべきシェルスクリプトのような役割も担わなければならないわけなので、そこには何らかの共通項を見いだせるはず、とも考えました。純粋に Smalltalk 的にはおかしなのや Perl での出題の趣旨を完全に違えてしまっているものも散見されますが、各方面におかれましてはご容赦あれかし。もちろん質問、あるいは、ぼけ・つっこみ は歓迎です。各項目の「書き込み」ボタンから遠慮なくどうぞ。

ここに示したスクリプトのほとんどは、Squeak システム内で文字を入力できる場所でならどこでも評価(入力テキストを選択して print it(alt/cmd-p))可能です。よかったら実際に試してみてください。本当にどこでもいいのですが、狭い場所でちまちま入力・評価を繰り返すのもけったいな話なので、いったん (FileStream fileNamed: 'test.txt') edit を do it(alt/cmd-d)して表示されるウインドウを作業場に使うと保存も容易でなにかと便利です(ワークスペースでもいいのですが、最近のバージョンで追加されたワークスペース変数という やっかいな機能のおかげで、評価時に働いてほしいテンポラリ変数のスペル修正機構が無力化されてしまっているので、個人的にはあまりお薦めしていません)。

なお、Squeak システムにスクリプトをコピペなどでもってゆくと、下の図のように _ は ← で、^ は ↑ で表示されます。--sumim

Uploaded Image: text.png

企画全体へのコメントはこちらへ


このページの最後へ



No.0001

●問題
次の2つの式の違いを述べよ。
'Hello.\'

'Hello.\' withCRs


●解答
文字列に withCRs メッセージを送信することにより文字列中の \ を改行に置き換えた新しい文字列を得ることができる。

●コメント欄



No.0002

●問題
次のコードは一気に print-it(alt/cmd-p)できない。なぜか?
| name |
name _ 'Squeak'
^ '私の名前は name です。' copyReplaceAll: 'name' with: name


●解答
name _ 'Squeak' の最後にピリオド「.」が抜けている。Smalltalk のコードにおいて、改行や余分なスペース、タブは意味を持たない。式を2つ以上連ねる場合は、その区切りを示すためにピリオドを入れておく必要がある。最終行のピリオドはあってもなくてもよい。

●コメント欄



No.0003

●問題
次のコードは選択して print it(alt/cmd-p)時になにをするものか。
| line |
line _ FillInTheBlank request: ''.
line _ line withoutTrailingBlanks.
^ line, ' ですね。\' withCRs


●解答
fill in the blank と呼ばれる入力欄を表示して入力をうながし、そこに入力された文字列の最後にスペースや改行があればそれを削除。最後に文字列を追加した新しい文字列を返す。Perl の場合と違って、fill in the blank 経由の入力ではユーザーが意識して入れなければ改行は付加されない(また、最後にあらためて改行を入れる必要もない)ので、
^ (FillInTheBlank request: ''), 'ですね。'
でもほぼ同じ。

●コメント欄



No.0004

●問題
使用している Squeak システムのバージョンを調べる式。

●解答
Smalltalk version
を選択して print it(alt/cmd-p)。もうすこし詳しく知りたければ、
Smalltalk aboutThisSystem
後者はデスクトップメニューの help... サブメニューにある about this system... を選択したのと同じ。

●コメント欄



No.0005

●問題
次のコードを動作を変えないように書き換えよ。
isTerse not ifTrue: [self inform: 'Program started.']


●解答
isTerse ifFalse: [self inform: 'Program started.']

self inform: (isTerse not ifTrue: ['Program started.'] ifFalse: [''])

self inform: (isTerse ifTrue: [''] ifFalse: ['Program started.'])
#ifTrue:ifFalse: の代わりに #ifFalse:ifTrue: も使える。

●コメント欄



No.0006

●問題

●解答

●コメント欄



No.0007

●問題
Squeak でよく出会うピンクのウインドウに
MessageNotUnderstood: UndefinedObject>>message
と書かれたエラー。最後の「message」はエラーを起こしたメッセージセレクタだとして、UndefinedObject は?

●解答
この手のエラーは、ある時点で変数へのしかるべきオブジェクトの代入(束縛)に失敗したとき、その変数を含んだ下流のメッセージ式を評価するときによく生じる。宣言直後の変数にはあらかじめ nil が束縛されているため、想定されるオブジェクトが受けるはずのメッセージを nil が受けることになりエラーとなる。細かいことを言うと、nil にしてみれば「自分はこのメッセージを知らない…」としごくまっとうな反応を示しているだけなので“エラー”ではない。一般には“ノーティファイア”と呼ばれる。メッセージが理解できない旨をつたえるこうしたノーティファイアは「オブジェクトが属するクラス >> 当該メッセージ名(メッセージセレクタ)」で示される。UndefinedObject は nil の属するクラス。また nil は UndefinedObject の唯一のインスタンス。
1 class                  => SmallInteger
16r3FFFFFFF class        => SmallInteger
(16r3FFFFFFF + 1) class  => LargePositiveInteger
1.0 class                => Float
'abc' class              => String
#abc class               => Symbol
$a class                 => Character
#(1 2 3) class           => Array
{1. 2. 3} class          => Array
[1. 2. 3] class          => BlockContext
nil class                => UndefinedObject
true class               => True
false class              => False
(Integer >> #factorial) class => CompiledMethod


●コメント欄



No.0008

●問題
次のおのおののメッセージ式の意味を述べよ。
'string' first: 1
'string' allButFirst: 1
'string' last: 1
'string' allButLast: 1
'string' copyFrom: 2 to: 3


●解答
順に「最初の一文字」「最初の一文字を除いてすべて」「最後の一文字」「最後の一文字を除いてすべて」「2文字目から3文字目」を抽出した文字列を返す。allButFirst: 1、allButLast: 1 はそれぞれ allButFirst、allButLast と同じ(No.0017 の別解に登場。ただし、レシーバは a String ではなく、an Array)。

●コメント欄



No.0009

●問題

●解答

●コメント欄



No.0010

●問題
「It's a pencil.」という文字列を表現する式。

●解答
'It''s a pencil.'
文字列を表現する '...' リテラル式内では「''」のように ' を2連続することで $' を表現できる。

●コメント欄



No.0011

●問題
「-」を 40 文字連続して持つ文字列を作るメッセージ式。

●解答
String new: 40 withAll: $-


●コメント欄



No.0012

●問題
変数 string に束縛した文字列を 10 回繰り返した文字列を作り、それを改めて string に束縛するコードを書け。

●解答
| string |
string _ 'Squeak'.
string _ String streamContents: [: stream | 10 timesRepeat: [stream nextPutAll: string]]
#new:withAll: の第二パラーメータに指定できるのはレシーバ(この場合 Sring クラス)のインスタンスの要素。a String の要素は a Character なので、$- のような場合はよいが、'Squeak' のような場合は #new:withAll: は使えない。

●コメント欄

なぜNo.0003でも使われている、コレクションを結合するための二項メッセージセレクタ「,」を使ってはいけないのですかと書いてみるテスト。--abee

#, はその結合作業の過程でレシーバのコピーを作るので、レシーバが大きなもの、あるいは大きくなる傾向にある場合や、(規模にもよりますが)この問題のように繰り返し結合作業を行なう必要がある用途には向きません、とつきなみに答えてみるテスト。--sumim



No.0013

●問題
次のコードの誤りを示せ。
| hourInteger |
hourInteger _ (FillInTheBlank request: 'hour integer:') asInteger.
hourInteger < 0 | 24 <= hourInteger
	ifTrue: [self error: 'Range error']
	ifFalse: [hourInteger < 12
		ifTrue: ['ante meridiem, aka am']
		ifFalse: [hourInteger = 12
			ifTrue: ['at noon']
			ifFalse: ['post meridiem, aka pm']]]


●解答
| hourInteger |
hourInteger _ (FillInTheBlank request: 'hour integer:') asInteger.
hourInteger < 0 | (24 <= hourInteger)
	ifTrue: [self error: 'Range error']
	ifFalse: [hourInteger < 12
		ifTrue: ['ante meridiem, aka am']
		ifFalse: [hourInteger = 12
			ifTrue: ['at noon']
			ifFalse: ['post meridiem, aka pm']]]
あるいは、
(hourInteger < 0) | (24 <= hourInteger)
Smalltalk では、二項演算すらもメッセージ送信としてユーザーは意識して書く必要があり、処理系もそのように解釈する。たとえば、3 + 4 は 3 への + 4 というメッセージ送信である。言ってみればこれはある種の“ドグマ”なので、無条件で受け入れないと Smalltalk ではあまりシアワセになれない…(^_^;)。このとき、二項メッセージ同士には優先順位がないため、たとえば、a < b | c <= d では、単純に前から「a に < b を」「その結果に | c を」「その結果に <= d を」の順、つまり ((a < b) | c) <= d というようにメッセージが送信される。これはもちろん記述者が望むものとは違うので、括弧でくくることで処理系に対して明示的にメッセージ送信順変更を指示する必要がある。ここでは、a < b の返値に | (c <= d) というメッセージを送りたいので、解答で示したように書かないとこのコードは正しく動かない。

ちなみに、Smalltalk では if-then-else 制御構造も文ではなく、#ifTrue:ifFalse: というセレクタを用いたメッセージ送信式として記述する。このとき、then 節、else 節に書くべきコードは、[ ] でくくって“ブロック”と呼ばれる無名関数オブジェクト(a BlockContext。BlcokContext のインスタンスの意)として記述し、#ifTrue:ifFalse: を使ったメッセージ送信に添えられた単なるパラメータとして扱われる(ifTrue: trueBlock ifFalse: falseBlock というメッセージを送っている)。

ただ実際のところ、#ifTrue:ifFalse: がメッセージ送信式なのは見た目だけで、内部的には(つまりコンパイル後のバイトコードレベルでは)効率を考慮して goto 文による条件付き分岐文に書き換えられていたりする。
3 < 4 ifTrue: [5] ifFalase: [6] のバイトコードプログラム
(行番号 <バイトコード> ニーモニックのようなもの、の順)

34 <24> pushConstant: 3
35 <25> pushConstant: 4
36 <B2> send: <
37 <99> jumpFalse: 40
38 <23> pushConstant: 5
39 <90> jumpTo: 41
40 <22> pushConstant: 6
3 < 4 はちゃんとメッセージ送信になっている(スタックマシンなのでソースコードでの解釈とちょっと違って、< 4 ではなく #< の送信のように読めてしまうが…)のに対して、ifTrue: [5] ifFalse: [6] はそうはなっていない。したがって、True >> #ifTrue:ifFalse: や False >> #ifTrue:ifFlase: というメソッドはたしかに存在し、他のメソッド同様に普通にそのソースへのアクセスもできるが、それを変更して定義を変えても何も起こらない。念のため、3 < 4 myIfTrue: [5] myIfFalse: [6] をコンパイルした際のバイトコードは、
38 <23> pushConstant: 3
39 <24> pushConstant: 4
40 <B2> send: <
41 <89> pushThisContext: 
42 <75> pushConstant: 0
43 <C8> send: blockCopy:
44 <A4 02> jumpTo: 48
46 <25> pushConstant: 5
47 <7D> blockReturn
48 <89> pushThisContext: 
49 <75> pushConstant: 0
50 <C8> send: blockCopy:
51 <A4 02> jumpTo: 55
53 <26> pushConstant: 6
54 <7D> blockReturn
55 <F2> send: myIfTrue:myIfFalse:
となり、こんどはちゃんと myIfTrue: [5] myIfFlase: [6] というメッセージの送信のかたちでコンパイルされていることがわかる。もちろんこのままでは実行時に、このメッセージを受信する 3 < 4 の返値(具体的には true )にメッセージ理解不能とはじかれてしまう。別途、True >> myIfTrue:myIfFlase: を True >> ifTrue:ifFalse: と同じコードで定義しておけば 5 を返すコードとして正常に機能する。

●コメント欄

二項メッセージセレクタ「|」とキーワードメッセージセレクタ「or:」ってどう違って、どう使い分ければよいのでしょう。--abee

両者ともに同じ用途(論理和)に用いることができますが、パラメータに a Boolean(正確には、Boolean のサブクラスである True もしくは Falase のインスタンスである true もしくは false )を期待するか、a Boolean を返す無名関数(ブロック。a BlockContext)を期待するかで異なります。見た目の親しみやすさでは前者ですが、効率を考慮すると後者を用いるほうがよいでしょう。--sumim



No.0014

●問題
現在の日時を返すメッセージ式。

●解答
Time dateAndTimeNow
Date dateAndTimeNow
いずれの返値も同じで、要素数が二つの配列(an Array)。これらの要素(今日の日付と現在の時刻)は個別に、
Time now
Date today
でそれぞれ a Time、a Date(Time、Date のインスタンス。不定冠詞+クラス名で、そのクラスのインスタンスを意味する)として得ることもできる。

a Time、a Date に対して、それぞれ、seconds、minutes、hours、あるいは、dayOfMonth、monthIndex、year、weekday などといったメッセージを送信することで、対応する各要素を数値で(weekday は文字列で)個別に得ることができる。
| now seconds minutes hours today day month year weekday |
now _ Time now.
seconds _ ('0', now seconds asString) last: 2.
minutes _ ('0', now minutes asString) last: 2.
hours _ ('0', now hours asString) last: 2.
today _ Date today.
day _ ('0', today dayOfMonth asString) last: 2.
month _ ('0', today monthIndex asString) last: 2.
year _ today year asString.
weekday _ today weekday first: 3.
^ year, '-', month, '-', day, ' (', weekday, ') ', hours, ':', minutes, ':', seconds
おな、同じようなことをするメソッドがすでに用意されているので、それらを使って、
| today |
today _ Date today.
^ today yyyymmdd, ' (', (today weekday first: 3), ') ', Time now print24
としてもよい。

a Date の要素を取り出すメソッドは、Date とタイプして選択した後 browse it (alt/cmd-b) してシステムブラウザを呼び出し、上段右から二番目のペイン(枠)の accessing をクリックして選択すると、そのすぐ右隣のペインで一覧できる。メソッド名をクリックすると下のペインにソースコードが現われる。

Uploaded Image: accessingdate.jpg


●コメント欄



No.0015

●問題
次のスクリプトの動作を説明せよ。
| collection element |
collection _ OrderedCollection new.
element _ 0.
3 timesRepeat: [collection add: (element _ element + 1)].
^ collection


●解答
collection、element という名のテンポラリ変数を宣言し、collection に an OrderedCollection 、element に 0 を束縛。element + 1 の返値をあらためて element に束縛すると同時に、collection に束縛した an OrderedCollection の要素に追加。これを 3 回繰り返す。最後に collection に束縛した an OrderedCollection を返す(「^」はリターン)。

Smalltalk では、変数へのオブジェクトの束縛の記述は右辺の値を返す。「_」(実際の表示は「←」)の代わりに「:=」(表示も同じ)も使用できる。また「^」は Squeak システムの画面では「↑」と表示される(ただし、システムフォントが NewYork 10 の場合)。

Squeak システム(もしくは '70 年代の暫定ダイナブック環境)では、ユーザーは原則としてグリフを比較的自由に編集できる。ただし、最近の Squeak システムでは、グリフ編集時に使用するウィジェットの関係で GUI フレームワークを普段使用しているもの(Morphic ベース)から旧式のもの(MVC ベース)にいったん切り替えないといけない。デスクトップメニューの open ... サブメニューから mvc project を選択し、enter 。画面が切り替わったら、そこでワークスペースなどを開いて
(TextStyle default fontAt: 1) fontArray first edit: $_

m17n 化していない場合は、

(TextStyle default fontAt: 1) edit: $_
を do it (alt/cmd-d)。すると、ビットエディタが開くので、そこでグリフを編集。終わったら、黄ボタンメニューから accept を選んでビットエディタを閉じれば、次の画面の書き換えから(たとえば何か文字を入力すれば先の式の最後の文字が)編集後のグリフに置き換わる。_ の ← や、^ の ↑ が うっとうしければ、自分で適正と思うグリフに直してしまえばよい。逆に、NewYork10 以外でも ↑、← を使いたい場合も同じ。

Uploaded Image: biteditor.png


●コメント欄



No.0016

●問題
次のスクリプトはなにをするか。
| name defaultDirectory |
name _ FillInTheBlank request: 'file or directory name:'.
defaultDirectory _ FileDirectory default.
self inform: (
	((defaultDirectory fileExists: name) or: [defaultDirectory directoryExists: name])
		ifTrue: ['OK. ', name, ' exists.']
		ifFalse: ['Oops! ', name, ' does not exist.'])


●解答
ユーザーに入力をうながし、入力文字列と一致するファイルもしくはディレクトリが現在使用中の仮想イメージと同じディレクトリにあれば 'exists'、そうでなければ、'does not exist' とポップアップを出す。

●コメント欄



No.0017

●解答
| inStream isFirstLine line lastCount lastNumber outStream |
inStream _ FileStream oldFileNamed: 'count.txt'.
outStream _ FileStream fileNamed: 'table.txt'.
lastCount _ lastNumber _ 0.
isFirstLine _ true.
[(line _ inStream nextLine) notNil] whileTrue: [
	| pair number count |
	pair _ Scanner new scanTokens: line.
	number _ pair first.
	count _ pair second.
	isFirstLine not ifTrue: [
		| up |
		up _ lastCount - count.
		outStream
			print: number;
			nextPutAll: ' -> ';
			print: lastNumber;
			nextPutAll: ': ';
			print: up;
			cr].
	lastNumber _ number.
	lastCount _ count.
	isFirstLine _ false].
inStream close.
outStream edit


別解:
| dataFile counts outStream sortedNumbers |
dataFile _ FileStream oldFileNamed: 'count.txt'.
counts _ Dictionary new.
(Scanner new scanTokens: dataFile contents) pairsDo: [: number : count |
	counts at: number put: count].
dataFile close.
sortedNumbers _ counts keys asSortedArray reverse.
outStream _ FileStream fileNamed: 'table.txt'.
sortedNumbers allButFirst with: sortedNumbers allButLast do: [: prevNum : currNum |
	outStream
		print: prevNum;
		nextPutAll: ' -> ';
		print: currNum;
		nextPutAll: ': ';
		print: (counts at: currNum) - (counts at: prevNum);
		cr].
outStream edit


●コメント欄


No.0018

●解答
| formerX formerY x y |
formerX _ x _ 'one'.
formerY _ y _ 'two'.
x _ {x. y}.
y _ x first.
x _ x second.
^ {{formerX. formerY}. {x. y}}


別解1:
| formerX formerY x y |
formerX _ x _ SmallInteger maxVal atRandom.
formerY _ y _ SmallInteger maxVal atRandom.
x _ x bitXor: y.
y _ y bitXor: x.
x _ x bitXor: y.
^ {{formerX. formerY}. {x. y}}


別解2:
| formerX formerY x y |
formerX _ x _ SmallInteger maxVal atRandom.
formerY _ y _ SmallInteger maxVal atRandom.
x _ (x bitXor: y) bitXor: (y _ y bitXor: (x bitXor: y)).
^ {{formerX. formerY}. {x. y}}
x _ x bitXor: (y _ y bitXor: (x _ x bitXor: y))) とは書けない。

別解3:
| formerX formerY x y |
formerX _ x _ 'one'.
formerY _ y _ 'two'.
x _ y flag: (y _ x).
^ {{formerX. formerY}. {x. y}}
別解2で分かったことを利用して。すべてのオブジェクトで noop でかつパラメータを一つ以上とるメソッドなら同様のことができる。なぜこういうことができるかは、コンパイルされたバイトコードを見ると分かる。
| x y |
[x _ y flag: (y _ x)] method symbolic

" x _ y flag: (y _ x) 部分のバイトコードプログラム "

22 <11> pushTemp: 1         " y の値をプッシュ"
23 <10> pushTemp: 0         " x の値をプッシュ"
24 <81 41> storeIntoTemp: 1 " 行番号 23 でプッシュした値を y へ代入"
26 <E2> send: flag:         " 新たな y (i.e, x)の値を伴って #flag: を行番号 22 でプッシュした値に送信"
27 <81 40> storeIntoTemp: 0 " 返値(self i.e. 行番号 22 でプッシュした値) を x へ代入"
つまり、メッセージ「flag: (y _ x) 」のレシーバである y に束縛されているオブジェクトは、flag: (y _ x) のパラメータ評価の際の y への代入の前にすでにスタックにプッシュされているので、代入の結果、y がそれまで x に束縛されていたオブジェクトを束縛するようになっても、影響を受けない…かららしい。

●コメント欄


No.0019

●解答
^ #(	'<pre>北海道</pre>'
	'<pre>青森\n秋田</pre>'
	'<pre>岩手\n山形</pre>それから<pre>宮城\n福島</pre>'  ) collect: [: html |

	| tokens |
	tokens _ OrderedCollection new.
	(html copyReplaceAll: '<pre>' with: String cr) linesDo: [: line |
		| key |
		key _ line findString: '</pre>' startingAt: 1.
		key isZero not ifTrue: [tokens add: (line first: key - 1)]].
	String streamContents: [: stream |
		tokens do: [: token | stream nextPutAll: (token copyReplaceAll: '\n' with: ',')]
			separatedBy: [stream nextPut: $,]]]

別解1:
^ #(	'<pre>北海道</pre>'
	'<pre>青森\n秋田</pre>'
	'<pre>岩手\n山形</pre>それから<pre>宮城\n福島</pre>'  ) collect: [: html |

	| tokens keyStart keyStop openDelims closeDelims |
	tokens _ OrderedCollection new.
	openDelims _ #('<pre>' '<PRE>').
	closeDelims _ #('</pre>' '</PRE>').
	keyStop _ 1.
	[keyStop <= html size] whileTrue: [
		keyStart _ html findAnySubStr: openDelims startingAt: keyStop.
		keyStart _ html skipAnySubStr: openDelims startingAt: keyStart.
		keyStop _ html findAnySubStr: closeDelims startingAt: keyStart.
		keyStart < keyStop ifTrue: [
			tokens add: (html copyFrom: keyStart to: (keyStop - 1))]].
	String streamContents: [: stream | 
		tokens do: [: token | stream nextPutAll: (token copyReplaceAll: '\n' with: ',')] 
			separatedBy: [stream nextPut: $,]]]

別解2:
^ #(	'<pre>北海道</pre>'
	'<pre>青森\n秋田</pre>'
	'<pre>岩手\n山形</pre>それから<pre>宮城\n福島</pre>'  ) collect: [: html |

	| preRegions |
	preRegions _ (HtmlParser parse: html readStream) body contents 
		select: [: each | each isKindOf: HtmlPreformattedRegion].
	String streamContents: [: stream | 
		preRegions 
			do: [: each | stream nextPutAll: (
				each textualContents copyReplaceAll: '\n' with: ',')] 
			separatedBy: [stream nextPut: $,]]]


●コメント欄



No.0020

●問題
次の式をワークスペースなどで print it (alt/cmd-p)、あるいは do it (alt/cmd-d) で評価したとき、シュード変数「self」は何を束縛しているか。言い換えると、メッセージ「error: 'No such file.'」はどんなオブジェクトに送信されるのか。
(FileStream oldFileOrNoneNamed: 'notfound.txt') ifNil: [self error: 'No such file.']


●解答
nil。

do it や print it したとき、そのスクリプトは、#DoIt という仮の名前のメソッドとして、そのとき self が束縛しているオブジェクトのクラスにインスタンスメソッドとして一時的に定義される。評価・実行(つまり、self に束縛されているオブジェクト、すなわちこの場合 nil への送信)が終わると、自動的にクラス(この場合 UndefinedObject )から取り除かれる。

メソッド #error: は、クラス Object に定義されているため、すべてのオブジェクトがメッセージ「error: aString」のレシーバとして期待された挙動を示すことができる。したがって、nil error: 'notfound.txt' でも同じ。このコンテキストで self、つまり、メッセージ「DoIt」および「error: 'No such file.'」のレシーバが nil であることは、#error: の出すノーティファイアの表示(コンテキスト一覧の一段目と二段目)からも確認できる。

Uploaded Image: nosuchfile.png


self は変数だが、ユーザーがあらためて別のオブジェクトを束縛することができないため“シュード変数(疑似変数、偽変数)”と呼ばれる。シュード変数には他に、nil、true、false、super、thisContext がある。メソッド定義の際に、パラーメータを束縛するために使うパラメータ変数も同じシュード変数。nil、true、false はそれぞれ常に変わらず、UndefinedObject、True、False の唯一のインスタンスを束縛している。self、super が束縛するオブジェクトはコンテキストによって変わるが、いずれも同じ、そのコンテキストを生じさせるきっかけとなったメッセージ送信のレシーバを束縛している(ちなみに、両者ではメッセージ受信時に行なわれるメソッド検索の起点のみが異なる)。thisContext はその時点のコンテキスト自身を束縛する。なおコンテキストは、メソッドの動作中の状態をオブジェクト化したもので、バイトコードがどこまで実行されたかや、テンポラリ変数(必要ならパラーメータ変数)へのオブジェクトの束縛状況などを状態として保持している。

●コメント欄


このページを編集 (24885 bytes)


Congratulations! 以下の 1 ページから参照されています。

This page has been visited 7603 times.