2017-05-10 1 views
1

発電機からの一部のサブセットを除外する最良の方法は何ですか?たとえば、私は文字列 "1023"を持ち、それぞれの数字のすべての可能な組み合わせを生成したいと思います。すべての組み合わせは次のようになります。発電機の除外

['1', '0', '2', '3'] 
['1', '0', '23'] 
['1', '02', '3'] 
['1', '023'] 
['10', '2', '3'] 
['10', '23'] 
['102', '3'] 
['1023'] 

私は、項目のいずれかに先頭の0が含まれているサブセットで興味がないので、有効なものは以下のとおりです。

['1', '0', '2', '3'] 
['1', '0', '23'] 
['10', '2', '3'] 
['10', '23'] 
['102', '3'] 
['1023'] 

私は2つの質問があります。

1)ジェネレータを使用している場合は、先行ゼロを持つものを除外する最適な方法は何ですか?現在、すべての組み合わせを生成し、後でループし、サブセットが有効な場合にのみ続行します。簡単にするために、私はサンプルコードでサブセットを印刷しています。作成されたジェネレータが非常に長いと仮定するか、またはそれが多くの無効なサブセットを拘束する場合、ジェネレータ全体をループすることはほとんど無駄です。無効な項目(先行ゼロがあるもの)が表示されたときにジェネレータを停止し、 'allCombinations'をフィルタにかける方法はありますか?

2)これらがない場合は、先行ゼロとの組み合わせを無視する)。発電機を使用して

コード:

import itertools 

def isValid(subset):   ## DIGITS WITH LEADING 0 IS NOT VALID 
    valid = True 
    for num in subset: 
     if num[0] == '0' and len(num) > 1: 
      valid = False 
      break 

    return valid 

def get_combinations(source, comb): 
    res = "" 
    for x, action in zip(source, comb + (0,)): 
     res += x 
     if action == 0: 
      yield res 
      res = "" 

digits = "1023" 
allCombinations = [list(get_combinations(digits, c)) for c in itertools.product((0, 1), repeat=len(digits) - 1)] 


for subset in allCombinations: ## LOOPS THROUGH THE ENTIRE GENERATOR 
    if isValid(subset): 
     print(subset) 
+1

'filter'組み込み関数を見たことがありますか? –

+0

@aryamccarthy:Python 2では、 'filter'は熱心で、ジェネレータではなくリストを返します。 – 9000

+3

Python 2には、遅延評価のための 'itertools.ifilter'があります。 Python 2のタグは表示されません。 –

答えて

2

フィルタリング「ノー先行ゼロ」のような簡単かつ明白な条件のために、それができます複合ビルディングレベルでより効率的に行うことができます。

カットのすべての組み合わせを生成
def generate_pieces(input_string, predicate): 
    if input_string: 
     if predicate(input_string): 
      yield [input_string] 
     for item_size in range(1, len(input_string)+1): 
      item = input_string[:item_size] 
      if not predicate(item): 
       continue 
      rest = input_string[item_size:] 
      for rest_piece in generate_pieces(rest, predicate): 
       yield [item] + rest_piece 

は、限りそれも面白いではありません。

>>> list(generate_pieces('10002', lambda x: True)) 
[['10002'], ['1', '0002'], ['1', '0', '002'], ['1', '0', '0', '02'], ['1', '0', '0', '0', '2'], ['1', '0', '00', '2'], ['1', '00', '02'], ['1', '00', '0', '2'], ['1', '000', '2'], ['10', '002'], ['10', '0', '02'], ['10', '0', '0', '2'], ['10', '00', '2'], ['100', '02'], ['100', '0', '2'], ['1000', '2']] 

だけが何のフラグメントがゼロをリードしていない場所:ゼロで始まる

>>> list(generate_pieces('10002', lambda x: not x.startswith('0'))) 
[['10002'], ['1000', '2']] 

サブストリングでした再帰的なステップについては決して考慮されていない。

+0

はい、ビルド処理中にフィルタリングを行う方が効率的です。サンプルコード – user1179317

+0

ありがとうございました '0'は実際に有効です。先頭に0が付いている、つまりlenが少なくとも2以上で先頭に '0'があるアイテムのみが無効です。 – user1179317

+0

'lambda x:x == '0' x.startswith( '0')'は動作しません。 – 9000

0

一つの一般的な解決策はただyieldを使用する前に、フィルタリングを試してみることです。私はちょうど降伏する前にあなたのフィルタリングの例を与えている:

import itertools 

def my_gen(my_string): 

    # Create combinations 
    for length in range(len(my_string)): 
     for my_tuple in itertools.combinations(my_string, length+1): 

      # This is the string you would like to output 
      output_string = "".join(my_tuple) 

      # filter here: 
      if output_string[0] != '0': 
       yield output_string 


my_string = '1023' 
print(list(my_gen(my_string))) 

EDIT:ジェネレータの理解代わりに追加されました

import itertools 

my_string = '1023' 
my_gen = ("".join(my_tuple)[0] for length in range(len(my_string)) 
         for my_tuple in itertools.combinations(my_string, length+1) 
         if "".join(my_tuple)[0] != '0') 
+0

あなたの解決策では私が望む答えは得られませんが、私はあなたが単に「歩留まり」の前にフィルタを表示しようとしていると思います。私はそれを考えていたが、私は 'allCombinations'のリストの理解があるので、私はそれを行うことができないと思う。リストの理解をスピードのために固執したいと思いますが、おそらく私は傾けることができません。 – user1179317

+0

C/C++でリストのスペースをあらかじめ割り当てるため、リストの解説は高速です。私は、発電機の解説はジェネレータの機能よりも速いとは信じていません。 –

関連する問題