ヘルプにはリスト内包表記を使用してPythonコードを改善する必要
-
12-09-2019 - |
質問
私は言語についての詳細を学ぶために自宅で小さなPythonプログラムを書いてきました。私が理解しようとした最新の機能がリスト内包表記されています。私は私の車は、私が過去に変更油を得ている頻度に基づいて、その次のオイル交換を必要とするとき推定小さなスクリプトを作成しました。以下のコードでは、oil_changes
は、私はオイルを変えましれるマイレージのリストです。
# Compute a list of the mileage differences between each oil change.
diffs = [j - i for i, j in zip(oil_changes[:-1], oil_changes[1:])]
# Use the average difference between oil changes to estimate the next change.
next_oil = oil_changes[-1] + sum(diffs) / len(diffs)
のコードは、正しい答えを生成します(チェックするために手で数学をやった)が、それはまだ非常にPython的に感じることはありません。私は、最初の行に元のリストの不要なコピーをたくさんやっていますか?私はこれを行うにはより良い方法がありますように感じるが、私はそれが何であるかわからない。
解決
oil_changes
リストが非常に長い場合を除き、、あなたは本当に心配する必要はありません。しかし、「ストリームベース」のコンピューティングのファンとして、私はそれはitertools
あなたはO(1)スペース(そしてもちろんのO(N)時間であなたのnext_oil
値を計算するために必要なすべてのツールを提供していますことを指摘することは興味深いことだと思います! - )に関係なく、それは、len(next_oil)
でどのように大きなN、得ません。
izip
自体、それが唯一のビットに乗法定数を減少させるが、O(N)としてあなたのスペースの需要を残しているため、不十分です。 Oまでこれらの要求をもたらすために、キーアイデアは、(1)izip
でtee
をペアリングすることである - と良いシンプルな昔ながらのループの賛成で、とにかく空間でO(N)になるリストの内包を回避します! - )。ここに来ます:
it = iter(oil_changes)
a, b = itertools.tee(it)
b.next()
thesum = 0
for thelen, (i, j) in enumerate(itertools.izip(a, b)):
thesum += j - i
last_one = j
next_oil = last_one + thesum / (thelen + 1)
代わりに、リストからスライスを取っての b
、一度、クローンのいずれかを、私たちは、それにイテレータを取る(その2つの独立した前進可能なクローンを作る)、それをティーショット、事前。 tee
Xは種々のクローンの前進の間の最大絶対差であるスペースO(X)をとります。スペース要件が明確であるので、ここでは、二つのクローン進歩は、せいぜい1によって異なりますO(1)。
izip
が作る1つずつ2やや斜めクローンイテレータの「ビュン」、そして私たちは、我々はの長さ、つまり、ループを通過する回数を追跡することができenumerate
でそれをドレスアップ反復可能我々は(!enumerate
は0から始まるので、我々は、最終的な式に+1を必要とする - )に反復しています。私たちは(+=
でも良いですが、それは長さを追跡しません! - )数字の罰金であるシンプルなsum
、との合計を計算
これはlast_one = a.next()
を使用するループの後には魅力的だが、a
が実際に排出されるので、それは動作しません - izip
はそれがa
が終わる実現する前に、それはb
1の最後の時間を進んでいるように、その引数のイテレート可能オブジェクトは、左から右へ進み! 。 Pythonのループ変数はループ自体の範囲に限定されるものではないので、それは、OKです - ループの後、j
はまだちょうどb
ようizip
はあきらめた前に進めるthelen
で最後に抽出した値は、(まだによって返された最後のカウント値を持っていますenumerate
)。私はそれがより明確かつ読みやすいと思うので、私は、まだ値last_one
に名前を付けるのではなく、最終的な表現に直接j
を使用しています。
だからそれがある - 私はそれが有益だった願っています! - ) - あなたはこの時間を提起し、特定の問題の解決のために、やり過ぎであることがほぼ確実だが。 「!Impara L'アルテ、電子mettilaダPARTE」...「アートを学び、その後、脇に置きます」 - - 我々イタリア人は古代のことわざを持って、私はここに非常に適用だと思うどの:それは学ぶことは良いことです先進的かつ洗練された方法は、非常に難しい問題を解決する場合に、あなたはそれらを満たしていますが、簡単な、普通の問題の非常に多くの一般的なケースでは、シンプルさと直接性のために行く必要のあるすべてのための - ではない可能性が高い勝った高度なソリューションを適用「トンが必要になること - !)
他のヒント
これを試してください:
assert len(oil_changes) >= 2
sum_of_diffs = oil_changes[-1] - oil_changes[0]
number_of_diffs = len(oil_changes) - 1
average_diff = sum_of_diffs / float(number_of_diffs)
itertools
のパッケージには、追加の発電スタイルの機能を提供します。たとえば、あなたはいくつかのメモリを節約するためにizip
の代わりにzip
を使用することができます。
あなたが代わりにリストの内包のジェネレータにaverage
を変えることができますので、あなたはまた、おそらくdiffs
関数を書くことができます:
from itertools import izip
def average(items):
sum, count = 0, 0
for item in items:
sum += item
count += 1
return sum / count
diffs = (j - i for i, j in izip(oil_changes[:-1], oil_changes[1:])
next_oil = oil_changes[-1] + average(diffs)
また、あなたはへdiffs
のあなたの定義を変更することができます:
diffs = [oil_changes[i] - oil_changes[i-1] for i in xrange(1, len(oil_changes))]
私はそれが本当に巨大な改善はありませんが、知りません。あなたのコードがあるとしてかなり良いです。
これは本当に、罰金です。ないすべては(あなたは関係なく、あなたがそれをフレームか、そうでない場合は、単純な計算では、いくつかのステップを持っていないしました)簡単です。 itertools.isliceとitertools.izipが、コード内の余分な手順はちょうどそれをさらに複雑になる(脇izipから)を使用してのように、コピーを低減するためのオプションがあります。ないすべてがリスト内包する必要がありますが、それは時々、審判の判定です。何があなたにきれいに見えますか?それが最善の理解読み込み隣の男とは何だろうか?あなたは3ヶ月でそのバグを修正するために戻ってきたときに、あなたが何を理解する?
私は、不要なコピーをたくさんやっています 最初の元のリストの ライン?
技術的には、はい。現実的には、ありません。あなたは時間のあなたのオイル文字通り何百万人を変更した場合を除き、スピードペナルティが重要になることはほとんどありません。あなたはzip
するizip
を変更することができますが、それはほとんどそれだけの価値は思わない(とPython 3.0で、zip
が効果的にがのizip
です)。
を挿入している古い引用はここにを。
(oil_changes[:-1]
はとにかく最短入力シーケンスの長さに切り捨てますので、あなたも、ちょうどoil_changes
でzip()
を置き換えることができます)。