ループの最後でコードを繰り返さないようにするのに役立つ3つの一般的なアプローチがあると思います。3つすべてについて、文字列内の単語を数える、自分の問題とは少し異なる問題の例を使用します。これが「デフォルト」バージョンで、コードと同様に、ループの最後でいくつかのロジックを繰り返します。
from collections import Counter
def countWords0(text):
counts = Counter()
word = ""
for c in text.lower():
if c not in "abcdefghijklmnopqrstuvwxyz'-":
if word:
counts[word] += 1
word = ""
else:
word += c
if word:
counts[word] += 1 # repeated code at end of loop
return counts
最初のアプローチは、すべての文字の後に「サブシーケンスの終了」処理(の一部)を実行することです。これにより、シーケンスがその文字の直後に終了した場合に簿記が正しくなります。あなたの例では、あなたの「else」条件を排除し、その中で毎回コードを実行することができます。(これはsergergの答えです。)
ただし、これは一部の種類のチェックでは簡単ではない場合があります。単語を数えるために、処理する「部分的な」サブシーケンスからのがらくたが蓄積するのを避けるために、いくつかの追加のロジックを追加する必要があります。これを行うコードは次のとおりです。
def countWords1(text):
counts = Counter()
word = ""
for c in text.lower():
if c not in "abcdefghijklmnopqrstuvwxyz'-":
word = ""
else:
if word:
counts[word] -= 1 # new extra logic
word += c
counts[word] += 1 # this line was moved from above
return counts + Counter() # more new stuff, to remove crufty zero-count items
2番目のオプションは、シーケンスの最後に番兵の値を追加することです。これにより、目的の「サブシーケンスの終わり」の動作がトリガーされます。番兵がデータを汚染するのを避ける必要がある場合(特に数字など)、これは注意が必要な場合があります。最長連続部分列問題の場合、シーケンスの最後の項目と等しくない値を追加できます。None
良い選択かもしれません。私の単語数の例では、単語以外の文字(改行など)は次のようになります。
def countWords2(text):
counts = Counter()
word = ""
for c in text.lower() + "\n": # NOTE: added a sentinel to the string!
if c not in "abcdefghijklmnopqrstuvwxyz'-":
if word:
counts[word] += 1
word = ""
else:
word += c
# no need to recheck at the end, since we know we ended with a space
return counts
3番目のアプローチは、コードの構造を変更して、予期せず終了する可能性のあるシーケンスの反復を回避することです。groupby
fromを使用する他の回答のように、ジェネレーターを使用してシーケンスを前処理することができますitertools
。(もちろん、ジェネレーター関数を自分で作成する必要がある場合は、同様の問題が発生する可能性があります。)
re
単語カウントの例では、モジュールの正規表現を使用して単語を見つけることができます。
from re import finditer
def countWords3(text):
return Counter(match.group() for match in
finditer("[\w'-]+", text.lower()))
適切なPythonicテキストが与えられた場合の出力(countWordsの4つのバージョンすべてで同じです):
>>> text = """Well, there's egg and bacon; egg sausage and bacon;
egg and spam; egg bacon and spam; egg bacon sausage and spam;
spam bacon sausage and spam; spam egg spam spam bacon and spam;
spam sausage spam spam bacon spam tomato and spam;
spam spam spam egg and spam; spam spam spam spam spam spam
baked beans spam spam spam; or Lobster Thermidor a Crevette
with a mornay sauce served in a Provencale manner with shallots
and aubergines garnished with truffle pate, brandy and with a
fried egg on top and spam."""
>>> countWords0(text)
Counter({'spam': 28, 'and': 12, 'egg': 8, 'bacon': 7, 'sausage': 4, 'a': 4,
'with': 4, 'well': 1, 'lobster': 1, 'manner': 1, 'in': 1, 'top': 1,
'thermidor': 1, "there's": 1, 'truffle': 1, 'provencale': 1,
'sauce': 1, 'brandy': 1, 'pate': 1, 'shallots': 1, 'garnished': 1,
'tomato': 1, 'on': 1, 'baked': 1, 'aubergines': 1, 'mornay': 1,
'beans': 1, 'served': 1, 'fried': 1, 'crevette': 1, 'or': 1})