map_between をやってみる
面白そうなのを見つけたのでやってみた。
やってみた
とりあえずこんな感じにしてみた。Ruby の each_cons みたいなものがPythonにもあるのかも。
#!/usr/bin/env python #-*- coding:utf8 -*- from itertools import imap test = [1, 2, 3, 4, 5] def map_between(x, func): return list(imap(func, x, x[1:])) print map_between(test, lambda x, y: x + y) #=> [3, 5, 7, 9] class List(list): def map_between(self, func): return list(imap(func, self, self[1:])) mylist = List(test) print mylist.map_between(lambda x, y: x + y) #=> [3, 5, 7, 9]
追記(2011/01/05)
良い子の戦国大名(id:imagawa_yakata)からご指摘をいただきました。ありがとうございますm(_ _)m
1. 引数lstがスライシングに応答しないとエラーになってしまうので、それを気にする必要の無い書き方にしたい。
2. スライシングすると別のオブジェクトを作って返す。仮に巨大なリストを引数に渡したときにそのリストの全長-1の大きさのリストを複製するのは(大したコストじゃないんだろうけど)嫌だ。
1. に関しては、組み込みのmap関数もシーケンス型しかサポートしてない気がするので、まースライシングできんかったらエラーでもいいかもなぁと個人的には思いました。
2. に関してはあまりよろしくないですね。確かに巨大なリストを渡した時に id:imagawa_yakataさんの方が圧倒的に良い感じですね。ジェネレータが返ってくるので、結果リストの最初の二つしか利用しませんとかいうケースの場合にも余計な処理をしないので嬉しい感じ。
という事を考えてたら、リスト前提で考えるんだったら、imap使う必要ないんじゃね?と思ったので書いてみた。
#!/usr/bin/env python #-*- coding:utf8 -*- test = [1, 2, 3, 4, 5] def map_between1(lst, func): return [func(lst[i], lst[i + 1]) for i in range(len(lst) - 1)] def map_between2(lst, func): return (func(lst[i], lst[i + 1]) for i in range(len(lst) - 1)) print map_between1(test, lambda x, y: x + y) #=> [3, 5, 7, 9] print list(map_between2(test, lambda x, y: x + y)) #=> [3, 5, 7, 9]
「map_between1」はまるっとリストを返すバージョン、「map_between2」はジェネレータを返すバージョン。これなら2番目の指摘の件は大丈夫かと思ったけど、結局funcに渡す時にlst[i]とかはコピーと一緒だから大して変わらんのかな?
追記(2011/01/06)
結局の所、新しく書いた map_between1 と map_between2 は渡された引数lstに大して range(len(lst) -1) とかやってるから、コピーするのと一緒で駄目駄目だった。ベンチマークぽい事もやってみたので、そのうち書こう
余談
はてなブログってシンタックスハイライトされなくなったの?