Yineleyicileri kullanarak bir vektörde nasıl gezinilir? (C ++)


105

Amaç, [] operatörü veya "at" yöntemi yerine bir dizge vektörünün "n'inci" öğesine erişmektir. Anladığım kadarıyla yineleyiciler konteynerler arasında gezinmek için kullanılabilir, ancak daha önce hiç yineleyici kullanmadım ve okuduğum şey kafa karıştırıcı.

Biri bana bunu nasıl başaracağıma dair biraz bilgi verebilirse, minnettar olurum. Teşekkür ederim.


Vektörler C ++ STL'sine özel değil mi? Ne olursa olsun düzenleyeceğim
kevin

kevin: vektör, herhangi bir dil tarafından, özellikle Mathematica veya Matlab gibi matematikle ilgili olanlar tarafından kullanılabilen genel bir terimdir.
Gabe

@michael, evet haha ​​gabe'nin yorumundan sonra düzenledim.
kevin

Yanıtlar:


112

Sırasıyla birinci ve son öğeye atıfta bulunan yineleyiciyi döndüren sınıfın beginve endyöntemini kullanmanız gerekir vector.

using namespace std;  

vector<string> myvector;  // a vector of stings.


// push some strings in the vector.
myvector.push_back("a");
myvector.push_back("b");
myvector.push_back("c");
myvector.push_back("d");


vector<string>::iterator it;  // declare an iterator to a vector of strings
int n = 3;  // nth element to be found.
int i = 0;  // counter.

// now start at from the beginning
// and keep iterating over the element till you find
// nth element...or reach the end of vector.
for(it = myvector.begin(); it != myvector.end(); it++,i++ )    {
    // found nth element..print and break.
    if(i == n) {
        cout<< *it << endl;  // prints d.
        break;
    }
}

// other easier ways of doing the same.
// using operator[]
cout<<myvector[n]<<endl;  // prints d.

// using the at method
cout << myvector.at(n) << endl;  // prints d.

5
Bu std::vector, rastgele erişim yineleyicilere sahip olduğu gerçeğini gözden kaçırır .
sbi

24
Yineleyici türünün rasgele erişim olup olmadığını bilseniz de bilmeseniz de, bir yineleyiciyi n boşluk ileri taşımanın "en iyi" yolu kendi döngünüzü yazmak değil, aramaktır std::advance(it, n). Tam olarak istediğiniz şeyi yapmak için tanımlanmıştır it + nve yineleyici rastgele erişim olarak etiketlenirse veya gerekiyorsa döngüyü otomatik olarak kullanır .
Steve Jessop

61

Tipik olarak yineleyiciler, bir kabın öğelerine doğrusal biçimde erişmek için kullanılır; ancak "rastgele erişim yineleyiciler" ile herhangi bir öğeye aynı şekilde erişmek mümkündür operator[].

To bir vektör erişim keyfi elemanları vec , aşağıdakileri kullanabilirsiniz:

vec.begin()                  // 1st
vec.begin()+1                // 2nd
// ...
vec.begin()+(i-1)            // ith
// ...
vec.begin()+(vec.size()-1)   // last

Aşağıda, tipik bir erişim modelinin bir örneği verilmiştir (C ++ 'ın önceki sürümleri):

int sum = 0;
using Iter = std::vector<int>::const_iterator;
for (Iter it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

Yineleyici kullanmanın avantajı, aynı kalıbı diğer kaplarla uygulayabilmenizdir :

sum = 0;
for (Iter it = lst.begin(); it!=lst.end(); ++it) {
    sum += *it;
}

Bu nedenle, konteyner türü ne olursa olsun aynı şekilde çalışacak şablon kodu oluşturmak gerçekten çok kolay . Yineleyicilerin diğer bir avantajı, verilerin bellekte yerleşik olduğunu varsaymamasıdır; örneğin, bir girdi akışından veri okuyabilen veya anında veri üreten bir ileri yineleyici yaratılabilir (örneğin, bir aralık veya rasgele sayı üreteci).

std::for_eachLambdas kullanan başka bir seçenek :

sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int i) { sum += i; });

C ++ 11'den autoberi, daha önce görüldüğü gibi (veya daha karmaşık) yineleyicinin çok uzun, karmaşık bir tür adını belirtmekten kaçınmak için kullanabilirsiniz :

sum = 0;
for (auto it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

Ayrıca, her varyant için daha basit bir yöntem vardır:

sum = 0;
for (auto value : vec) {
    sum += value;
}

Ve son olarak, std::accumulatetamsayı veya kayan nokta sayıları eklerken dikkatli olmanız gereken yerler de vardır .


53

C ++ - 11'de şunları yapabilirsiniz:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto i : v)
{
   // access by value, the type of i is int
   std::cout << i << ' ';
}
std::cout << '\n';

Varyasyonlar için buraya bakın: https://en.cppreference.com/w/cpp/language/range-for


4
NEDEN SIFIR BEĞENİ VAR ?! <3
jperl

3
@jperl 8 yıl geç gönderildi. Yeterli oy almak için önümüzdeki 8 yılı alacak :)
lashgar

@jperl, yanıt konu dışı. Bu döngü özelliği güzel olsa da, OP'nin sorusu olan n'inci öğede olduğunuzu bilmenize yardımcı olmaz. Ayrıca, bunun gibi O (n) zaman karmaşıklığı gerektiren herhangi bir yanıt çok kötüdür. Bir vektörün n'inci elemanına erişim her zaman O (1) olmalıdır.
Elliott

@lashgar Bunu dizi ile denedim ama başarısız oldum. Dizi için çalışıyor mu?
dönem s'q

@ eras'q, 7.5.0Ubuntu 18.04'te gcc ile denedi ve dizi için aynı şekilde çalışıyor.
lashgar

17

Vector'un yineleyicileri rastgele erişimli yineleyicilerdir, bu da düz işaretçiler gibi göründükleri ve hissettikleri anlamına gelir.

Kapsayıcının begin()yönteminden döndürülen yineleyiciye n ekleyerek n'inci öğeye erişebilir veya operatörü kullanabilirsiniz [].

std::vector<int> vec(10);
std::vector<int>::iterator it = vec.begin();

int sixth = *(it + 5);
int third = *(2 + it);
int second = it[1];

Alternatif olarak , her tür yineleyici ile çalışan gelişmiş işlevini kullanabilirsiniz . (Rastgele erişimli olmayan yineleyicilerle gerçekten "rasgele erişim" gerçekleştirmek isteyip istemediğinizi düşünmeniz gerekir, çünkü bu yapılması pahalı bir şey olabilir.)

std::vector<int> vec(10);
std::vector<int>::iterator it = vec.begin();

std::advance(it, 5);
int sixth = *it;

1
Bu advancedurumda sabit zamanda çalışması garantili olduğundan, rastgele erişimli yineleyiciler veya bilinmeyen kategorideki yineleyiciler için de kullanabilirsiniz . Bu nedenle, kullanıcı tanımlı yineleyiciler doğru şekilde etiketlenmelidir.
Steve Jessop

Gerçekten, ancak advancerastgele erişim yineleyicileriyle uğraştığınızı biliyorsanız kullanmak gerçekten can sıkıcıdır (out parametresi kullanımından dolayı). Sadece genel kodda tavsiye eder ve (çok kullanılmadığı takdirde algoritma iyi olmayan rasgele erişimli yineleyicinızı desteklemiyorsa, bu nedenle olmak - örneğin, std::sort olabilir bir sıralamak std::listama buna gülünç verimsiz olurdu değil çünkü ).
UncleBens

Elbette, klasik örnek, algoritmanızın yalnızca bir InputIterator'a ihtiyaç duymasıdır, ancak herhangi bir nedenle bazen ileri atlar, bu nedenle yineleyicinin rastgele erişime sahip olması durumunda daha verimli olmasını istersiniz. Algoritmanızı yalnızca kullanarak rastgele erişimle sınırlandırmaya değmez operator+. Ancak soru açıkça vektörle ilgiliydi, bu nedenle cevabınızın ilk bölümünde yanlış bir şey yok. Daha advanceönce hiç görmemiş biri için ikinci bölümün, "isteseniz bile, rastgele erişim yineleyicileriyle ilerlemeyi kullanamazsınız" anlamına gelebileceğini düşündüm .
Steve Jessop

Tamam, o kısmı değiştirdim ve bir vektörle örnek verdim.
UncleBens

ikinci satır, Vectorküçük harf olmalıdır
Lei Yang

0

İki yineleyicinin artırılmasını gerektirmeyen bir döngü içinde bir kullanarak a'nın ithdizinine erişmenin bir örneği aşağıda verilmiştir .std::vectorstd::iterator

std::vector<std::string> strs = {"sigma" "alpha", "beta", "rho", "nova"};
int nth = 2;
std::vector<std::string>::iterator it;
for(it = strs.begin(); it != strs.end(); it++) {
    int ith = it - strs.begin();
    if(ith == nth) {
        printf("Iterator within  a for-loop: strs[%d] = %s\n", ith, (*it).c_str());
    }
}

Döngü olmadan

it = strs.begin() + nth;
printf("Iterator without a for-loop: strs[%d] = %s\n", nth, (*it).c_str());

ve atyöntemi kullanma :

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
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.