2016-04-13 22 views
1

Çok büyük (gigabaytlar) keyfi nesneler verildiğinde (buna ints için benzer bir çözüm gördüm), ya eşdeğerlikle alt listeleri kolayca gruplayabilir miyim? Orijinal listeyi tüketen yerinde veya jeneratör tarafından.Nesnelerin bir listesini devamlılık ile nasıl gruplayabilirim?

l0 = [A,B, A,B,B, A,B,B,B,B, A, A, A,B] #spaces for clarity 

İstenilen sonuç:

for g in gen(l0): 
    print g 
.... 
['A', 'B'] 
['A', 'B', 'B'] 
['A', 'B', 'B', 'B', 'B'] 
.... 
: Böyle çalışacak bir jeneratör gen0(l) olarak yapılabilir

#find boundaries 
b0 = [] 
prev = A 
group = A 
for idx, elem in enumerate(l0): 
    if elem == group: 
     b0.append(idx) 
    prev = elem 
b0.append(len(l0)-1) 

for idx, b in enumerate(b0): 
    try: 
     c = b0[idx+1] 
    except: 
     break 
    if c == len(l0)-1: 
     l1.append(l0[b:]) 
    else: 
     l1.append(l0[b:c]) 

:

[['A', 'B'], ['A', 'B', 'B'], ['A', 'B', 'B', 'B', 'B'], ['A'], ['A'], ['A', 'B']] 

ben şöyle bir döngü versiyonunu yazdı

vb?

DÜZENLEME: Python 2.6 veya 2.7

EDIT kullanarak: Tercih çözüm çoğunlukla kabul Yanıta göre:

def gen_group(f, items): 
    out = [items[0]] 
    while items: 
     for elem in items[1:]: 
      if f(elem, out[0]): 
       break 
      else: 
       out.append(elem) 

     for _i in out: 
      items.pop(0) 
     yield out 
     if items: 
      out = [items[0]] 

g = gen_group(lambda x, y: x == y, l0) 

for out in g: 
    print out 
+0

Bu yüzden bir listeyi alt listelere ayırmak istersiniz, böylece her alt liste ilk öğe olarak "A" ile başlar ve yalnızca bu tek "A" öğesini içerir? –

+0

Hayır, her bir alt listenin sadece ilk A olmasını ve daha sonra birçok B'nin gelmesini istiyorum. – jambox

+0

Listenin "A" ile başlaması güvenli bir varsayım mıdır? –

cevap

1

İşte görevi gerçekleştirmek için basit bir jeneratör. Bu, listenin "A" ile başladığını varsayar (DELIMETER değişken).

Örnek çıktı:

for out in gen_group(l0): 
    print out 

['A', 'B'] 
['A', 'B', 'B'] 
['A', 'B', 'B', 'B', 'B'] 
['A'] 
['A'] 
['A', 'B'] 
['A'] 

Karşılaştırmalı Zamanlamalarını üretir:

timeit.timeit(s, number=100000)s kod satırlı dize mevcut cevap, her bir (test etmek için kullanılır aşağıda listelenmiştir):

     Trial 1 Trial 2 Trial 3 Trial 4 | Avg 
This answer (s1):  0.08247 0.07968 0.08635 0.07133 0.07995 
Dilara Ismailova (s2): 0.77282 0.72337 0.73829 0.70574 0.73506 
John Coleman (s3):  0.08119 0.09625 0.08405 0.08419 0.08642 

Bu cevap en hızlı, ama çok yakın.Farkın John Coleman'ın cevabındaki ek argüman ve anonim işlev olduğundan şüpheleniyorum.

s1="""l0 = ["A","B", "A","B","B", "A","B","B","B","B", "A", "A", "A","B"] 

def gen_group(L): 
    out = ["A"] 
    while L: 
     for ind, elem in enumerate(L[1:]): 
      if elem == "A": 
       break 
      else: 
       out.append(elem) 

     for i in range(ind + 1): 
      L.pop(0) 

     yield out 
     out = ["A"] 

out =gen_group(l0)""" 

s2 = """A, B = 'A', 'B' 
x = [A,B, A,B,B, A,B,B,B,B, A, A, A,B] 
map(lambda arr: [i for i in arr[0]], map(lambda e: ['A'+e], ''.join(x).split('A')[1:]))""" 

s3 = """def subListGenerator(f,items): 
    i = 0 
    n = len(items) 
    while i < n: 
     sublist = [items[i]] 
     i += 1 
     while i < n and not f(items[i]): 
      sublist.append(items[i]) 
      i += 1 
     yield sublist 

items = ['A', 'B', 'A', 'B', 'B', 'A', 'B', 'B', 'B', 'B', 'A', 'A', 'A', 'B'] 
g = subListGenerator(lambda x: x == 'A',items)""" 
+0

İyi ama genel bir çözüm istiyordum - A ve B sadece yer tutucularıdır, bu yüzden C/D olabilir veya (aslında) bazı öznitelikleri karşılaştırdığım nesnelerdir, böylece obj0.att1 == obj1.att1 ile çalışır. Bence bu temelde öyle olsa da, düşünmek için bana bir dakika verin ... – jambox

+0

Zamanlamalara göre, ben buna bir beraberlik derim. – jambox

+0

@jambox, birkaç deneme daha yapmaya karar verdi, hemen hemen bir beraberlik – wnnmaw

1

bu durumda aşağıdaki çalışır. Ne olursa olsun, l[0] != 'A' koşulunu değiştirebilirsiniz. Muhtemelen bir argüman olarak geçebilirim, böylece başka bir yerde yeniden kullanabilirsiniz.

def gen(l_arg, boundary): 
    l = l_arg.copy() # Optional if you want to save memory 
    while l: 
     sub_list = [l.pop(0)] 
     while l and l[0] != boundary: # Here boundary = 'A' 
      sub_list.append(l.pop(0)) 
     yield sub_list 

Bu sizin listesinin başında bir 'A' olduğunu varsayar. Ve liste kopyalanır, liste Gb aralığında olduğunda mükemmel değildir. Orijinal listeyi tutmakla ilgilenmezseniz, hafızadan tasarruf etmek için kopyayı kaldırabilirsiniz. Böyle

+0

l = l_arg.copy() büyük girdiyi kullanarak çok fazla bellek kullanamaz mı? – jambox

+0

Son cümleye bakın. Temel olarak, listeye daha sonra ihtiyacınız yoksa, işlevin ilk satırını kaldırabilirsiniz. – ursan

+0

Tamam benim hatam, bunu söylediğini fark etmemiştim. Başka bir şey 'A' kodlanmış, ben rasgele nesneler dedim. Öyleyse John'un çözümü bunun için puan kazanıyor çünkü ben onun eşdeğer bir func ile geçebiliyorum. – jambox

2

Belki bir şey:

def subListGenerator(f,items): 
    i = 0 
    n = len(items) 
    while i < n: 
     sublist = [items[i]] 
     i += 1 
     while i < n and not f(items[i]): 
      sublist.append(items[i]) 
      i += 1 
     yield sublist 

gibi kullanılan:

>>> items = ['A', 'B', 'A', 'B', 'B', 'A', 'B', 'B', 'B', 'B', 'A', 'A', 'A', 'B'] 
>>> g = subListGenerator(lambda x: x == 'A',items) 
>>> for x in g: print(x) 

['A', 'B'] 
['A', 'B', 'B'] 
['A', 'B', 'B', 'B', 'B'] 
['A'] 
['A'] 
['A', 'B'] 
+1

İşlev geçmek biraz gereksiz görünüyor, bunun için herhangi bir sebep var mı? – wnnmaw

+1

@wnnmaw Özgün soru, bazı keyfi olmayan eşdeğerlik kavramına göre "keyfi nesnelerin" alt listelere bölünmesiyle ilgilidir. Dize eşdeğerliği ile çok az şey yapılacağından şüpheleniyorum ama yeni bir alt listenin başlaması gerekip gerekmediğini belirlemek için bazı Boolean işlevi var. Açıkçası, kodun değiştirilebilmesi, kondisyonun jeneratöre sabit şekilde bağlanması için yeterince kolay olacaktır. –

+0

Evet, bu, keyfi nesneler üzerinde çalışmasını istedim, bu yüzden bu anlamda daha iyi. OTOH @winmaw sizin için istediğim girdi listesini tüketiyor! Eh, sadece bir onay vermek ... garip. – jambox

2

Ne senin kesme noktası olduğunu varsayalım.

def gen_group(L): 
    DELIMETER = "A" 
    out = [DELIMETER] 
    while L: 
     for ind, elem in enumerate(L[1:]): 
      if elem == DELIMETER : 
       break 
      else: 
       out.append(elem) 

     for i in range(ind + 1): 
      L.pop(0) 

     yield out 
     out = [DELIMETER ] 

fikri listesini kesilip geriye hiçbir şey kalmayıncaya kadar sublists verim etmektir:

>>> A, B = 'A', 'B' 
>>> x = [A,B, A,B,B, A,B,B,B,B, A, A, A,B] 
>>> map(lambda arr: [i for i in arr[0]], map(lambda e: ['A'+e], ''.join(x).split('A')[1:])) 
[['A', 'B'], ['A', 'B', 'B'], ['A', 'B', 'B', 'B', 'B'], ['A'], ['A'], ['A', 'B']] 
+1

çok bellek harcadı ama iyi tek liner :) – marmeladze

+0

kabul, güzel tek liner ama yapamam '' .join (x) çok gigabyte listesi ... :) – jambox

+1

Benim düşünceme göre, bu bir astar şeyleri anlamak zorlaştırıyor ... –

İlgili konular