collect は Enumerable#collect だが collect! は Array#collect! だった
Rubyのドキュメントを見て、「あれ? Arrayでcollectが定義されているぞ。eachから派生するイテレータ*1はEnumerableで定義するんじゃなかったか?」と思ったが、よく見たらexclamation markが付いていた。collect!、つまり破壊的メソッドだ。破壊的なイテレータは、Enumerableをインクルードするクラスの方で定義するようだ。
そういう目でドキュメントを眺めると、
- Enumerableで定義しているイテレータはどれも破壊的ではない
- 一方、ArrayやHashで定義しているイテレータは、それらクラスに特有の処理をもつか(Hash#each_key など)、あるいは破壊的であるか(Array#collect! など)、のどちらかの性質を持つものが多い
ということがわかる。おそらく、破壊的メソッドはクラスの持つデータ構造に依存するから、Enumerableで定義するのは無理だということだろう。
余談だが、MatrixクラスはEnumerableをインクルードしていない(eachメソッドがないため)。よって非破壊的なcollectでも当然、Matrix#collectとなっている。
*1:正確には「ブロック付メソッド呼び出し」。