too late amatuar programmer

[python] リストから条件を満たす最初の要素を抽出する関数

2010-07-19 by ebon | Lavel:

pythonを書いていたら、おもむろに、以下の動作をする関数が欲しくなった.
" リストから、条件を満たす最初の要素を抽出する "
haskellでいうところの,find()にあたる関数というとイメージしやすいだろうか.

単に、" 一致する最初の要素を抽出する " という要件であれば、.index()メソッドがある.
しかし、求めているものは、完全一致する要素の抽出ではなく、
条件を与え、それを満たす最初の要素を抽出する関数だ.

条件を与えるという点で、" filter的な振る舞いをしつつ、それの真になる最初の要素 "
というイメージになるかな.

buludinメソッドの中に特になさそうだったので、高階関数関連のモジュール,itertoolsを調べてみた.

takewhile():predicateが真である限りiterableから要素を返すイテレータ
dropwhile():predicateが真である限りは要素を無視し、その後は全ての要素を返すイテレータ

というhaskellおなじみの関数はあったが、find()に対応する関数はないようだ.

# 調べ不足でどこかにあるのかもしれない..ただ、それはそれで、後に見つけたときに、
自分のアプローチのダメな点が分かりやすくなるだろうから良しとしたい..

ということで、haskellでいうfind()にあたる関数をpythonで書いてみる.
まず参考に、動かしながら挙動を確認してみようと思い、
takewhileをそのままコピペしたら、

AttributeError: 'list' object has no attribute 'next'

というエラーが出てしまった..そもそも for の中でnext()は呼ばれているはずなので、
ここで,.next()を呼んでいる理由が分からない.
ひとまず流して、、dropwhileの方を参考にする.
  1. def find_first(predicate, iterable):  
  2.     # my_find(lambda x: x<5, [6,7,5,4,1]) --> 4  
  3.     iterable = iter(iterable)  
  4.     for x in iterable:  
  5.         if predicate(x):  
  6.             return x  
  7.         else:  
  8.             continue  
  9.   
  10. test1 = find_first(lambda x: x<5, [6,7,5,4,1])  
  11.    


再帰で書かれている訳ではないので、
haskellと対応させにくい感じはある.

なので、いわゆる明示的な再帰バージョンも書いてみる.

と思ったが、pythonでろくに再帰を書いたことがなく、
リストに対して再帰する時の書き方から始めなければならない..
ちまたにあるpythonの再帰のサンプルでは、
数字をマイナスしていくタイプの再帰を例としたものばかりな感じだ.
そんな中で以下の方の記事がとても参考になった.

Python でリストに対する再帰的な関数の適用

# これ以外でも参考になりそうなものが多くある感じ.重宝させていただきたい..

それで書いたものが以下.

  1. def find_first2(predicate,L):  
  2.     """ 
  3.     porpose : リストから、与えられた条件を満たす最初の要素を抽出する 
  4.     contract: find2 :: (a -> Bool) -> [a] --> a 
  5.     example : find2(lambda x: x < 5, [6,7,8,5,3,7]) --> 5 
  6.     """  
  7.     if L == []: return  
  8.     if predicate(L[0]):  
  9.         return L[0]  
  10.     else:  
  11.         return find2(predicate,L[1:])  
  12.   
  13. test2 = find_first2(lambda x: x < 5, [6,7,8,5,3,7])  
  14.    

pythonでhaskell式の書き方がされてないのは、
遅延評価がデフォルトで組み込まれてないからだろうか.
iterとyieldでジェネレータ化?しておかないと、
リストをまず全て読み込んでしまうということだろうか?

その辺りの知識・理解がなさ過ぎるな..
浅いとこばかりで遊んでることを痛感してしまう.
以下の記事などを読んで勉強したい.

Pythonの技法:ジェネレータを用いた遅延リストの構築

0 comment:

Post a Comment