จะนำทางผ่านเวกเตอร์โดยใช้ตัววนซ้ำได้อย่างไร (C ++)


105

เป้าหมายคือการเข้าถึงองค์ประกอบ "ที่ n" ของเวกเตอร์ของสตริงแทนตัวดำเนินการ [] หรือเมธอด "at" จากสิ่งที่ฉันเข้าใจตัวทำซ้ำสามารถใช้เพื่อนำทางผ่านคอนเทนเนอร์ได้ แต่ฉันไม่เคยใช้ตัวทำซ้ำมาก่อนและสิ่งที่ฉันกำลังอ่านอยู่ก็ทำให้สับสน

หากใครสามารถให้ข้อมูลเกี่ยวกับวิธีบรรลุเป้าหมายนี้ได้ฉันจะขอบคุณ ขอบคุณ.


เวกเตอร์ไม่เป็นเอกสิทธิ์เฉพาะของ STL ของ C ++ ใช่หรือไม่ ฉันจะแก้ไขโดยไม่คำนึงถึง
kevin

kevin: vector เป็นคำทั่วไปที่สามารถใช้ได้กับทุกภาษาโดยเฉพาะคำที่เกี่ยวข้องกับคณิตศาสตร์เช่น Mathematica หรือ Matlab
Gabe

@michael ใช่ฮ่าฮ่าฉันแก้ไขมันหลังจากความคิดเห็นของ gabe
kevin

คำตอบ:


112

คุณต้องใช้ประโยชน์จากbeginและendวิธีการของ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
สิ่งนี้คิดถึงข้อเท็จจริงที่std::vectorมีตัววนซ้ำการเข้าถึงโดยสุ่ม
sbi

24
ไม่ว่าคุณจะทราบชนิด iterator คือ random-access หรือไม่ "ดีที่สุด" วิธีที่จะย้ายไปข้างหน้า iterator n ช่องว่างไม่ได้ที่จะเขียนห่วงของคุณเอง std::advance(it, n)แต่การเรียกร้อง มันถูกกำหนดให้ทำในสิ่งที่คุณต้องการและจะใช้โดยอัตโนมัติit + nหากตัววนซ้ำถูกแท็กเป็นการเข้าถึงแบบสุ่มหรือทำวนซ้ำหากจำเป็น
Steve Jessop

61

โดยทั่วไปแล้วตัววนซ้ำจะใช้เพื่อเข้าถึงองค์ประกอบของคอนเทนเนอร์ในรูปแบบเชิงเส้น แต่การที่มี "iterators เข้าถึงโดยสุ่ม" ก็เป็นไปได้ในการเข้าถึงองค์ประกอบใด ๆ operator[]ในแบบเดียวกับ

ในการเข้าถึงองค์ประกอบตามอำเภอใจในเวกเตอร์ vecคุณสามารถใช้สิ่งต่อไปนี้:

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

ต่อไปนี้เป็นตัวอย่างของรูปแบบการเข้าถึงทั่วไป (C ++ เวอร์ชันก่อนหน้า):

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

ข้อดีของการใช้ iterator คือคุณสามารถใช้รูปแบบเดียวกันกับคอนเทนเนอร์อื่น ๆ :

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

ด้วยเหตุนี้จึงเป็นเรื่องง่ายมากที่จะสร้างแม่แบบรหัสที่จะทำงานเดียวกันคำนึงถึงประเภทของภาชนะ ข้อดีอีกอย่างของการวนซ้ำคือไม่ถือว่าข้อมูลนั้นอยู่ในหน่วยความจำ ตัวอย่างเช่นเราสามารถสร้างตัววนไปข้างหน้าซึ่งสามารถอ่านข้อมูลจากอินพุตสตรีมหรือสร้างข้อมูลได้ทันที (เช่นช่วงหรือตัวสร้างตัวเลขสุ่ม)

อีกทางเลือกหนึ่งที่ใช้std::for_eachและ lambdas:

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

เนื่องจาก C ++ 11 คุณสามารถใช้autoเพื่อหลีกเลี่ยงการระบุชื่อประเภทที่ยาวและซับซ้อนของตัววนซ้ำอย่างที่เคยเห็นมาก่อน (หรือซับซ้อนกว่านั้น):

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

และนอกจากนี้ยังมีตัวแปรที่ง่ายกว่าสำหรับแต่ละตัวแปร:

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

และสุดท้ายยังมีstd::accumulateจุดที่คุณต้องระวังไม่ว่าคุณจะเพิ่มจำนวนเต็มหรือตัวเลขทศนิยม


54

ใน C ++ - 11 คุณสามารถทำได้:

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';

ดูรูปแบบต่างๆได้ที่นี่: https://en.cppreference.com/w/cpp/language/range-for


4
ทำไมสิ่งนี้ถึงไม่ชอบ?! <3
jperl

3
@jperl โพสต์ช้าไป 8 ปี จะใช้เวลา 8 ปีข้างหน้าเพื่อให้ได้รับ
คะแนนโหวต

@jperl คำตอบคือนอกประเด็น แม้ว่าฟีเจอร์ลูปนี้จะดี แต่ก็ไม่ได้ช่วยให้คุณรู้ว่าเมื่อไหร่ที่คุณอยู่ในองค์ประกอบที่ n ซึ่งเป็นคำถามของ OP นอกจากนี้คำตอบใด ๆ ที่ต้องใช้ความซับซ้อนของเวลา O (n) เช่นคำตอบนี้แย่มาก การเข้าถึงองค์ประกอบที่ n ของเวกเตอร์ควรเป็น O (1) เสมอ
Elliott

@lashgar ฉันลองใช้อาร์เรย์แล้ว แต่ล้มเหลว มันใช้ได้กับอาร์เรย์หรือไม่?
era s'q

@ eras'q ลองกับ gcc 7.5.0บน Ubuntu 18.04 และทำงานกับอาร์เรย์ในลักษณะเดียวกัน
lashgar

17

ตัววนซ้ำของเวกเตอร์เป็นตัววนซ้ำการเข้าถึงโดยสุ่มซึ่งหมายความว่ามันมีลักษณะและความรู้สึกเหมือนพอยน์เตอร์ธรรมดา

คุณสามารถเข้าถึงองค์ประกอบที่ n โดยการเพิ่ม n เพื่อ iterator กลับมาจากตู้คอนเทนเนอร์ของวิธีการหรือคุณสามารถใช้ประกอบการbegin()[]

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

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

หรือคุณสามารถใช้ฟังก์ชั่นล่วงหน้าซึ่งทำงานร่วมกับตัวทำซ้ำทุกประเภท (คุณต้องพิจารณาว่าคุณต้องการดำเนินการ "เข้าถึงโดยสุ่ม" ด้วยตัววนซ้ำที่ไม่ใช่การเข้าถึงโดยสุ่มจริง ๆ หรือไม่เนื่องจากอาจเป็นเรื่องที่ต้องทำราคาแพง)

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

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

1
คุณสามารถใช้advanceสำหรับตัววนซ้ำการเข้าถึงโดยสุ่มได้เช่นกันหรือตัววนซ้ำของหมวดหมู่ที่ไม่รู้จักเนื่องจากมีการรับประกันว่าจะทำงานในเวลาคงที่ในกรณีนั้น นี่คือเหตุผลที่ตัววนซ้ำที่ผู้ใช้กำหนดเองควรได้รับการติดแท็กอย่างถูกต้อง
Steve Jessop

อันที่จริง แต่advanceเป็นเรื่องที่น่ารำคาญในการใช้ (เนื่องจากการใช้พารามิเตอร์ out) หากคุณรู้ว่าคุณกำลังจัดการกับตัววนซ้ำการเข้าถึงโดยสุ่ม จะแนะนำเฉพาะในรหัสทั่วไปและหากไม่ได้ใช้มาก (หากอัลกอริทึมไม่รองรับตัววนซ้ำที่ไม่ใช่การเข้าถึงโดยสุ่มเป็นอย่างดีเช่นนั้นเช่นstd::sort สามารถเรียงลำดับ a std::listแต่ไม่ได้เพราะมันจะไม่มีประสิทธิภาพอย่างน่าขัน ).
UncleBens

แน่นอนว่าตัวอย่างคลาสสิกจะเป็นถ้าอัลกอริทึมของคุณเท่านั้นที่จริงต้องการ InputIterator แต่ด้วยเหตุผลใดก็ตามบางครั้งมันก็ข้ามไปข้างหน้าเพื่อให้คุณอยากให้มันมีประสิทธิภาพมากขึ้นถ้า iterator จะมีการเข้าถึงแบบสุ่ม ไม่คุ้มที่จะ จำกัด อัลกอริทึมของคุณให้เข้าถึงโดยสุ่มโดยใช้operator+เท่านั้น แต่คำถามนั้นชัดเจนเกี่ยวกับเวกเตอร์ดังนั้นส่วนแรกของคำตอบของคุณไม่มีอะไรผิดปกติ ฉันแค่คิดว่าส่วนที่สองอาจบอกเป็นนัยว่า "คุณไม่สามารถใช้ขั้นสูงกับตัววนซ้ำการเข้าถึงโดยสุ่มแม้ว่าคุณต้องการ" ให้กับคนที่ไม่เคยเห็นadvanceมาก่อน
Steve Jessop

ตกลง reworded บิตนั้นและยกตัวอย่างด้วยเวกเตอร์
UncleBens

บรรทัดที่สองVectorควรเป็นตัวพิมพ์เล็ก
Lei Yang

0

นี่คือตัวอย่างของการเข้าถึงithดัชนีstd::vectorโดยใช้std::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());
    }
}

โดยไม่ต้อง for-loop

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

และใช้atวิธีการ:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.