Bir std :: vector yineleyicisinin dizinini almanın en etkili yolu nedir?


439

Ben bir vektör üzerinde yineleme ve yineleyici şu anda işaret dizin gerekir. AFAIK bu iki şekilde yapılabilir:

  • it - vec.begin()
  • std::distance(vec.begin(), it)

Bu yöntemlerin artıları ve eksileri nelerdir?

Yanıtlar:


558

Ben tercih ediyorum it - vec.begin()Naveen'in verdiği zıt bir nedenle kesin: bu kadar olmaz bir liste haline vektör değiştirirseniz derlemek. Bunu her yineleme sırasında yaparsanız, bir O (n) algoritmasını O (n ^ 2) algoritmasına çevirebilirsiniz.

Başka bir seçenek, yineleme sırasında kapsayıcıda atlamazsanız, dizini ikinci bir döngü sayacı olarak tutmak olacaktır.

Not: itbir kapsayıcı yineleyicinin ortak adıdır std::container_type::iterator it;.


3
Kabul. Eksi işaretinin en iyisi olduğunu söyleyebilirim, ancak std :: distance kullanmaktan ziyade ikinci bir döngü sayacı tutmak daha iyi olurdu, çünkü bu işlev yavaş olabilir.
Steven Sudit

28
bu da ne böyle it?
Steinfeld

32
@Steinfeld bir yineleyici. std::container_type::iterator it;
Matt Munson

2
İkinci bir döngü sayacı eklemek o kadar bariz bir çözümdür ki utanıyorum ki hiç düşünmedim.
Mordred

3
@Swapnil std::list, öğelere konumlarına göre doğrudan erişim sunmadığından, yapamazsanız list[5], yapamamanız gerekir list.begin() + 5.
José Tomás Tocino

135

std::distance(vec.begin(), it)Herhangi bir kod değişikliği olmadan kap değiştirmek için izin verecek gibi tercih ederim . Örneğin, rasgele erişim yineleyicisi sağlama std::listyerine kullanmaya karar verirseniz std::vector, kodunuz yine de derlenir. Std :: distance, yineleyici özelliklerine bağlı olarak en uygun yöntemi aldığından, performansta herhangi bir düşüş olmaz.


50
Eğer rasgele erişim yineleyiciler olmadan bir kap kullanırken, en iyisi değil de verimsiz olduğu için bu tür mesafeleri hesaplamak
Eli Bendersky

6
@Eli: Buna katılıyorum, ama çok özel bir durumda gerçekten gerekliyse, yine de bu kod işe yarayacak.
Naveen

9
Kap değiştiğinde kodun yine de değiştirilmesi gerektiğini düşünüyorum - bir std :: list değişkeni adlı veckötü bir haber var. Kod genel olarak yeniden yazıldıysa, kapsayıcı türünü şablon parametresi olarak alarak, o zaman rasgele erişimli yineleyicilerin ele alınması hakkında konuşabiliriz (ve yapmalıyız) ;-)
Steve Jessop

1
Ve bazı konteynerler için uzmanlık.
ScaryAardvark

19
@SteveJessop: Adında bir vektörün olması da veckötü bir haber.
Nehir Tam

74

UncleBens ve Naveen'in de gösterdiği gibi, her ikisi için de iyi nedenler var. Hangisi "daha iyi" ne istediğinize bağlıdır: Sabit zamanlı davranışı garanti etmek istiyor musunuz, yoksa gerektiğinde doğrusal zamana geri düşmesini istiyor musunuz?

it - vec.begin()sabit zaman alır, ancak operator -yalnızca rastgele erişim yineleyicilerde tanımlanır, bu nedenle kod örneğin liste yineleyicilerle hiç derlenmez.

std::distance(vec.begin(), it) tüm yineleyici türleri için çalışır, ancak yalnızca rasgele erişim yineleyicilerde kullanılırsa sabit zamanlı bir işlem olur.

Hiçbiri "daha iyi" değildir. İhtiyacınız olanı kullanın.


1
Geçmişte bunun için faul düştüm. İki std :: map yineleyicisinde std :: distance kullanarak ve O (N) olmasını beklemek.
ScaryAardvark

6
@ScaryAardvark: O (1) olmasını beklemiyor musunuz?
jalf

12

Bunu beğendim: it - vec.begin()çünkü bana açıkça "başlangıçtan itibaren mesafe" diyor. Yineleyiciler ile aritmetik olarak düşünmeye alışkınız, bu yüzden -burdaki burç en açık göstergedir.


19
Kelimenin tam anlamıyla, kelimeyi kullanmaktan uzaklığı bulmak için çıkarma yapmak daha açık distancemı?
Travis Gockel

4
@Travis, benim için öyle. Bu bir zevk ve gelenek meselesi. Biz it++böyle bir şey söylemiyoruz std::increment(it), öyle değil mi? Bu daha az net sayılmaz mı?
Eli Bendersky

3
++Operatör biz yineleyici artırmak olarak nasıl STL dizilerinin bir parçası olarak tanımlanır. std::distanceilk ve son eleman arasındaki eleman sayısını hesaplar. Aslında -operatör çalışır sadece bir tesadüftür.
Travis Gockel

3
@MSalters: ve yine de ++ :-) kullanıyoruz
Eli Bendersky

10

Algoritmanızı bir std::vector::iteratorve std::vector::iteratorsadece kullanmakla kısıtlanmış / sabit kodlanmışsanız, hangi yöntemi kullanacağınız önemli değildir. Algoritmanız, diğerinden birini seçmenin herhangi bir fark yaratabileceği noktanın ötesinde somutlaştırılmıştır. İkisi de aynı şeyi yapıyor. Bu sadece kişisel bir tercih meselesidir. Şahsen açık bir çıkarma kullanırdım.

Öte yandan, gelecekte bir günün başka bir yineleyici türüne uygulanabilmesine izin vermek için algoritmanızda daha yüksek bir genelliği korumak istiyorsanız, o zaman en iyi yöntem niyetinize bağlıdır . Burada kullanılabilecek yineleyici türüyle ilgili olarak ne kadar kısıtlayıcı olmak istediğinize bağlıdır.

  • Açık çıkarma kullanırsanız, algoritmanız oldukça dar bir yineleyici sınıfıyla sınırlandırılır: rasgele erişimli yineleyiciler. (Bu şimdi aldığınız şeydir std::vector)

  • Kullanırsanız distance, algoritmanız çok daha geniş bir yineleyici sınıfını destekler: giriş yineleyicileri.

Tabii ki, distancerasgele erişimli olmayan yineleyiciler için hesaplama genel olarak verimsiz bir işlemdir (yine, rasgele erişimli olanlar için çıkarma kadar etkilidir). Algoritmanın rasgele erişimli yineleyiciler için verimlilik açısından anlamlı olup olmadığına karar vermek size kalmıştır . Ortaya çıkan verimlilik kaybı, algoritmanızı tamamen işe yaramaz hale getirme noktasına yıkıcıdır, o zaman çıkarmaya daha iyi yapışmanız, böylece verimsiz kullanımları yasaklamanız ve kullanıcıyı diğer yineleyici türleri için alternatif çözümler aramaya zorlamanız gerekir. Rasgele erişimli yineleyicilerle verimlilik hala kullanılabilir aralıktaysa, distancealgoritmanın rasgele erişimli yineleyicilerle daha iyi çalıştığı gerçeğini kullanmalı ve belgelemelisiniz.



3

-Varyantı std::vectorsadece için kullanırım - ne anlama geldiği oldukça açıktır ve operasyonun basitliği (bir işaretçi çıkarma işleminden daha fazla değildir) sözdizimi ile ifade edilir ( distancediğer tarafta, pisagor gibi sesler) ilk okuma, değil mi?). UncleBen'in belirttiği -gibi vector, kazayla değiştirildiği takdirde statik bir iddia olarak da hareket eder list.

Ayrıca bence çok daha yaygın - bunu kanıtlayacak sayı yok. Ana argüman: it - vec.begin()kaynak kodunda daha kısadır - daha az yazma işi, daha az yer kaplama. Sorunuza verilen doğru yanıtın bir zevk meselesi olduğu anlaşılmaktadır, bu da geçerli bir argüman olabilir.


0

İşte indeks ile birlikte 10'un "all" oluşumlarını bulmak için bir örnek. Bunun biraz yardımcı olacağını düşündüm.

void _find_all_test()
{
    vector<int> ints;
    int val;
    while(cin >> val) ints.push_back(val);

    vector<int>::iterator it;
    it = ints.begin();
    int count = ints.size();
    do
    {
        it = find(it,ints.end(), 10);//assuming 10 as search element
        cout << *it << " found at index " << count -(ints.end() - it) << endl;
    }while(++it != ints.end()); 
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.