コレクション
Collectionのサブクラスを紹介。(Array、OrderedCollection、List、Set、Bagなど)
Stringとそのサブクラス(SymbolやByteString)、Dictionaryは別管理しているのでそちらを参照。
Collectionには順番が保障されているタイプ(以下、配列型と表記)とされていないタイプ(以下、集合型と表記)がある。
SequenceableCollectionのサブクラスか否かで決まる。
Collectionのサブクラスのメジャーなクラスの特徴
- Array: 配列型。リテラルで定義することができる。項目の追加・削除を禁止している。
- OrderedCollection: 配列型。項目追加・削除ができ、いはゆる配列というイメージに最も近い振る舞いをする。
- List: 配列型。項目追加・削除ができる。いまいちOrderedCollectionとの使い分けのポイントがわからないが、位置を指定した追加や削除に向いているはず。
- SortedCollection: 配列型。ソートされた状態が保障されている配列。ソートブロックを内包している。
- Set: 集合型。重複のない集合。
- Bag: 集合型。重複を許し、さらに重複項目の重複数まで保持している。
- SelectionInList: 画面を表示するときのリストボックスに使われる。Listと選択中インデックスを保持している。
- SelectionInListSortAware: 画面を表示するときのデータセットに使われる。List、選択中インデックス、ソート項目とその方向を保持している。
7.0~のマイナーバージョンアップの間に増えたメソッドも結構あるみたいだ。
Array new: 10
まずはここから。適当なクラス new: n とすればだいたいどのクラスでも作成できる。
ただし、メモリ空間を確保しただけで実際に at: n でアクセスするとエラーになるクラスのほうが多い。
終端を表すインスタンス変数(lastIndexとかlimitとかtallyとか)が0のままだからだ。
そういうクラスで終端を後付で変更することなしにアクセスしたい場合は
OrderedCollection withSize: 10
とする。
Array new: 10 withAll: 0
ArrayかStringで理解できる。他のクラスで作りたい場合は(Array new: n withAll: x) asXxxxとする。
(Array new: 5) atAllPut: 0
としてもよい。
#($a $b 1 2 3 'Hello' #World)
いくつでも設定できる。
Array(withメソッド)の場合
Array with: Date today with: Time now
withは4つまで。そこまでしかメソッドが用意されていない。
もっと作りたい場合は(Array with:with:with:with:),(Array with:)のようにカンマでつなぐ。
OrderedCollectionの場合
(foo := OrderedCollection new) add: 1; add: 2; add: 3; yourself
これ1行でfooの中に3つの項目が入る。
Listの場合
(foo := List new) at:1 put: $a; at:2 put: $b; at:3 put: $c; yourself
これ1行でfooの中に3つの項目が入る。
storeOn:メソッドを読むと、どのように書けばオブジェクトが作れるかわかる。
#($a $b $c), #(1 2 3)
#($a $b $c) size
要素数を調べる。コレクションが空か調べるために aCollection size = 0 ifTrue:[...]とかやるのを見かけるが、
そういう場合は下記のisEmpty等が用意されているのでそれらを使うべきだろう。
Array new isEmpty
コレクションが空の場合trueを返す。
#($a $b $c) isNotEmpty
#($a $b $c) notEmpty
コレクションが空でない場合trueを返す。
isEmptyの逆がどっちかわからなくなってもいいように2種類用意されていると思われる。
#($a $b $c) beginsWith: 'ab'
ArrayとStringで比較しているが、at:メソッドを理解できるクラスなら比較可能。この場合trueになる。
#($a $b $c) endsWith: 'bc'
beginsWith:の逆からメソッド。
ArrayとStringで比較しているが、at:メソッドを理解できるクラスなら比較可能。この場合trueになる。
#('Zun' 'Zun' 'ZUN' 'zun' 'Doko') includes: 'Doko'
要素が含まれていればtrueを返す。
位置まで知りたければindexOfかlastIndexOf:を用いる。ブロックで条件指定したければcontains:を用いる。
#('Zun' 'Zun' 'ZUN' 'zun' 'Doko') contains: [:i | '*zun*' match: i]
条件に合致する要素が含まれていればtrueを返す。
条件に合致する要素を取得したい場合はdetect:を用い、位置を取得したい場合はfindFirst:またはfindLastを用いる。
#(1 3 5 7 9) allSatisfy: [:i | i odd]
全ての要素が条件に合致すればtrueを返す。
#(2 4 6 8 0 1) anySatisfy: [:i | i odd]
要素のどれか一つでも条件に合致すればtrueを返す。
#($a $b $c $b) indexOf: $b
要素が見つからなければエラーになるので、回避策としてindexOf:ifNone:もあり。
ブロックで条件を指定したければfindFirst:を用いる。
#($a $b $c $b) lastIndexOf: $b
indexOfの逆からメソッド。要素が見つからなければエラーになるので、回避策としてlastIndexOf:ifNone:もあり。
ブロックで条件を指定したければfindLast:を用いる。
#($a $B $c $D) findFirst: [:i | i isUppercase]
どの要素も条件に合致しない場合は0を返す。
#($a $B $c $D) findLast: [:i | i isUppercase]
どの要素も条件に合致しない場合は0を返す。
#(1 2 3 4 5 6 7 8 9 0) indexOfSubCollection: (3 to: 6) startingAt: 2
コレクション(配列型)同士の比較を行う。インターバル(3 to: 6)も配列型コレクションのサブクラスなので比較可能。
文字列にも使えるが、文字列の場合はfindString:startingAt:が用意されている。
#('Zun' 'Zun' 'ZUN' 'zun' 'Doko') detect: [:i | '*zun*' match: i]
4つの「ect」メソッドの1つ。
条件に合致する要素が含まれていない場合はエラーになるので、回避策としてdetect:ifNoneもあり。
条件に合致する要素があるか真偽を問う場合はcontains:を用い、位置を取得したい場合はfindFirst:またはfindLastを用いる。
(XXXXX detect: [YYYY] ifNone: [nil]) isNil ifTrue: [ZZZ] なんていうのを見かける。contains:の存在に気づいて欲しい。
'Good morning' beMutable replaceAll: $o with: $a
破壊的メソッドで、リテラル文字列でもbeMutableすれば破壊的修正が可能。文字列で使うことがほとんどだと思う。
開始終了位置を指定できるcopyReplaceFrom:to:with:というメソッドもある。
'Good morning' beMutable replaceFrom: 1 to: 4 with: 'Bad '
破壊的メソッドで、リテラル文字列でもbeMutableすれば破壊的修正が可能。
from:to:の範囲とwith:の文字数が合わないとエラーになる。文字列で使うことがほとんどだと思う。
#($a $b $c $d) copyReplaceAll: 'cd' with: #(1 3)
開始終了位置を指定できるcopyReplaceFrom:to:with:というメソッドもある。配列型コレクション同士なら置換可能。
#(1 2 3) collect: [:i | i * 2]
よく#(1 2 3) do: [:i | i * 2]とやってもコレクションが変わらないと嘆くのを見かけるが、
do:でアクセスする要素はリードオンリーなので、これで対応する。
#($a $b $c) first
#($a $b $c) last
#($a $b $c) at: 2
#($a $b $c) first: 2
#($a $b $c) last: 2
#($a $b $c) asList after: $b
指定した要素が見つからなければエラー、指定した要素が最後の場合もエラー。
findIndexOf:を利用して+1しているだけ。
#($a $b $c) asList before: $b
指定した要素が見つからなければエラー、指定した要素が最初の場合もエラー。
findIndexOf:を利用して-1しているだけ。
#($a $b $c) allButFirst: 1
#($a $b $c) allButLast: 1
#($a $b $c) copyFrom: 1 to: 2
汎用性の高さには定評があるように思う。
#($a $b $c) copyUpTo: $b
長いコレクションになると信頼性は低いだろうなぁ。
#(1 nil 3 nil nil 6 7 nil nil 0) copyWithout: nil
nilを指定すればrubyでいうcompactと同じ動きになる。
#(1 2 3 4 5 6 7 8 9 0) select: [:i | i odd]
4つの「ect」メソッドの1つ。逆に除く場合はreject:を用いる。
foo := #($a $b $a $c $a $d) asList.
foo remove: $a
破壊的メソッドで、実行後、コレクションをもう一度見てみると指定した値が除かれている。
また、remove:の返り値は指定した値になる。
指定した要素が見つからない場合はエラーになるので、回避策としてremove:ifAbsentもあり。
foo := #($a $b $c) asList.
foo removeFirst
破壊的メソッドで、実行後、コレクションをもう一度見てみると最初の要素が除かれている。
また、removeFirstの返り値は指定した最初の要素の値になる。いはゆるキューを実現できる。
コレクションが空の場合はエラー。
foo := #($a $b $c) asList.
foo removeLast
破壊的メソッドで、実行後、コレクションをもう一度見てみると最後の要素が除かれている。
また、removeLastの返り値は指定した最後の要素の値になる。いはゆるスタックを実現できる。
コレクションが空の場合はエラー。
foo := #($a $b $c) asList.
foo removeAtIndex: 2.
破壊的メソッドで、実行後、コレクションをもう一度見てみると指定した位置の要素が除かれている。
また、removeAtIndex:の返り値は指定した位置の要素の値になる。
コレクションのサイズより大きい位置を指定した場合はエラー。
foo := #($a $b $c) asList.
foo removeAll
破壊的メソッドで、実行後、コレクションをもう一度見てみると空になっている。
また、removeAllの返り値はコレクションの値そのものになる。
実際の動きは1項目ずつremove:するので遅い。
foo := #($a $b $c) asList.
foo removeAll: #($a $b)
破壊的メソッドで、実行後、コレクションをもう一度見てみるとコレクションと共通する要素が除かれている。
また、removeAllの返り値はコレクションの共通要素になる。1つでも見つからないとエラーになる。
実際の動きはレシーバと引数の直積になるので遅い。
foo := #($a $b $c) asList.
foo removeFrom: 1 to: 2
破壊的メソッドで、実行後、コレクションをもう一度見てみると指定した範囲の要素が除かれている。
また、removeFrom:to:の返り値は指定した範囲のコレクションになる。
foo := #(1 2 3 4 5 6 7 8 9 0) asList.
foo removeAllSuchThat: [:i | i odd]
破壊的メソッドで、実行するとコレクション自身を変更しつつ、条件を満たすものを返り値として抽出することができる。
Visualworks界の最強のキラーメソッドと確信している。実力は中村主水並みだと思う。
#(1 2 3 4 5 6 7 8 9 0) reject: [:i | i odd]
4つの「ect」メソッドの1つ。逆に残す場合はselect:を用いる。
remove系のメソッドと異なり、破壊的ではない。
(OrderedCollection new) add: 1; add: 2; add: 3; yourself
任意の値のコレクションを作るところでも活躍。
(OrderedCollection new) addLast: 1; addLast: 2; addLast: 3; yourself
add:と使い方は同じ。
(OrderedCollection new) addFirst: 1; addFirst: 2; addFirst: 3; yourself
add:やaddFirst:とは逆に、先頭に追加する。
#(1 2 3) asList addAll: #(4 5 6);yourself
コレクションの最後にコレクションを追加する。
#(1 2 3) asList addAllLast: #(4 5 6);yourself
addAll:と使い方は同じ。
#(1 2 3) asList addAllFirst: #(4 5 6);yourself
コレクションの最初にコレクションを追加する。
#($a $b $c) asList add: $d after: $a ;yourself
コレクションを順番に見ていって挿入する位置を探すのでちょっと遅い。
#($a $b $c) asList add: $d before: $a ;yourself
コレクションを順番に見ていって挿入する位置を探すのでちょっと遅い。
#($a $b $c) asList add: $d beforeIndex: 2 ;yourself
add:after:とadd:before:は対になっているのに、なぜかこれはbeforeしかない。
#($a $b $c) copyWith: $d
Arrayは要素を追加することが許されていないが、コピーを作る時についでに追加という感じならOK。
foo := #(1 3 5 8 5 6 2 3 5 7 6 9 5 4) asBag.
foo add: 1 withOccurrences: 9.
foo
既に存在する要素であれば出現数が加算される。
#('Hello' 'World') do: [:i | Transcript cr; show: i]
もっとも基本的なループ。その汎用性ゆえに至る所で使われるが、今一度、do:を使った車輪の再開発をしていないか見直して欲しい。
ちなみにブロック内で使う引数は参照専用なので、do:を使って自身の値を:=で書き換えることはできない。
foo := Array new: 5.
foo keysDo: [:i | foo at: i put: i]
コレクションのインデックスで何かしたいときに使う。自身の配列にアクセスすることも可能。
foo := Array new: 5 withAll: 2.
foo keysAndValuesDo: [:key :val | foo at: key put: val * key]
このメソッドが真価を発揮するのはDictionaryの場合かな。
foo := ''.
#('Zun' 'Zun' 'ZUN' 'zun' 'Doko') do: [:i | foo := foo, '"', i, '"'] separatedBy: [foo := foo, ',']
CSVを作るときに真価を発揮する。逆にそれ以外の使いどころがわからない。
dic := Dictionary new.
brother := #('長男' '次男' '三男' '四男' '五男' '六男').
name := #('おそ松' '唐松' 'チョロ松' '市松' '十姉妹' '椴松').
brother with: name do: [:i :j | dic at: i asSymbol put: j].
2つのコレクションのサイズが異なるとエラーになるんダヨ~ン。
いい例が見つからなかったぜ、てやんでぃ、バーローちきしょう!
#(7 9 4 5 2 0 1 3) asSortedCollection
SortedCollectionが得られる。つまり、返り値はクラスが変わっている。
(Array with: (#a->9) with: (#b->6) with: (#c->4) with: (#d->2)) asSortedCollection: [:e1 :e2 | e1 value < e2 value]
ソート条件はブロック内に2つの引数が必要。
#(7 9 4 5 2 0 1 3) beMutable sort
#(7 9 4 5 2 0 1 3) beMutable sorted
破壊的メソッドで、実行後、コレクションをもう一度見てみると並べ替えられている。
sortとsortedの違いはいまいちわからない。
(Array with: (#a->9) with: (#b->6) with: (#c->4) with: (#d->2)) sort: [:e1 :e2 | e1 value < e2 value]
(Array with: (#a->9) with: (#b->6) with: (#c->4) with: (#d->2)) sorted: [:e1 :e2 | e1 value < e2 value]
(Array with: (#a->9) with: (#b->6) with: (#c->4) with: (#d->2)) sortWith: [:e1 :e2 | e1 value < e2 value]
破壊的メソッドで、実行後、コレクションをもう一度見てみると並べ替えられている。
sort:とsorted:の違いはいまいちわからない。
#($a $b $c) beMutable swap: 2 with: 3
(Array with: (#a -> 'A') with: (#b -> 'B')) fold: [:i :j | i value, ',', j value]
変数を用意しないでつなげることができるので便利。要素が空だとエラー。
CSVを作るときには要素間だけにカンマを入れてくれるので非常に強力。
また要素が1つだけだと思うような結果にならないこともある。
上記の例は要素を1つにするとvalueが評価されない。
#(1 3 5 7 9) inject: 0 into: [:i :j | i + j]
変数を用意しないでつなげることができるので便利。
上記の例は合計を計算する。
最大値、最小値であれば [:i :j | i min: 9] とか [:i :j | i max: j] とかやればよい。
文字列結合は [:i :j | i, j] でできる。
ただしfoldと違ってCSVを作ろうとするとカンマが余る。
'Morning,Afternoon,Evening' tokensBasedOn: $,
コレクション共通メソッドだが、CSVを区切るために用意したとしか思えない。
'Morning,Afternoon/Evening;Night' runsFailing: [:i | #($, $/ $;) includes: i]
コレクション共通メソッドだが、文字列以外での使いどころがわからない。
'Morning,Afternoon/Evening;Night' runsFailing: [:i | #($, $/ $;) includes: i] do: [:i | Transcript cr; show: i]
コレクション共通メソッドだが、文字列以外での使いどころがわからない。
'123,456,789.321' runsSatisfying: [:i | i isDigit]
runsFailing:の逆条件バージョン。マッチする要素が続く場合、まとめて一区切りとする。
'123,456,789.321' runsSatisfying: [:i | i isDigit] do: [:i | Transcript cr; show: i]
runsFailing:do:の逆条件バージョン。マッチする要素が続く場合、まとめて一区切りとする。
'getFieldKnownNotToBeAName:' piecesCutWhere: [:i :j | i isLowercase and: [j isUppercase]]
コレクション共通だが、やはり文字列向きといえる。これはキャメルケースを単語ごとにカットした場合。
'getFieldKnownNotToBeAName:' piecesCutWhere: [:i :j | i isLowercase and: [j isUppercase]] do: [:i | Transcript cr; show: i]
コレクション共通だが、やはり文字列向きといえる。これはキャメルケースを単語ごとにカットした場合。
do:付きのメソッドをいろいろ用意してあるが、その効果はいかほどのものか疑問に思う。
確かに1度コレクションのコピーを作ってからdo:するよりは効率がいいのだが、使用する頻度を考えるとな。。。
#(1 3 5 8 5 6 2 3 5 7 6 9 5 4) asSet
Setクラスにするだけで重複がなくなる。順番は保障されない。
#(1 3 5 8 5 6 2 3 5 7 6 9 5 4) asBag
Bagクラスにするだけで重複数も一緒に持つことができる。Bagも順番が保障されないタイプ。
重複数はoccurrencesOf:で取得することができる。
#(1 3 5 8 5 6 2 3 5 7 6 9 5 4) groupedBy: [:i | i odd]
tureとfalseをキーとするDictionaryが返る。
実はremoveAllSuchThat:で破壊的に分けることができ、しかも変数も少なくできるので、それを知っていると出番がなくなる。
#(1 3 5 8 5 6 2 3 5 7 6 9 5 4) occurrencesOf: 5
コレクションを1回ループさせてカウントするので、何度も実行するならasBagしてからの方が効率がよい。
#($a $b $c $d $e) reverse
逆にした状態を後々まで保持しておいていろいろ利用するなら使う価値があるかもしれない。
逆からループするなら下記の同時にdo:するタイプを使ったほうが効率がいい。
#($a $b $c $d $e) reverse di: [:i | Transcript cr; show: i]
メソッドはself size to: 1 by: -1 do: []となっている。つまり、reverse:して、コレクションのコピーを
返してから、さらにdo:するよりはずっと効率がいい。
最終更新:2013年02月21日 15:14