【Python】PEPを訳して読むーPEP322【ほぼ日】

PEP(Python Enhancement Proposal)はPythonを改良する案、リリーススケジュール、コーディングスタイルなど開発を円滑に進める上で重要なことを文書にまとめておく場所。ここには、Pythonをコーディングするすべての人にとって重要な内容がきっと入っているはず。ビギナーが言うようにもちろん、英語。よって、これをできるだけ毎日、翻訳しながら読み解きまとめていくことで備忘録としていきたいと思います。英語は非常に苦手なので誤訳はご指摘ください。

※以下、アイコンが出てくる箇所は私のコメントになります。それ以外は、翻訳または翻訳のまとめです。

基本情報

Reverse Iteration

PEP:322
Title:Reverse Iteration
逆イテレーション
Status:Final
Type:Standards Track
Created:24-Sep-2003
Python-Version:2.4
Post-History:24-Sep-2003
Replaces:381

アブストラクト

シーケンスの逆イテレーションをサポートするための組み込み関数を追加することを提案します。

動機

インデックス付け可能なオブジェクトでの、逆イテレーションの方法はエラーが発生しやすく、不自然で、可読性に乏しいです。

for i in xrange(n-1, -1, -1):
    print seqn[i]

別のアプローチはそれを繰り返す前にリストを逆にすることで対応しています。この手法は、コンピュータサイクル、メモリ、およびコード行を無駄にします。

rseqn = list(seqn)
rseqn.reverse()
for value in rseqn:
    print value

拡張スライシングは、コードのオーバーヘッドを最小限に抑える3つ目のアプローチですが、メモリ効率、美しさ、または明快さがありません。

逆方向反復は順方向反復よりも一般的ではありませんが、実際には定期的に発生します。以下のユースケースを参照してください。

提案

__getitem __()および__len __()をサポートするシーケンスオブジェクトに対して逆イテレータを作成するbackward()という組み込み関数を追加します。

上記の例は次のように単純化されます。

for i in reversed(xrange(n)):
    print seqn[i]
for elem in reversed(seqn):
    print elem

基本的な考え方は、逆イテレータを指定する最も明確でエラーの少ない方法は、順方向で指定してから逆方向に指定することです。

実装は、次のように単純なものになります。

def reversed(x):
    if hasattr(x, 'keys'):
        raise ValueError("mappings do not support reverse iteration")
    i = len(x)
    while i > 0:
        i -= 1
        yield x[i]

言語の構文を変更する必要はありません。この提案は完全に後方互換性があります。

Cの実装と単体テストはhttps://bugs.python.org/issue834422にあります。

BDFLの宣告

このPEPはPy2.4で条件付きで受け入れられました。これは関数が無用であることがわかった場合、Py2.4b1の前に削除できることを意味します。

代替メソッド名

  • reviter:Jeremy Fincherの提案は、iter()の使用と一致
  • ireverse:itertools命名規則を使用
  • inreverse:私以外に誰もこれを好きには思えない

reverseという名前は、基礎となるリストを変更するlist.reverse()の名前と重複するため、候補から外れています。

討論

これは標準ライブラリから取られた逆イテレーションのいくつかの例であり、逆反復がなぜ必要だったかについてのコメントです。

  • atexit.exit_handlers()は以下を使用します。
    while _exithandlers:
        func, targs, kargs = _exithandlers.pop()
            . . .

    このアプリケーションではポッピングが必要なので、新しい機能は役に立ちません。

  • heapq.heapify()は、for i in xrange(n//2 - 1, -1, -1)を使用します。より高いレベルの順序付けがより低いレベルの順序付けのペアより簡単に形成されるからです。 このアルゴリズムのフォワードバージョンは可能です。しかし、逆の方向で基礎となるリストを反復する残りのヒープコードを複雑にするでしょう。for i in reversed(xrange(n//2))は、カバーされる範囲とそれが何回繰り返すかを明確にします。
  • mhlib.test()は以下を使用します。
    testfolders.reverse();
    for t in testfolders:
        do('mh.deletefolder(%s)' % `t`)

    基礎となるリストの末尾が反復中に変更されるため、逆反復の必要性が生じます。

  • platform._dist_try_harder()は、for n in range(len(verfiles)-1,-1,-1)を使用します。ループはverfilesから選択された要素を削除しますが、後の繰り返しのためにリストの残りをそのまま残す必要があります。
  • random.shuffle()for i in xrange(len(x)-1, 0, -1)を使用します。これは、減少し続けるプールからランダムに要素を選択するのを最も簡単に実現します。このアルゴリズムは順方向に実行することができますが、直感的ではなく提示されることはめったにありません。 for i in reversed(xrange(1, len(x)))は視覚的に確認するのがはるかに簡単です。
  • rfc822.Message.__delitem__()は以下を使用します。
    list.reverse()
    for i in list:
        del self.headers[i]

    基礎となるリストの末尾が反復中に変更されるため、逆イテレーションの必要が生じます。

リジェクトされた代替案

iterableを最後まで実行し、結果を保存してから、結果に対してリバースイテレータを返すことによって、invert()をすべてのiterableに適用しようとするいくつかの代替案が提出されました。完全な一般性の概念をいくつか満たしながら、入力を最後まで実行することは、最初から反復子を使用するという目的に反します。また、基礎となるイテレータが無限大の場合、小さな災害が発生します。

関数を別のモジュールに入れたり、型オブジェクトに結び付けたりすることは考えられていません。zip()enumerate()と同様に、この関数は日々のプログラミングで直接アクセスできる必要があります。それぞれが基本的なループ問題、すなわちロックステップ反復、ループカウント、および逆イテレーションを解決します。何らかの形式の点在のアクセスを必要とすると、それらの単純さ、日々の有用性、およびアクセシビリティーが妨げられます。それらは、どのアプリケーションドメインからも独立した、コアとなるループ構造です。

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です