Làm thế nào để điều hướng qua một vectơ bằng cách sử dụng trình vòng lặp? (C ++)


105

Mục đích là truy cập phần tử "thứ n" của một vectơ chuỗi thay vì toán tử [] hoặc phương thức "at". Theo những gì tôi hiểu, trình vòng lặp có thể được sử dụng để điều hướng qua các vùng chứa, nhưng tôi chưa bao giờ sử dụng trình vòng lặp trước đây và những gì tôi đang đọc thật khó hiểu.

Nếu ai đó có thể cho tôi một số thông tin về cách đạt được điều này, tôi sẽ đánh giá cao nó. Cảm ơn bạn.


Không phải vectơ dành riêng cho STL của C ++? Tôi sẽ chỉnh sửa nó bất kể
kevin

kevin: vector là một thuật ngữ chung có thể được sử dụng bởi bất kỳ ngôn ngữ nào, đặc biệt là những ngôn ngữ liên quan đến toán học như Mathematica hoặc Matlab.
Gabe

@michael, vâng haha ​​Tôi đã chỉnh sửa nó sau nhận xét của gabe.
kevin

Câu trả lời:


112

Bạn cần sử dụng phương thức beginendphương thức của vectorlớp, lớp này trả về trình vòng lặp tham chiếu đến phần tử đầu tiên và phần tử cuối cùng tương ứng.

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
Điều này bỏ lỡ thực tế là std::vectorcó các trình lặp truy cập ngẫu nhiên.
sbi

24
Bất kể bạn có biết kiểu trình vòng lặp là truy cập ngẫu nhiên hay không, cách "tốt nhất" để di chuyển trình vòng lặp về phía trước n khoảng trắng không phải là viết vòng lặp của riêng bạn mà là gọi std::advance(it, n). Nó được định nghĩa để thực hiện chính xác những gì bạn muốn và nó sẽ tự động sử dụng it + nnếu trình vòng lặp được gắn thẻ là truy cập ngẫu nhiên hoặc thực hiện vòng lặp nếu cần.
Steve Jessop

61

Thông thường, các trình vòng lặp được sử dụng để truy cập các phần tử của một vùng chứa theo kiểu tuyến tính; tuy nhiên, với "vòng lặp truy cập ngẫu nhiên", có thể truy cập bất kỳ phần tử nào theo cùng một kiểu như operator[].

Để truy cập các phần tử tùy ý trong một vectơ vec , bạn có thể sử dụng như sau:

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

Sau đây là ví dụ về mẫu truy cập điển hình (các phiên bản C ++ trước đây):

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

Lợi thế của việc sử dụng trình lặp là bạn có thể áp dụng cùng một mẫu với các vùng chứa khác :

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

Vì lý do này, thực sự dễ dàng tạo mã mẫu hoạt động giống nhau bất kể loại vùng chứa là gì . Một ưu điểm khác của trình vòng lặp là nó không cho rằng dữ liệu nằm trong bộ nhớ; ví dụ: người ta có thể tạo một trình lặp chuyển tiếp có thể đọc dữ liệu từ luồng đầu vào hoặc đơn giản là tạo dữ liệu một cách nhanh chóng (ví dụ: một bộ tạo phạm vi hoặc số ngẫu nhiên).

Một tùy chọn khác bằng cách sử dụng std::for_eachvà lambdas:

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

Vì C ++ 11, bạn có thể sử dụng autođể tránh chỉ định tên kiểu rất dài, phức tạp của trình lặp như đã thấy trước đây (hoặc thậm chí phức tạp hơn):

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

Ngoài ra, có một biến thể đơn giản hơn cho mỗi biến thể:

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

Và cuối cùng cũng có std::accumulatenơi bạn phải cẩn thận xem bạn đang thêm số nguyên hay dấu phẩy động.


53

Trong C ++ - 11, bạn có thể làm:

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

Xem tại đây để biết các biến thể: https://en.cppreference.com/w/cpp/language/range-for


4
TẠI SAO ĐIỀU NÀY KHÔNG CÓ LIKES ?! <3
jperl 12/07/18

3
@jperl Đã đăng muộn 8 năm. Sẽ mất 8 năm tiếp theo để có được đủ upvotes :)
lashgar

@jperl, câu trả lời là lạc đề. Mặc dù tính năng vòng lặp này rất hay, nhưng nó không giúp bạn biết khi nào bạn đang ở phần tử thứ n, đây là câu hỏi của OP. Ngoài ra, bất kỳ câu trả lời nào yêu cầu độ phức tạp thời gian O (n), chẳng hạn như câu trả lời này, là rất tệ. Truy cập phần tử thứ n của vectơ phải luôn là O (1).
Elliott

@lashgar Tôi đã thử điều này với mảng nhưng không thành công. Nó có hoạt động cho mảng không?
thời đại

@ eras'q, đã thử với gcc 7.5.0trên Ubuntu 18.04 và hoạt động với mảng theo cách tương tự.
lashgar

17

Các trình vòng lặp của Vector là các trình vòng lặp truy cập ngẫu nhiên có nghĩa là chúng trông giống như các con trỏ đơn giản.

Bạn có thể truy cập phần tử thứ n bằng cách thêm n vào trình vòng lặp được trả về từ begin()phương thức của vùng chứa hoặc bạn có thể sử dụng toán tử [].

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

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

Ngoài ra, bạn có thể sử dụng hàm nâng cao hoạt động với tất cả các loại trình vòng lặp. (Bạn sẽ phải xem xét liệu bạn có thực sự muốn thực hiện "truy cập ngẫu nhiên" với các trình vòng lặp truy cập không ngẫu nhiên hay không, vì đó có thể là một việc tốn kém.)

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

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

1
Bạn cũng có thể sử dụng advancecho các trình vòng lặp truy cập ngẫu nhiên hoặc các trình vòng lặp không xác định, vì nó được đảm bảo hoạt động trong thời gian không đổi trong trường hợp đó. Đây là lý do tại sao các trình vòng lặp do người dùng xác định phải được gắn thẻ chính xác.
Steve Jessop

Thật vậy, nhưng advancethực sự khó chịu khi sử dụng (vì sử dụng tham số out) nếu bạn biết mình đang xử lý các trình lặp truy cập ngẫu nhiên. Sẽ chỉ đề xuất trong mã chung và nếu không được sử dụng nhiều (nếu thuật toán không hỗ trợ tốt các trình lặp truy cập không ngẫu nhiên, thì có thể như vậy - ví dụ: std::sort có thể sắp xếp một std::listnhưng không vì nó sẽ không hiệu quả một cách lố bịch ).
UncleBens

Chắc chắn, ví dụ cổ điển sẽ là nếu thuật toán của bạn chỉ thực sự cần InputIterator, nhưng vì bất kỳ lý do gì nó đôi khi bỏ qua, vì vậy bạn muốn nó hiệu quả hơn nếu trình vòng lặp có quyền truy cập ngẫu nhiên. Không có giá trị hạn chế thuật toán của bạn chỉ truy cập ngẫu nhiên bằng cách sử dụng operator+. Nhưng câu hỏi đã rõ ràng về vector, vì vậy không có gì sai với phần đầu tiên của câu trả lời của bạn. Tôi chỉ nghĩ rằng phần thứ hai có thể ngụ ý "bạn không thể sử dụng trước với các trình lặp truy cập ngẫu nhiên, ngay cả khi bạn muốn" cho một người chưa từng thấy advancetrước đây.
Steve Jessop

OK, đã nhập lại bit đó và đưa ra ví dụ với một vector.
UncleBens

Dòng thứ hai, Vectornên chữ thường
Lei Yang

0

Dưới đây là một ví dụ về việc tiếp cận các ithchỉ số của một std::vectorsử dụng một std::iteratortrong một vòng lặp mà không yêu cầu incrementing hai vòng lặp.

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());
    }
}

Không có vòng lặp for

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

và sử dụng atphương pháp:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.