二次元配列の要素を縦方向に足す


技術メモ帳 - 二次元配列を列方向に処理 で、まず読み込みは素直にしておいて、得られた配列が、

ary = [[1,2,3],[4,5,6],[7,8,9]]

だったとして、さあ、そこからどうするか…というのを考えてみました。Ruby には transpose というのがあるので、これを使うのがよさそうです。

ary.transpose.collect{|row| row.inject(0){|s,e| s+e}}  #=> [12, 15, 18]


Enumerable#sum というのがもし Ruby にあったなら、もっと直感的に書けそうですね。(念のため、Smalltalk の Collection >> #sum の定義は、このようにはなっていません。実際に定義をご覧になって、なぜなのかを考えてみるのも面白いでしょう。っても、メソッドのコメントにその“答え”そのものずばりが書いてあるのですが…(^_^;))

module Enumerable
  def sum
    inject(0){|s,e| s+e}
  end
end
ary.transpose.collect{|row| row.sum}


さて。我らが Smalltalk ではどうか?


Ruby と異なり、Smalltalk では Collection >> #+ が対応する各要素の和を要素に持つ配列を返します。(余談ですが、Smalltalk-76 のころまでは SmalltalkRuby と同じ仕様でした…)

#Ruby
[1,2,3] + [4,5,6]      #=> [1,2,3,4,5,6]
"Smalltalk"
#(1 2 3) + #(4 5 6)   " => #(5 7 9) "

ちなみに、連結させたいときには、#, という名前のメソッドをレシーバに起動させます。

"Smalltalk"
#(1 2 3) , #(4 5 6)   " => #(1 2 3 4 5 6) "


この #+ の振る舞いを利用すると、Smalltalk で、くだんの処理は #inject:into: を用い、これまた #inject:into: の使い方としてはごくありふれた記述として…、次のように書くことができます。

| array |
array := #((1 2 3) (4 5 6) (7 8 9)).

array inject: #(0 0 0) into: [:sum :each | sum + each]
=> #(12 15 18)


さらに、Smalltalk には #sum がすでにあるので、

#((1 2 3) (4 5 6) (7 8 9)) sum

と書いて済ませてしまうほうがクールでしょう。ただし、ここで起動される #sum は、なにも二次元配列専用のメソッドだというわけではない点に注意してください。

#(1 2 3 4 5 6 7 8 9 10) sum     " => 55 "
{1@1. 2@2. 3@3. 4@4. 5@5} sum   " => 15@15 "


このように普遍的な記述が可能で、もちろんそれに対する期待通りの結果を返してきてくれることの背景には、前述の Collection >> #+ の振る舞いが大きく係わっています。それとからめて Collection >> #+ は、その実装の方法も面白いので、興味のあるかたは是非、定義を見てみてください。「+」を(必要ならタイプして入力後、)選択して、browse it(cmd/alt + b)か、implementors of it(cmd/alt + m)でブラウザが開くので、#+ メソッドの一覧から Collection >> #+ を選べば呼び出せます。

http://squab.no-ip.com:8080/collab/uploads/implementorsofplus.png


いわゆる“ダブルディスパッチ”という手法が使われています。