某スレでちょっと flatten がらみの話があったのをみて、そういえば、Squeak システムの Smalltalk には(と言うのは面倒なので、普通、単に Squeak には…と言います。でもこう言うと知らない人は Squeak という言語があるように読んでしまっていけません…)Ruby の flatten みたいのがないなぁ…と常々思っていたのを思い出したので、ちょっとまじめに調べてみました。
で、ググりつつ、行き着いたのが Konz さんのこの投稿。
http://lists.squeakfoundation.org/pipermail/squeak-dev/2003-June/060772.html
まあ、ちょっと運用が面倒ですが、似たような機能はあるみたいですね。
FlattenEncoder stream process: #(1 2 3 (1 2 3 (1 2 3)))
でいけます。ただ、Ruby と違って Squeak システムの Smalltalk では(くどいっ)文字列もコレクションなので、
FlattenEncoder stream process: #(this (is a (pen))) => an OrderedCollection($t $h $i $s $i $s $a $p $e $n)
こんなふうに文字列やシンボルまで flatten されてしまいます。しかたがないので要素が、外枠と同じ species の場合だけ flatten する SpeciesSpecificFlattenEncoder(ながっ)を作りましょう。
FlattenEncoder subclass: #SpeciesSpecificFlattenEncoder instanceVariableNames: 'species ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Postscript Filters' SpeciesSpecificFlattenEncoder >> write: anObject anObject species == (species ifNil: [species _ anObject species]) ifTrue: [super write: anObject] ifFalse: [self writeObject: anObject]
これでめでたく、期待通りの flatten ができます。
SpeciesSpecificFlattenEncoder stream process: #(this (is a (pen))) => an OrderedCollection(#this #is #a #pen)
まああと、SequenceableCollection >> #flatten を定義しておくほうが親切ですね。
SequenceableCollection >> flatten | flatten | flatten _ SpeciesSpecificFlattenEncoder stream process: self. ^ flatten as: self species
こんなんで Ruby に追いつけましたでしょうか。
#(this (is a (pen))) flatten => #(this is a pen)
これで話題にのぼったスクリプトも(効率を無視すれば)比較的素直に記述できます。
array flatten |
もちろん、一層の flatten なら #inject:into: でその場で書けるので別に flatten をわざわざこしらえるまでもないような気もしますが…。
array flatten |