sử dụng std :: điền để điền vectơ với số lượng ngày càng tăng


82

Tôi muốn điền vector<int>bằng cách sử dụng std::fill, nhưng thay vì một giá trị, vectơ phải chứa các số theo thứ tự tăng dần sau.

Tôi đã cố gắng đạt được điều này bằng cách lặp lại từng tham số thứ ba của hàm, nhưng điều này sẽ chỉ cung cấp cho tôi một trong hai vectơ được lấp đầy bởi 1 hoặc 2 (tùy thuộc vào vị trí của ++toán tử).

Thí dụ:

vector<int> ivec;
int i = 0;
std::fill(ivec.begin(), ivec.end(), i++); // elements are set to 1
std::fill(ivec.begin(), ivec.end(), ++i); // elements are set to 2

25
Sử dụng std::iotathay vì std::fill(giả sử trình biên dịch của bạn đủ mới để hỗ trợ nó).
Jerry Coffin,

1
Thật khó chịu, đây dường như là một phần của tiêu chuẩn mới (mà tôi không được sử dụng). Tôi thấy thư viện BOOST có chức năng như vậy, nhưng nó không chấp nhận vectơ ( boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/… ) nhưng một số loại tùy chỉnh. Không có lựa chọn nào khác?
BlackMamba,

2
user1612880, nếu bạn không thể sử dụng C ++ 11 / Boost, chỉ cần sử dụng mã của Liran. Nó không phải là một yêu cầu rằng tất cả các hoạt động phải được trên một dòng duy nhất cũng không có sự thiếu hụt trên toàn thế giới của các nhân vật có sẵn các tập tin mã nguồn C :-)
paxdiablo

Nó không phải là để thiếu mà cho hiệu suất. Tuy nhiên, nếu không có cách nào để thực hiện điều này mà không có các vụ hack tàn nhẫn, tôi sẽ sử dụng giải pháp do Liran cung cấp.
BlackMamba,

@ user1612880 Bạn đã thử với std::vector. Phiên bản tăng cường là một mẫu hàm và "tên kiểu" của đối số đầu tiên chỉ định một khái niệm. Thật khó để nói, bởi vì tôi chỉ có thể tìm thấy một thông số kỹ thuật rất hình thức, và không có mô tả đơn giản, nhưng tôi nghĩ rằng điều đó std::vectorphù hợp với khái niệm.
James Kanze,

Câu trả lời:


125

Tốt hơn là sử dụng std::iotanhư thế này:

std::vector<int> v(100) ; // vector with 100 ints.
std::iota (std::begin(v), std::end(v), 0); // Fill with 0, 1, ..., 99.

Điều đó nói rằng, nếu bạn không có bất kỳ c++11hỗ trợ nào (vẫn là một vấn đề thực sự ở nơi tôi làm việc), hãy sử dụng std::generatenhư sau:

struct IncGenerator {
    int current_;
    IncGenerator (int start) : current_(start) {}
    int operator() () { return current_++; }
};

// ...

std::vector<int> v(100) ; // vector with 100 ints.
IncGenerator g (0);
std::generate( v.begin(), v.end(), g); // Fill with the result of calling g() repeatedly.

6
cái quái gì đại diện iotacho, dù sao? (Có vẻ như ai đó đã gõ nhầm từ rõ ràng như nhau itoa.)
Luke Usherwood

9
Nó không "đứng" cho bất cứ thứ gì, nó tương đương với chữ i trong tiếng Hy Lạp. Đó là tên được sử dụng cho một hàm tương tự trong APL và ngôn ngữ mảng đã khơi dậy nhiều ý tưởng trong STL của Stepanov.
BoBTFish

1
Ah-ha, cảm ơn! OK vì vậy nó là một từ hơn là một chủ nghĩa viết tắt; Tôi có thể gặp may mắn hơn khi nhớ lại điều đó bây giờ. Tài liệu tham khảo CPP đã nói về "Tăng giá trị khởi điểm" (lưu ý sự giống nhau nhỏ) vì vậy tôi có tên viết tắt trong đầu. (Và kết nối Hy Lạp không hiển thị ngay lập tức bởi Google.) Cảm ơn bạn đã tham khảo lịch sử.
Luke Usherwood

47

Bạn nên sử dụng std::iotathuật toán (được định nghĩa trong <numeric>):

  std::vector<int> ivec(100);
  std::iota(ivec.begin(), ivec.end(), 0); // ivec will become: [0..99]

std::fillchỉ cần gán giá trị cố định đã cho cho các phần tử trong dãy đã cho [n1,n2). Và std::iotađiền vào phạm vi đã cho [n1, n2)với các giá trị tăng dần, bắt đầu với giá trị ban đầu và sau đó sử dụng ++value. Bạn cũng có thể sử dụng std::generatethay thế.

Đừng quên đó std::iotalà thuật toán C ++ 11 STL. Nhưng rất nhiều trình biên dịch hiện đại hỗ trợ nó, ví dụ như GCC, Clang và VS2012: http://msdn.microsoft.com/en-us/library/vstudio/jj651033.aspx

PS Hàm này được đặt tên theo hàm số nguyên từ ngôn ngữ lập trình APL và ký hiệu một chữ cái Hy Lạp iota. Tôi suy đoán rằng ban đầu trong APL cái tên kỳ lạ này được chọn vì nó giống với một “integer”(mặc dù trong toán học iota được sử dụng rộng rãi để biểu thị phần ảo của một số phức).


1
Bạn có thể muốn thêm std :: iota là từ C ++ 11
hetepeperfan

@AlexanderKaraberov Nhưng hầu hết các nơi không sử dụng phiên bản mới nhất của trình biên dịch và không thể sử dụng C ++ 11.
James Kanze,

1
iotalà trong STL hơn 15 năm trước đây, vì vậy một số trình biên dịch đã luôn ủng hộ nó, rất lâu trước C ++ 11
Jonathan Wakely

2
Vectơ đó sẽ có kích thước bằng 0. Bạn sẽ cần thêm kích thước vào vùng chứa: std :: vector <int> ivec (100); std :: iota (ivec.begin (), ivec.end (), 0);
AKludges ngày

13

Lựa chọn đầu tiên của tôi (ngay cả trong C ++ 11) sẽ là boost::counting_iterator:

std::vector<int> ivec( boost::counting_iterator<int>( 0 ),
                       boost::counting_iterator<int>( n ) );

hoặc nếu vectơ đã được tạo:

std::copy( boost::counting_iterator<int>( 0 ),
           boost::counting_iterator<int>( ivec.size() ),
           ivec.begin() );

Nếu bạn không thể sử dụng Boost: hoặc std::generate(như được đề xuất trong các câu trả lời khác) hoặc counting_iteratortự triển khai , nếu bạn cần nó ở nhiều nơi khác nhau. (Với Boost, bạn có thể sử dụng một transform_iteratorcủa một counting_iteratorđể tạo ra tất cả các loại chuỗi thú vị. Nếu không có Boost, bạn có thể làm được rất nhiều điều này bằng tay, hoặc trong các hình thức của một loại đối tượng phát cho std::generate, hoặc là một cái gì đó bạn có thể cắm vào một trình lặp đếm viết tay.)


Trong phần đầu tiên của mã, mà constructor của std::vectorđang được gọi là? Phải là phương thức tạo phạm vi , nhưng có thể boost::counting_iteratorchuyển đổi hoàn toàn thành InputIterator không?
CinCout

8

Tôi đã xem các câu trả lời với std :: create nhưng bạn cũng có thể "cải thiện" điều đó bằng cách sử dụng các biến tĩnh bên trong lambda, thay vì khai báo một bộ đếm bên ngoài hàm hoặc tạo một lớp trình tạo:

std::vector<int> vec;
std::generate(vec.begin(), vec.end(), [] {
    static int i = 0;
    return i++;
});

Tôi thấy nó ngắn gọn hơn một chút


5
staticcó ngữ nghĩa sai ở đây, IMO. Tôi sẽ sử dụng một nắm bắt tổng quát [i = 0]() mutableđể rõ ràng rằng biến được xác định phạm vi đối với phiên bản cụ thể của lambda, không phải loại lớp được tạo ra của nó. Thật khó để tìm ra một tình huống có sự khác biệt trong thực tế, và điều đó có thể chỉ ra một thiết kế có vấn đề, nhưng trong mọi trường hợp, tôi nghĩ rằng ngữ nghĩa sẽ tốt hơn khi sử dụng một biến thành viên. Thêm vào đó, nó làm cho mã ngắn gọn hơn; bây giờ phần thân của lambda có thể là một câu lệnh duy nhất.
underscore_d

Vâng, điều đó có vẻ tốt hơn; Tôi chưa bao giờ nhìn thấy một biến được khởi tạo trong chụp lambda trước :)
brainsandwich

6

Nếu bạn không muốn sử dụng các tính năng của C ++ 11, bạn có thể sử dụng std::generate:

#include <algorithm>
#include <iostream>
#include <vector>

struct Generator {
    Generator() : m_value( 0 ) { }
    int operator()() { return m_value++; }
    int m_value;
};

int main()
{
    std::vector<int> ivec( 10 );

    std::generate( ivec.begin(), ivec.end(), Generator() );

    std::vector<int>::const_iterator it, end = ivec.end();
    for ( it = ivec.begin(); it != end; ++it ) {
        std::cout << *it << std::endl;
    }
}

Chương trình này in từ 0 đến 9.


3
@bitmask Chắc chắn nếu bạn có lambdas thì bạn cũng có std::iota, phải không?
BoBTFish

1
@bitmask Nhưng điều đó sẽ đòi hỏi C ++ 11 :)
juanchopanza

2
Điều khá khó hiểu và không hợp lý là thực tế itendđược định nghĩa bên ngoài vòng lặp for. Bất kỳ lý do cho điều này?
Christian Rau

1
@FrerichRaabe Lý do thứ hai nghe có vẻ hợp lý (dù lỗi VS ngu ngốc như vậy có thể hợp lý chút nào, nó có thể thay đổi được trong cài đặt dự án), nhưng tôi không hiểu lý do đầu tiên, có gì sai với std::vector<int>::const_iterator it = vec.begin(), end = ivec.end()(không phải gõ lặp lại cũng không gọi nhiều lần)? Và dòng dài hơn, tốt, đó là C ++ và bạn sẽ không gặp phải một số ngắt dòng không thường xuyên (và những ngày của màn hình 80 ký tự cũ đã qua). Nhưng tôi đoán đó là vấn đề hương vị và bạn đã nhận được sự ủng hộ của tôi từ lâu.
Christian Rau

2
@ChristianRau: Thành thật mà nói: trong thực tế, tôi sử dụng bất kỳ kiểu nào mà mã trong tệp mà tôi đang chỉnh sửa sử dụng, tức là tôi đánh giá tính nhất quán cao hơn và những ưu điểm hoặc nhược điểm mà chúng tôi đã đề cập cho đến nay.
Frerich Raabe

6

Chúng ta có thể sử dụng hàm tạo tồn tại trong tệp tiêu đề thuật toán.

Đoạn mã:

#include<bits/stdc++.h>
using namespace std;


int main()
{
    ios::sync_with_stdio(false);

    vector<int>v(10);

    int n=0;

    generate(v.begin(), v.end(), [&n] { return n++;});

    for(auto item : v)
    {
      cout<<item<<" ";
    }
    cout<<endl;

    return 0;
}

Đây là một câu trả lời rất thanh lịch và có lẽ là ngắn gọn nhất trong trường hợp chung.
Owl

1
IMO ưu việt hơn khi trở nthành thành viên của lambda thông qua chụp ảnh tổng quát [n = 0]() mutable, vì vậy nó không gây ô nhiễm phạm vi xung quanh.
underscore_d

Tôi đang sử dụng cái này. Có thể không hữu ích cho các tình huống nâng cao nhưng đủ tốt cho người mới bắt đầu c ++. giải pháp tốt nếu một người có thể sử dụng lambda.
Abinash Dash

4

std :: iota được giới hạn trong dãy n, n + 1, n + 2, ...

Nhưng nếu bạn muốn điền vào một mảng với một chuỗi chung f (0), f (1), f (2), v.v.? Thông thường, chúng ta có thể tránh một trình tạo theo dõi trạng thái. Ví dụ,

int a[7];
auto f = [](int x) { return x*x; };
transform(a, a+7, a, [a, f](int &x) {return f(&x - a);});

sẽ tạo ra chuỗi các ô vuông

0 1 4 9 16 25 36

Tuy nhiên, thủ thuật này sẽ không hoạt động với các vùng chứa khác.

Nếu bạn bị mắc kẹt với C ++ 98, bạn có thể làm những điều khủng khiếp như:

int f(int &x) { int y = (int) (long) &x / sizeof(int); return y*y; }

và sau đó

int a[7];
transform((int *) 0, ((int *) 0) + 7, a, f);

Nhưng tôi sẽ không giới thiệu nó. :)


1
OP không thể sử dụng C ++ 11 (không có lambas)
yizzlez

2

cái này cũng hoạt động

j=0;
for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){
    *it = j++;
}

2
Tất nhiên, việc dò tìm thủ công với các trình vòng lặp hoạt động, nhưng nếu OP muốn làm điều đó, họ sẽ không hỏi về thuật toán stdlib, phải không?
underscore_d

2

Về mặt hiệu suất, bạn nên khởi tạo vector bằng cách sử dụng reserve()kết hợp với các push_back()hàm như trong ví dụ dưới đây:

const int numberOfElements = 10;

std::vector<int> data;
data.reserve(numberOfElements);

for(int i = 0; i < numberOfElements; i++)
    data.push_back(i);

Tất cả các std::fill, std::generatevv đang hoạt động trên phạm vi nội dung vector hiện có, và, do đó các vectơ phải được lấp đầy với một số dữ liệu trước đó. Ngay cả khi làm như sau: std::vector<int> data(10);tạo một vectơ với tất cả các phần tử được đặt thành giá trị mặc định của nó (tức là 0 trong trường hợp của int).

Đoạn mã trên tránh khởi tạo nội dung vectơ trước khi điền vào nó với dữ liệu bạn thực sự muốn. Hiệu suất của giải pháp này có thể nhìn thấy rõ trên các tập dữ liệu lớn.


2

Có một tùy chọn khác - mà không cần sử dụng iota. Biểu thức for_each + lambda có thể được sử dụng:

vector<int> ivec(10); // the vector of size 10
int i = 0;            // incrementor
for_each(ivec.begin(), ivec.end(), [&](int& item) { ++i; item += i;});

Hai điều quan trọng tại sao nó hoạt động:

  1. Lambda nắm bắt phạm vi bên ngoài [&] có nghĩa là tôi sẽ có sẵn biểu thức bên trong
  2. mục được truyền dưới dạng tham chiếu, do đó, nó có thể thay đổi bên trong vectơ

1

Nếu bạn thực sự muốn sử dụng std::fillvà bị giới hạn trong C ++ 98, bạn có thể sử dụng một cái gì đó như sau,

#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>

struct increasing {
    increasing(int start) : x(start) {}
    operator int () const { return x++; }
    mutable int x;
};

int main(int argc, char* argv[])
{
    using namespace std;

    vector<int> v(10);
    fill(v.begin(), v.end(), increasing(0));
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    return 0;
}

1

Nói về tăng:

auto ivec = boost::copy_range<std::vector<int>>(boost::irange(5, 10));

1

Tôi biết đây là câu hỏi cũ, nhưng tôi hiện đang sử dụng thư viện để xử lý chính xác vấn đề này. Nó yêu cầu c ++ 14.

#include "htl.hpp"

htl::Token _;

std::vector<int> vec = _[0, _, 100];
// or
for (auto const e: _[0, _, 100]) { ... }

// supports also custom steps
// _[0, _%3, 100] == 0, 4, 7, 10, ...

1
Rất tiếc. Đó là một số mã rất khó đọc và nó cũng sẽ không hợp lệ ở phạm vi toàn cầu, như đã viết, bởi vì các số nhận dạng bắt đầu bằng _được dự trữ cho việc triển khai ở đó.
underscore_d

0

Tôi đã tạo một hàm mẫu đơn giản Sequence(), để tạo chuỗi số. Chức năng theo sau seq()chức năng trong R ( liên kết ). Điều thú vị về chức năng này là nó hoạt động để tạo ra nhiều loại và chuỗi số.

#include <iostream>
#include <vector>

template <typename T>
std::vector<T> Sequence(T min, T max, T by) {
  size_t n_elements = ((max - min) / by) + 1;
  std::vector<T> vec(n_elements);
  min -= by;
  for (size_t i = 0; i < vec.size(); ++i) {
    min += by;
    vec[i] = min;
  }
  return vec;
}

Ví dụ sử dụng:

int main()
{
    auto vec = Sequence(0., 10., 0.5);
    for(auto &v : vec) {
        std::cout << v << std::endl;
    }
}

Điều lưu ý duy nhất là tất cả các số phải thuộc cùng một kiểu suy luận. Nói cách khác, đối với số đôi hoặc số nổi, hãy bao gồm số thập phân cho tất cả các đầu vào, như được minh họa.

Cập nhật: 14/06/2018


0

brainsandwich và underscore_d đã đưa ra những ý kiến ​​rất hay. Vì việc điền là thay đổi nội dung, for_each (), thuật toán đơn giản nhất trong số các thuật toán STL, cũng sẽ điền vào hóa đơn:

std::vector<int> v(10);
std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i++;});    

Việc nắm bắt tổng quát [i=o]truyền tải biểu thức lambda với một bất biến và khởi tạo nó ở trạng thái đã biết (trong trường hợp này là 0). từ khóa mutablecho phép trạng thái này được cập nhật mỗi khi lambda được gọi.

Chỉ cần một chút sửa đổi để có một chuỗi các ô vuông:

std::vector<int> v(10);
std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i*i; i++;});

Để tạo ra seq giống R không khó hơn, nhưng lưu ý rằng trong R, chế độ số thực sự gấp đôi, vì vậy thực sự không cần thiết phải tham số hóa kiểu. Chỉ cần sử dụng gấp đôi.

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.