ポテト・プログラミング

「おイモがひとーつ、おイモがふたーつ、おイモがみっつー、おイモがよっつー....」

ポテト・プログラミングとは、データ構造を構築したり逆に分解していくときに関数インターフェース経由でベクタやイテレータを渡すのではなく、for ループの使用を推奨するやり方です。しかしこの方法だと、最適化をおこなうときに関数インターフェース自体に手を加えることになってしまいます。

次の例はファイルの各行の数値を足していく処理をポテト・プログラミング的 に書いたものです。

f = file('test.numbers')
accum = 0.
for line in f.readlines():
    
accum += float(line)
print accum

これと対照的なのが次のような書き方です。

f = file('test.numbers')
print sum(map(float, f.readlines()))

どうしてポテト・プログラミングが良くないんですか?別に問題ないと思いますけど

ポテト・プログラミングと非ポテト・プログラミングのもう少し実践的な比較例と して、システムユーザの一覧を読み込む場合の処理を見てみましょう。

ポテト版

def system_users():
    
lines = file('/etc/passwd').readlines()
    
for line in lines:
        
if len(line[:line.find('#')].strip()):
            
fields = line.split(':')
            
if int(fields[2]) < 1000:
                
yield fields[0]

timeit.py result: 100000 loops, best of 3: 187 usec per loop

ベクタ版:

import pwd
import operator
def system_users():
    
users = pwd.getpwall()
    
users.sort(key=operator.itemgetter(2))
    
for user in users:
        
if user[2] < 1000:
            
yield user[0]
        
else:
            
return

100000 loops, best of 3: 137 usec per loop

「この比較、不公平! ポテト版はスクラッチから実装してるのにベクタ版はラ イブラリ使ってる!」と思うかもしれません。

比較には続きがあります。

import operator

def my_getpwall():
    
lines = file('/etc/passwd').readlines()
    
for line in lines:
        
uncommline = line[:line.find('#')].strip()
        
fields = uncommline.split(':')
        
yield fields

def system_users():
    
users = list(my_getpwall())
    
users.sort(key=operator.itemgetter(2))
    
for user in users:
        
if user[2] < 1000:
            
yield user[0]
        
else:
            
return

10000 loops, best of 3: 168 usec per loop

もし pwd モジュールがなければこのように同等品を書き起こす必要がありま す。でも、プログラムコードを最適化するとき、ループを構成する関数呼び出 し部分をまたいでの変更はしたくないはずです。関数インターフェースを変え ずに最適化ができるようなものを望んでいるはずです。極端な場合、readline を for c in string: if c == 'n' のように自分で書かなければならない場 合もあるのですから。

理想を言えば for user in users なんてループを書かなくて済めばそれに越 したことはありません。Axiom の機能を使って上記と同等の内容をポテト・スタイルで書くと次のようになります。

def axiom_users(s):
    
for u in s.query(User):
        
if u.uid < 1000:
            
yield u.name

そして次がベクタ・スタイルです。

def axiom_users(s):
    
for u in s.query(User, User.uid < 1000):
        
yield u.name

Axiom はイテレーションや条件式をアプリケーションに追加しなくてもいいように、最適化可能な内容をライブラリの内側にできる限り取り込むようになっています。

最終更新 2006-08-04 21:35:12

8月 2006
  1 2 3 4 5 6
7 8 910111213
14151617181920
21222324252627
28293031   
7月
2006
 9月
2006

PotatoProgrammingPotatoProgrammingExplained の訳。

XML-Image Letterimage

© 2006-2010, Yasushi Iwata