配列にsumを適用

sum()の引数は通常、数値のリストだと思うが、リストにも適用できるのは知らなかった。

>>> sum([[1], [2], [3,4]], [])
[1, 2, 3, 4]

文字列はダメらしい。

>>> sum(['a', 'b', 'c'], '')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sum() can't sum strings [use ''.join(seq) instead]

親切にもエラーが出てjoinを使えとメッセージが出る。こういうところは、非常にPythonらしいところ。他は例えば、デフォルトでリストを辞書のキーにできないようにしているのも典型。


sumとreduceどちらが速いかということだが、+でリスト同士結合するとインスタンス作りまくりだと思われるので、どっちみち速度を気にする場合はappendするような方法にした方が良いと思う。

>>> reduce(lambda ls0, ls1: ls0.extend(ls1) or ls0, [[1], [2], [3,4]])
[1, 2, 3, 4]

ところで、Pythonのシーケンス型(の類)の判定は、isinstance(ls, (list, tuple))で良いのかな?listとtuple以外で(イテレータ以外の)シーケンスを返すものってあったかな?文字列型(の類)は厳密にはEAFPで文字列型が持つメソッド読んでみてというやり方がPythonクックブックに載っていたと思う。イテレータまで含めたiterableを判定するのはどうするのだろう?Python3でrange()やdict.keys()などは、rangeクラスやdict_keysクラスのオブジェクトを返すが、isinstance(d.keys(), types.GeneratorType)はFalseになったなあ。Pythonは厳密な型以外の型の判定は結構難しい。少なくとも推奨されている方法というのは存在しないと思われる。

コレクション vs 配列


以前、ドメイン特化なベースライブラリを作成する場合、メソッドの引数や戻り値でコレクションにすべきか、配列にすべきか、両方に対応すべきかで迷っていた。しかし、上記の記事を見て、だいぶ見えてきた。


ListはIEnumerableを実装しているので、IEnumerableを基本とすべきだと思う。そうすれば、LINQの拡張メソッドともつながるし、コレクションはメソッド一発で配列に簡単に変換できる。引数や戻り値に限らず基本的に、通常はコレクションを使用して、特殊な用途のみに配列を使用すべきだと思う。


VS2008を入手できたので、LINQ to Objectの使い方を色々勉強しないとなあ。情報が少ない気がするが、まだ.NET 2.0を使用しているところが多いのかな。


追記(2009/4/14):

C#2.0以降であれば、配列は下限がゼロの1次元配列には、IListが自動的に実装されるらしい。ということは、関数の引数を受け取る側では、IEnumerableでもIListでも配列を受け取れる。配列の実装は場合分けする必要ない。恐らく、IEnumerableとIListの使い分けは、単にforeachなどで回したいだけか、Addなどもしたいかなど、どこまでのメソッドが必要になるかで、ケースバイケースとなると思う。


参考: