2012-04-05 37 views
5

Öncelikle, örneğin quickly.for C++ tarzı yineleyicinızı gözden geçirin:Daha anlamlı python yineleyicileri nasıl yapılır? Sadece C++ yineleyici gibi

//--- Iterating over vector with iterator. 
vector<int> v; 
. . . 
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) { 
    cout << *it << endl; 
} 

O esnektir. Altta yatan konteyner tiplerini değiştirmek kolaydır. Örneğin, ekleme ve silme işlemlerinin sayısının o kadar yüksek olduğuna karar verebilirdiniz ki bir liste bir vektörden daha verimli olacaktır. Ayrıca birçok kullanışlı üye işlevine de sahiptir. Vektör kullanım yineleyicileri için üye işlevlerinin birçoğu, örneğin, ata, ekle veya sil. Ayrıca, yineleyici (destekleniyorsa) bidirectionaly'yi (++, - gibi) kullanabiliriz. Bu, nesneler gibi bir akışı ayrıştırmak için kullanışlıdır.

Python'un sorunları: 1: Şu anda, döngü sözdizimi için python, C++ 'dan daha az esnektir. (iyi, daha güvenli) 2: "it! = iter.end()" stili yerine, python bir sonraki() öğesinde olmadığı zaman istisna atar. Esnek değil.

Soru 1: Fikrim yukarıda doğru mu?

Tamam. İşte benim soru, daha güçlü bir python yineleyici C++ yineleyiciler kadar güçlü nasıl uygulamak için geliyor? Şu anda, döngü sözdizimi için python, C++ 'dan daha az esnektir. Ayrıca http://www.velocityreviews.com/forums/t684406-pushback-iterator.html gibi bazı olası çözümler buluyorum. ancak kullanıcıdan iteratör sormak yerine bir şeyi push_back yapmasını ister.

Soru 2: Python'da Çift Yönlü Bir Yineleyiciyi uygulamak en iyisidir? Tıpkı http://www.cplusplus.com/reference/std/iterator/BidirectionalIterator/ gibi.

it = v.begin(); 
while(it!=v.end()) { 
    //do sth here 

    if (condition1) 
     ++it;//suppose this iterator supports ++ 
    if(condition2) 
     --it;//suppose this iterator supports -- 
} 

temel özellikleri şunlardır: 1) iki yönlü, 2) daha basit "son" kontrol sözde kodu aşağıdaki gibidir. "++" veya "-" operatörleri veya ortak işlevler önemli değildir (yine de anlamsal bir fark yoktur). Ben cevaplar bazı olası çözümleri var :

Güncelleme

sayesinde (n)

i = 0 
while i < len(sequence): # or i < len and some_other_condition 
    star_it = sequence[i] 
    if condition_one(star_it): 
     i += 1 
    if condition_two(star_it): 
     i = max(i - 1, 0) 

Ancak dizinin aksine, listenin rastgele erişim olmalıdır Ç. Python'daki "liste" nesnesinin dahili olarak, bağlantılı liste benzeri şeyler kullanılarak gerçekleştirildiğini varsayalım. Böylece, bu süre döngü çözüm verimli değildir. Ancak, C++ 'da, "rastgele yineleyici", "çift yönlü yineleyici" var. Nasıl daha iyi bir çözüm bulmalıyım? Teşekkürler.

+1

"Python" da yapamayacağınız bir şeyin somut bir örneğini "C++" ile kolayca yapabiliyor musunuz? –

+0

Python'un verim + istisnası mekanizması inanılmaz derecede esnektir (fonksiyonel programlamada "süreklilik" olarak adlandırdığımız şeydir). Onları düzgün kullanmayı öğrenin ve ödüllendirileceksiniz. Bileşik yineleyicileri Python'da C++ 'dan tanımlamak çok daha kolaydır. –

+0

Bu soru çok belirsiz ve mevcut formdaki soruya gerçek bir cevap yok. –

cevap

0

Aslında, C++ yineleyici sistemi çok büyük değil. Iterators işaretçiler benzeyen ve onlar kendi sorunlarının vardır: std::for_each(v0.begin(), v2.end(), func);

Python yaklaşımı kadar:

  • tekil değerler: v.end() güvenle indirgenmedikleri edilemez
  • inversiyon sorunları: std::for_each(end, begin, func);
  • uyumsuzluk sorunları Bu bağlamda daha iyi (ilk başta istisna kullanımı oldukça şaşırtıcı olsa da, aslında yuvalanmış yineleyicileri tanımlamaya yardımcı olur), çünkü bir Python yineleyicisi daha çok bir Range.

    Range kavramı çok daha iyi 11C++ daha menzil için döngü yapısı sunar: Bu alabilir da, bir yineleyici ile mümkündür

    for (Object& o: range) { 
    } 
    

    şey bir dizi ile de mümkündür Bunu gerçekleştirmek için bazı zamanlar ve bazı çeviriler C++ işaretçi benzeri yineleyiciler ile eğitilmiş olanlar için ilk bakışta gerçeküstücüler gibi görünüyor. Örneğin, alt aralıklar mükemmel ifade edilebilir: slicerange içinde pozisyon [2, 9) tüm öğeleri alacağını

    for (Object& o: slice(range, 2, 9)) { 
    } 
    

    . Bu nedenle, dilinize (Python) karşı savaşmak yerine, daha fazla incelemeniz ve onun tarzını benimsemeniz gerekir. Bir dile karşı mücadele genellikle bir kaybetme savaşıdır, deyimlerini öğrenir, verimli olur.

+0

Basitçe söylemek gerekirse, sadece bir Çift Yönlü Iteratöre ihtiyacım var. O kadar çok dilbilgisi şeklimiz olsun, C++ 'da aptal ve şeffaf değil, kolay gibi görünüyor. –

5

Durumların çoğunda, Python'un for ve yineleyicileri, en basit olanıdır. Bu onların amacı ve esneklikten ödün vermemeleri gereken - esneklik eksikliği bir sorun değil.

for döngüsünü kullanamadığınız bazı durumlarda, C++ yineleyicileri daha basit olabilir. Ancak, her zaman bir C++ yineleyicisi kullanmaktan daha karmaşık olmayan Python'da çok daha karmaşık bir yol vardır.


Eğer sadece bir while döngü kullanmak, döngü gelen yineleyici ilerleyen ayırmak gerekirse:

it = iter(obj) 

try: 
    while True: # or some secondary break condition other than StopIteration 
     star_it = next(it) 
     if condition_one(star_it): 
      star_it = next(it) 
except StopIteration: 
    pass # exhausted the iterator 

ben --it Python mantıklı sadece iki durumda düşünebilirsiniz.

Birincisi, bir sıra üzerinde yineleme yapıyorsunuz. Eğer geriye gitmek gerekiyorsa o durumda, hiç bir yineleyici kullanmayın - sadece while döngü ile bir sayaç kullanın: Bir iki kat bağlantılı liste üzerinde yineleme eğer

i = 0 
while i < len(sequence): # or i < len and some_other_condition 
    star_it = sequence[i] 
    if condition_one(star_it): 
     i += 1 
    if condition_two(star_it): 
     i = max(i - 1, 0) 

saniyedir . Bu durumda, yine, bir yineleyici kullanmayın - sadece normal düğümleri hareket:

current = node 
while current: # or any break condition 
    if condition_one(current): 
     current = current.next 
    if condition_two(star_it): 
     current = current.prev 

Eğer mantıklı düşünebilir, ancak yukarıdaki yöntemlerden birini kullanamazsınız bir durum , set veya dict gibi sırasız bir koleksiyona sahiptir. Ancak, --it, bu durumda anlamlıdır. Koleksiyon sırasız olduğundan, semantik olarak, daha önce ulaşılan öğelerin herhangi biri uygun olacaktır - sadece önceki önceki öğe değil.

Yani, ya mydict.values() veya tuple(myset) gibi bir dizi üzerinde yineleme ve bir sayacı kullanarak geri bellek ihtiyacı, gitmek için doğru nesneyi bilmek için, ya da önceki değerlerinin bir dizisini monte ederek gidip kullanılarak olarak for döngü yerine bir while döngü ve next döngü.

+1

@XinlinCao Ancak, bu durumlara göre uyarlanmış olan çözümlerin genel kullanıma göre daha kolay olması, özellikle de genel durumun diğer durumlar için daha karmaşık hale getirilmesi gerektiğinde. Python'un ayrıştırma araçları vardır. Dosyalara göre, bir yineleyicinin 'aramayı' çağırmaktan daha iyi olduğunu görmüyorum? – agf

+2

@XinlinCao: Kullanım durumlarınız çok belirsiz. Somut bir gerçek dünya kullanım vakası verin ve size Python'da iyi bir çözüm gösterebiliriz. –

+0

@XinlinCao Söylemeye çalışıyoruz __that yanlış soru__. Python'da, C++'da iki yönlü bir yineleyici ile yapacağınız şeyi yapmak için daha iyi yollar vardır. Çözümünüz için – agf

0

Sen piton nesneleri kullanarak C benzer bir şekilde ++ uygulamak olabilir:

class Iterable(object): 
    class Iterator(object): 
    def __init__(self, father, pos=0): 
     self.father = father 
     self.pos = pos 

    def __getitem__(self, pos=0): 
     return self.father[self.pos + pos] 

    def __setitem__(self, pos, value): 
     self.father[self.pos + pos] = value 

    def __iadd__(self, increment): 
     self.pos += increment 
     return self 

    def __isub__(self, decrement): 
     self.pos -= decrement 
     return self 

    def __ne__(self, other): 
     return self.father != other.father or self.pos != other.pos 

    def __eq__(self, other): 
     return not (self != other) 

    def begin(self): 
    return self.Iterator(self) 

    def end(self): 
    return self.Iterator(self, len(self)) 

class Vector(list, Iterable): 
    pass 

v = Vector([54, 43, 32, 21]) 

counter = 0 
it = v.begin() 
print it, it[0] 
while it != v.end(): 
    counter += 1 
    print it[0] 
    if counter == 2: 
    it += 1; # suppose this iterator supports ++ 
    if counter == 1: 
    it -= 1; # suppose this iterator supports -- 
    it += 1 

Bu it[0] tarafından *itit += 1 tarafından ve it++ (aynı zamanda C++ analog) yerini alır, ancak yürürlükte hemen hemen aynı kalır. Eğer bunu yaparsanız bir kaç durumlar için ;-)

1

Çözümler Bahsettiğiniz olsa

Sen Pythonic yollarını bırakın:

  1. Alttaki kapta nesneleri değiştirmek istiyor. sözlüklerde için, anahtar veya öğelerin üzerinde sadece değerleri yineleme: listeleri için

    for key, value in my_dict.iteritems(): 
        if conditiion(value): 
         my_dict[key] = new_value 
    

    enumerate() kullanın:

    for index, item in enumerate(my_list): 
        if condition(item): 
         my_list[index] = new_item 
    
  2. tek bir "look yeşil ışık" değerine sahip bir yineleyici istiyorum. Muhtemelen belirli duruma uyarlanmış bir şey kullanmak istiyorsunuz, ama burada genel durumlar için bir reçete:

    def iter_with look_ahead(iterable, sentinel=None): 
        iterable, it_ahead = itertools.tee(iterable) 
        next(it_ahead, None) 
        return izip_longest(iterable, it_ahead, fillvalue=sentinel) 
    
    for current, look_ahead in iter_with look_ahead(tokens): 
        # whatever 
    
  3. Sen tersten yineleme istiyorum. Bunu destekleyen kaplar için reversed() kullanın.

  4. Rastgele erişime sahip olmak istiyorsunuz. Sadece bir listesi ve kullanım endeksleri içine iterable açmak Python'da liste nesnesi bir dizidir, bu nedenle söz konusu belirtilen verimlilik kaygısı aslında olmayan bir konu olduğunu

    my_list = list(my_iterable) 
    
0

Not.

+1

Bu, çözüm yerine yorum bölümünde bulunmalıdır. – DaveyLaser