話題の join を模した #join: は GNU Smalltalk にはあるみたいなのですが、
$ gst -v GNU Smalltalk version 3.0.1 $ gst GNU Smalltalk ready st> #('this' 'is' 'a' 'pen') join: ' ' 'this is a pen'
残念ながら Squeak Smalltalk には無いので、同じことをしたいときには #inject:into: とか #do:separatedBy: とか #streamContents: とかを駆使しつつ手続き的にやらないといけません。
| array | array := #(this ia a pen). ^array allButFirst inject: array first into: [:result :each | result, ' ', each] "=> 'this ia a pen' "
| out | out := ''. #(this ia a pen) do: [:each | out := out, each] separatedBy: [out := out, ' ']. ^out "=> 'this ia a pen' "
| out | out := String new writeStream. #(this is a pen) do: [:each | out nextPutAll: each] separatedBy: [out space]. ^out contents "=> 'this is a pen' "
String streamContents: [:ss | #(this is a pen) do: [:each | ss nextPutAll: each] separatedBy: [ss space]] "=> 'this is a pen' "
どれでもいいのですが、最後のあたりをメソッドに焼き直して Collection [注*1] に定義すれば、Squeak Smalltalk でも #join: を使えるようになるわけです。
… と、ここまで書いたところで、念のため…と思って #do:separatedBy: の senders-of (alt + n) を調べてみたら、はたしてCollection>>#asStringOn:delimiter: なる便利なものを発見してしまいました。あ、いや、どこかで見かけた覚えはあるような気もしなくもないですが、だとしても今の今まで肝心なところで完全にスルーしていました。orz
String streamContents: [:ss | #(this is a pen) asStringOn: ss delimiter: ' '] "=> 'this is a pen' "
ということで、気を取り直してこれを使って #join: を定義。
Collection >> join: delimString ^String streamContents: [:ss | self asStringOn: ss delimiter: delimString]
#(this ia a pen) join: ' ' "=> 'this is a pen' "
めでたし、めでたし。
まあ当初の予定通り、#do:separatedBy: を使ったとしても、そんなには変わりませんけれどもね。
SequenceableCollection >> join: delimString ^String streamContents: [:ss | self do: [:each | ss nextPutAll: each] separatedBy: [ss nextPutAll: delimString]]
余談ですが、最後だけデリミタを変えたいときのための #asStringOn:delimiter:last: なんてのもついでに見つけました。あ、いや、どこかで見かけた覚えはあるような気も…(以下略
| out | out := String new writeStream. #(a b c d) asStringOn: out delimiter: ', ' last: ' and '. ^out contents "=> 'a, b, c and d' "
*1:当初は断然 SequenceableCollection のほうがよいように思われましたが、もちろん順序があればその順で、というのはいいとして、順序がない場合でもそれなりに列べばいいような気もしてきたので、Collection に変更。すぐあとに出てくる #asStringOn:delimiter: も Collection ですしね。