'Hello.\' 'Hello.\' withCRs
| name | name _ 'Squeak' ^ '私の名前は name です。' copyReplaceAll: 'name' with: name
| line | line _ FillInTheBlank request: ''. line _ line withoutTrailingBlanks. ^ line, ' ですね。\' withCRs
でもほぼ同じ。^ (FillInTheBlank request: ''), 'ですね。'
を選択して print it(alt/cmd-p)。もうすこし詳しく知りたければ、Smalltalk version
後者はデスクトップメニューの help... サブメニューにある about this system... を選択したのと同じ。Smalltalk aboutThisSystem
isTerse not ifTrue: [self inform: 'Program started.']
#ifTrue:ifFalse: の代わりに #ifFalse:ifTrue: も使える。isTerse ifFalse: [self inform: 'Program started.'] self inform: (isTerse not ifTrue: ['Program started.'] ifFalse: ['']) self inform: (isTerse ifTrue: [''] ifFalse: ['Program started.'])
と書かれたエラー。最後の「message」はエラーを起こしたメッセージセレクタだとして、UndefinedObject は?MessageNotUnderstood: UndefinedObject>>message
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
'string' first: 1 'string' allButFirst: 1 'string' last: 1 'string' allButLast: 1 'string' copyFrom: 2 to: 3
文字列を表現する '...' リテラル式内では「''」のように ' を2連続することで $' を表現できる。'It''s a pencil.'
String new: 40 withAll: $-
| string | string _ 'Squeak'. string _ String streamContents: [: stream | 10 timesRepeat: [stream nextPutAll: string]]#new:withAll: の第二パラーメータに指定できるのはレシーバ(この場合 Sring クラス)のインスタンスの要素。a String の要素は a Character なので、$- のような場合はよいが、'Squeak' のような場合は #new:withAll: は使えない。
| 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']]]
Smalltalk では、二項演算すらもメッセージ送信としてユーザーは意識して書く必要があり、処理系もそのように解釈する。たとえば、3 + 4 は 3 への + 4 というメッセージ送信である。言ってみればこれはある種の“ドグマ”なので、無条件で受け入れないと Smalltalk ではあまりシアワセになれない…(^_^;)。このとき、二項メッセージ同士には優先順位がないため、たとえば、a < b | c <= d では、単純に前から「a に < b を」「その結果に | c を」「その結果に <= d を」の順、つまり ((a < b) | c) <= d というようにメッセージが送信される。これはもちろん記述者が望むものとは違うので、括弧でくくることで処理系に対して明示的にメッセージ送信順変更を指示する必要がある。ここでは、a < b の返値に | (c <= d) というメッセージを送りたいので、解答で示したように書かないとこのコードは正しく動かない。(hourInteger < 0) | (24 <= hourInteger)
3 < 4 はちゃんとメッセージ送信になっている(スタックマシンなのでソースコードでの解釈とちょっと違って、< 4 ではなく #< の送信のように読めてしまうが…)のに対して、ifTrue: [5] ifFalse: [6] はそうはなっていない。したがって、True >> #ifTrue:ifFalse: や False >> #ifTrue:ifFlase: というメソッドはたしかに存在し、他のメソッド同様に普通にそのソースへのアクセスもできるが、それを変更して定義を変えても何も起こらない。念のため、3 < 4 myIfTrue: [5] myIfFalse: [6] をコンパイルした際のバイトコードは、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
となり、こんどはちゃんと myIfTrue: [5] myIfFlase: [6] というメッセージの送信のかたちでコンパイルされていることがわかる。もちろんこのままでは実行時に、このメッセージを受信する 3 < 4 の返値(具体的には true )にメッセージ理解不能とはじかれてしまう。別途、True >> myIfTrue:myIfFlase: を True >> ifTrue:ifFalse: と同じコードで定義しておけば 5 を返すコードとして正常に機能する。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:
いずれの返値も同じで、要素数が二つの配列(an Array)。これらの要素(今日の日付と現在の時刻)は個別に、Time dateAndTimeNow Date dateAndTimeNow
でそれぞれ a Time、a Date(Time、Date のインスタンス。不定冠詞+クラス名で、そのクラスのインスタンスを意味する)として得ることもできる。Time now Date today
おな、同じようなことをするメソッドがすでに用意されているので、それらを使って、| 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
| collection element |
collection _ OrderedCollection new.
element _ 0.
3 timesRepeat: [collection add: (element _ element + 1)].
^ collection
を do it (alt/cmd-d)。すると、ビットエディタが開くので、そこでグリフを編集。終わったら、黄ボタンメニューから accept を選んでビットエディタを閉じれば、次の画面の書き換えから(たとえば何か文字を入力すれば先の式の最後の文字が)編集後のグリフに置き換わる。_ の ← や、^ の ↑ が うっとうしければ、自分で適正と思うグリフに直してしまえばよい。逆に、NewYork10 以外でも ↑、← を使いたい場合も同じ。(TextStyle default fontAt: 1) fontArray first edit: $_ m17n 化していない場合は、 (TextStyle default fontAt: 1) edit: $_
| 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.'])
| 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
| formerX formerY x y | formerX _ x _ 'one'. formerY _ y _ 'two'. x _ {x. y}. y _ x first. x _ x second. ^ {{formerX. formerY}. {x. y}}
| 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}}
x _ x bitXor: (y _ y bitXor: (x _ x bitXor: y))) とは書けない。| 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}}
別解2で分かったことを利用して。すべてのオブジェクトで noop でかつパラメータを一つ以上とるメソッドなら同様のことができる。なぜこういうことができるかは、コンパイルされたバイトコードを見ると分かる。| formerX formerY x y | formerX _ x _ 'one'. formerY _ y _ 'two'. x _ y flag: (y _ x). ^ {{formerX. formerY}. {x. y}}
つまり、メッセージ「flag: (y _ x) 」のレシーバである y に束縛されているオブジェクトは、flag: (y _ x) のパラメータ評価の際の y への代入の前にすでにスタックにプッシュされているので、代入の結果、y がそれまで x に束縛されていたオブジェクトを束縛するようになっても、影響を受けない…かららしい。| 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 へ代入"
^ #( '<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: $,]]]
^ #( '<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: $,]]]
^ #( '<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: $,]]]
(FileStream oldFileOrNoneNamed: 'notfound.txt') ifNil: [self error: 'No such file.']
このページを編集 (24885 bytes)
以下の 1 ページから参照されています。 |
This page has been visited 7601 times.