takeやdrop
- d.y.d: ヘッドドロップ (http://www.kmonos.net/wlog/79.html#_2005071114)
k.inabaさんのheadやdropをPythonでどう書けるか検討してみた。
from itertools import dropwhile, takewhile # Pythonで # Rubyの妄想で # 結果 [1,2,3,4,5][0] # [1,2,3,4,5].head 1 [1,2,3,4,5][:1] # [1,2,3,4,5].head(1) [1] [1,2,3,4,5][:2] # [1,2,3,4,5].head(2) [1, 2] list(takewhile(lambda x: x<3, [1,2,3,4,5])) # [1,2,3,5,5].head{|x|x<3} [1, 2] [1,2,3,4,5][1:] # [1,2,3,4,5].drophead [2, 3, 4, 5] [1,2,3,4,5][1:] # [1,2,3,4,5].drophead(1) [2, 3, 4, 5] [1,2,3,4,5][2:] # [1,2,3,4,5].drophead(2) [3, 4, 5] list(dropwhile(lambda x: x<3, [1,2,3,4,5])) # [1,2,3,4,5].drophead{|x|x<3} [3, 4, 5] [1,2,3,4,5][-1] # [1,2,3,4,5].tail 5 [1,2,3,4,5][-1:] # [1,2,3,4,5].tail(1) [5] [1,2,3,4,5][-2:] # [1,2,3,4,5].tail(2) [4, 5] sorted(takewhile(lambda x: x>3, # [1,2,3,4,5].tail{|x|>3} [4, 5] reversed([1,2,3,4,5])), key=[1,2,3,4,5].index) [1,2,3,4,5][:-1] # [1,2,3,4,5].droptail [1, 2, 3, 4] [1,2,3,4,5][:-1] # [1,2,3,4,5].droptail(1) [1, 2, 3, 4] [1,2,3,4,5][:-2] # [1,2,3,4,5].droptail(2) [1, 2, 3] sorted(dropwhile(lambda x: x>3, # [1,2,3,4,5].droptail{|x|x>3} [1, 2, 3] reversed([1,2,3,4,5])), key=[1,2,3,4,5].index) [1,2,3,4,5].pop() # [1,2,3,4,5].pop 5 不可能 # [1,2,3,4,5].pop(1) [5] 不可能 # [1,2,3,4,5].pop(2) [4, 5] 不可能 # [1,2,3,4,5].pop{|x|x>3} [4, 5]
Pythonに関してまとめると、
- リストとイテレータに分けて考えるべき
- リストもイテレータも後ろから slice、take、drop するのは苦手 ⇒ ダイレクトに実現するメソッドがない
- 後ろから slice、take、drop する場合
>>> from itertools import dropwhile >>> L = xrange(1,6) >>> list( dropwhile(lambda x: x>3, reversed(L)) ) [3, 2, 1]
- 逆順の逆順を何とか1手で実現できないだろうか?
- 逆順にしなくても、もっと上手に後ろからとる方法は別にあるのだろうか?
- sliceはPythonの方が記号的で直感的に読みやすい
- Pythonはsliceが演算子で、take、dropが関数なので統一感がない。Rubyは全てメソッドで統一感がある
- Pythonはiteratorを受け付けるがRubyは恐らく受け付けない
LL魂でもMatzさんが言っていたがRubyは早く遅延評価版のコレクションを導入すべきだよなあ。そこらへんはPythonも組み合わせにより受け付けてくれない場合があるのでもうちょっとしっかりプロトコルを作るべき。というか実装の問題?
追記(2007/11/18):
reversed([1,2,3,4,5])よりも、[1,2,3,4,5][::-1]の方が若干簡単だが、本質的なところで違いはない。イテレータにもスライシングが使えたら便利だが難しいのだろうか…。