リスト(数列)の差分のリストがほしかったんだけど、はまったのでメモ。
最初、↓こう書いた。
def difference(lis): d = reduce(lambda x, y: (x[0].append(y-x[1]), y), lis, ([],0)) d[0].pop(0) return d[0] a = range(10) diff = difference(a) print diff
2行目のx[0].append(y-x[1])で差分を蓄積していってそれを返している(3行目は余分な初期値との差分があるからそれを取り除いている)。
ところが実行するこうだ。
^o^ > python difference.py Traceback (most recent call last): File "difference.py", line 9, in diff = difference(a) File "difference.py", line 3, in difference d = reduce(lambda x, y: (x[0].append(y-x[1]), y), lis, ([],0)) File "difference.py", line 3, in d = reduce(lambda x, y: (x[0].append(y-x[1]), y), lis, ([],0)) AttributeError: 'NoneType' object has no attribute 'append'
‘NoneType’のオブジェクトは’append’なんて属性持ってないと。しばらく悩んだけど、原因はタイトルのとおり。
1回目のappendを呼び出したところでNoneが返ってくるので、2回目の呼び出しでは失敗する。
>>> a = range(5) >>> a [0, 1, 2, 3, 4] >>> type(a.append(5)) <type 'NoneType'>
結局、次のように書き換えたらうまくいったけど、関数がひとつ増えてしまった。
def difference2(lis): d = reduce(lambda x, y: (add_list(x[0], y-x[1]), y), lis, ([],0)) d[0].pop(0) return d[0] def add_list(lis, x): lis.append(x) return lis a = range(10) diff = difference2(a) print diff
^o^ > python difference2.py [1, 1, 1, 1, 1, 1, 1, 1, 1]
RubyのArray#pushならselfを返してくれるから素直に書けるんだけどなあ。
こんばんは
いつも勉強させていただいています。
私も同じ事を考えた事がありますがappendがNone を返すのには理由があり、
http://stackoverflow.com/questions/1682567/why-does-pythons-list-append-evaluate-to-false
↑の回答にあるように
Command–query separation
の原則に従っているからです。
破壊的なコマンドに対しては一貫性を持たせて
プログラマに意識させるような意図があるんだと思います。
リストの差分を返すならば
以下のようなやり方もいいかと思います。
from itertools import starmap
from operator import sub
difference = lambda l:list(starmap(sub,zip(l[1:],l)))
前エントリでも触れられていたzipを使ってます。
1つずらしたlistをzipして引数にして差分をとる形です。
importが増えるきらいはありますが
Haskellに慣れ親しんだ人にはむしろしっくりくるのでは無いでしょうか?
蛇足ですが、python2系ではzipは前エントリのコメントで触れた
itertools.izipを使うほうが効率的です。
python3系ではzipは2系のizipと同様の動作をします。
いろいろ教えてくださってありがとうございます。
なるほど、1つずらしたリストとzipを使って差分を取るんですか。勉強になります。
更に蛇足ですがリスト内包表記を使う方がより
直観的でpythonicかも知れないです。
>>> difference = lambda l:[x-y for x,y in zip(l[1:],l)]
>>> difference(range(10))
[1, 1, 1, 1, 1, 1, 1, 1, 1]
インポートも要りませんし、より短いです。
golfがしたいわけでは無いでしょうが ;)
これはいいですね。わかりやすいです。