…ect:(Smalltalk ではコロンも忘れないでねw)メソッドたち


なにやら、ここ一両日、コレクションブロックメソッドがらみでアクセスをいただくようになったのは、“るびま”こと、Rubyist Magazine 最新号の Rubyist Hotlinks 【第 14 回】 角谷信太郎さん のコーナーで、なかば恒例となっている map 派、collect 派の話にからめて…のようですね。(これとは別に、Ruby ビギナーのための CGI 入門 【第 3 回】 エラーの修正 のコーナーで speakillof さんには、オブジェクト指向の概念の発明者は誰ですか? への参照もいただきました。ありがとうございます!)


せっかく(?)なので、Ruby の …ect メソッド群のもとになった Smalltalk には、どんな …ect: メソッド群があるのか、SqueakSmalltalk で調べてみました。

| ectSelectors collectionClasses |

collectionClasses := Collection allSubclasses.
collectionClasses add: Collection.

ectSelectors := Set new.

collectionClasses do: [:class |
   ectSelectors addAll: (class selectors select: [:sel | 
      (sel endsWith: #ect:) & (sel asLowercase endsWith: #object:) not])].

^ ectSelectors asArray
=> #(#indicesCollect: #collect: #collect:thenSelect: 
     #overlappingPairsCollect: #pairsCollect: #valuesCollect: 
     #associationsSelect: #withIndicesCollect: #withIndexCollect: 
     #select: #reject: #computeFromNear:far:fov:aspect: #detect: 
     #groupsOf:atATimeCollect: #select:thenCollect: #with:collect:)

あまり面白みのない結果になりました(^_^;)。それぞれについて、ざっと使用例を挙げてみると、

#(1 2 3 4 5) collect: [:each | each * 2]
   " => #(2 4 6 8 10) "
#(1 2 3 4 5) collect: [:each | each * 2] thenSelect: [:each | each > 5] 
   " => #(6 8 10) "
#(1 2 3 4 5) overlappingPairsCollect: [:aa :bb | aa + bb]
   " => #(3 5 7 9) "
#(1 2 3 4) pairsCollect: [:aa :bb | aa + bb]
   " => #(3 7) "
#(a b c) withIndexCollect: [:idx :each | {idx. each}]
   " => #((a 1) (b 2) (c 3)) "
#(1 2 3 4 5) select: [:each | each odd]
   " => #(1 3 5) "
#(1 2 3 4 5) reject: [:each | each odd]
   " => #(2 4) "
#(1 3 4 5) detect: [:each | each even]
   " => 4 "
#(1 2 3 4 5 6) groupsOf: 3 atATimeCollect: [:group | group sum]
   " => #(6 15) "
#(1 2 3 4 5) select: [:each | each even] thenCollect: [:each | each * 2]
   " => #(4 8) "
#(a b c) with: #(1 2 3) collect: [:aa :bb | {aa. bb}]
   " => #((a 1) (b 2) (c 3)) "

と、まあ、こんな感じです。残りは以下に。


#valuesCollect: は、一見 Dictionary に定義されたメソッドかと思いきや、そうではなく ShortRunArray のメソッドで、#collect: だと結果が an Array に自動的に変換されてしまうところを、a ShortRunArray を保ったままにするのが目的のようです。

(#(1 1 1 2 2 3) as: ShortRunArray) valuesCollect: [:val | val * 2]
   " => ShortRunArray ((3 2) (2 4) (1 6)) "


#associationsSelect: は Dictaonary に定義されたメソッドなのですが、#select: との違いは分かりませんでした。

({#a->1. #b->2. #c->3} as: Dictionary) associationsSelect: [:assoc | assoc value odd]
   " => a Dictionary(#a->1 #c->3 ) "
({#a->1. #b->2. #c->3} as: Dictionary) select: [:assoc | assoc value odd]
   " => a Dictionary(#a->1 #c->3 ) "


indices なんたらは Matrix に定義されたメソッド。

(Matrix ones: 3) indicesCollect: [:row :col | row + col]
   " => a Matrix(2 3 4 3 4 5 4 5 6) "
(Matrix identity: 3) withIndicesCollect: [:val :row :col | val * (row + col)]
   " => a Matrix(2 0 0 0 4 0 0 0 6) "


なお、#computeFromNear:far:fov:aspect: は、先の抽出スクリプトで意識して省いた …object: 系と同様、…ect: 群とは関係ないようです。


個人的には、#overlappingPairsCollect: が新しい発見でした。w


関連:


追記
そういえば、Ruby では inject も …ect なんですね。Smalltalk では #inject:into: なので、上のスクリプトには引っかかってきませんでした。

#(1 2 3 4 5 6) inject: 0 into: [:sum :each | sum + each]
   " => 21 "