vektörü artan sayılarla doldurmak için std :: fill kullanın


85

Bir vector<int>kullanarak doldurmak istiyorum std::fill, ancak bir değer yerine vektör, daha sonra artan sırada sayılar içermelidir.

Bunu, fonksiyonun üçüncü parametresini birer birer yineleyerek başarmayı denedim, ancak bu bana sadece 1 veya 2 ile dolu vektörleri ( ++operatörün konumuna bağlı olarak) verirdi .

Misal:

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
std::iotaBunun yerine kullanın std::fill(derleyicinizin onu destekleyecek kadar yeni olduğunu varsayarsak, yine de).
Jerry Coffin

1
Ne yazık ki, bu yeni standardın (kullanmamam gereken) bir parçası gibi görünüyor. BOOST kütüphanesinin böyle bir işlevi olduğunu gördüm, ancak vektörleri kabul etmiyor ( boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/… ), ancak bazı özel türleri kabul ediyor. Başka bir seçenek yok mu?
BlackMamba

2
user1612880, C ++ 11 / Boost'u kullanamıyorsanız, sadece Liran'ın kodunu kullanın. Bu bir değil gereklilik her operasyon tek bir hat üzerinde olmak zorunda ne de :-) C kaynak kodu dosyaları kullanılabilir karakter dünya çapında bir sıkıntısı olduğunu
paxdiablo

Kıtlık için değil, performans için. Bununla birlikte, bunu acımasız hackler olmadan mümkün kılmanın bir yolu yoksa, Liran tarafından sağlanan çözümü kullanacağım.
BlackMamba

@ user1612880 ile denediniz mi std::vector? Yükseltme sürümü bir işlev şablonudur ve ilk argümanın "tür adı" bir kavramı belirtir. Söylemesi zor, çünkü sadece çok biçimsel bir tanım bulabiliyorum ve basit bir açıklama bulamıyorum, ama bunun std::vectorkonsepte uyduğunu düşünüyorum .
James Kanze

Yanıtlar:


128

Tercihen şu şekilde kullanın std::iota:

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

Bununla birlikte, eğer herhangi bir c++11desteğiniz yoksa (hala çalıştığım yerde gerçek bir problem), şöyle kullanın std::generate:

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
bu ne anlama geliyor iota? (Görünüşe göre birisi eşit derecede net bir şekilde yanlış yazmış itoa.)
Luke Usherwood

9
Hiçbir şeyi "temsil etmez", i harfinin Yunanca karşılığıdır. APL'de benzer bir işlev için kullanılan ad ve Stepanov'un STL'sindeki fikirlerin çoğunu ateşleyen dizi dilidir.
BoBTFish

1
Ah-ha teşekkürler! Tamam öyleyse bu bir baş harften çok bir kelimedir; Bunu şimdi hatırladığımda daha şanslı olabilirim. CPP Reference, "Başlangıç ​​değerinin artışları" ndan bahsetti (hafif benzerliğe dikkat edin) bu yüzden kafamda baş harfleri aldım. (Ve Yunan bağlantısı Google'da hemen belli değil.) Tarihsel referans için de teşekkürler.
Luke Usherwood

47

std::iotaAlgoritmayı kullanmalısınız (tanımlanmış <numeric>):

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

Çünkü std::fillsadece verilen sabit değeri verilen aralıktaki elemanlara atar [n1,n2). Ve std::iotaverilen aralığı [n1, n2), başlangıç ​​değerinden başlayıp ardından kullanarak sırayla artan değerlerle doldurur . Alternatif olarak ++valueda kullanabilirsiniz std::generate.

Bunun std::iotaC ++ 11 STL algoritması olduğunu unutmayın . Ancak birçok modern derleyici bunu destekler, örneğin GCC, Clang ve VS2012: http://msdn.microsoft.com/en-us/library/vstudio/jj651033.aspx

PS Bu işlev, APL programlama dilindeki tamsayı işlevinden sonra adlandırılır ve Yunanca bir iota harfini belirtir. Başlangıçta APL'de bu garip ismin seçildiğini çünkü “integer”(matematikte iota karmaşık bir sayının hayali kısmını belirtmek için yaygın olarak kullanılmasına rağmen) tahmin ediyorum .


1
Std eklemek isteyebilirsiniz :: iota C ++ 11'den
hetepeperfan

@AlexanderKaraberov Ancak çoğu yer derleyicinin en son sürümünü kullanmıyor ve C ++ 11'i kullanamıyor.
James Kanze

1
iota15 yıldan daha uzun bir süre önce
STL'de yer aldı

2
Bu vektörün boyutu 0 olacaktır. Konteynere bir boyut eklemeniz gerekir: std :: vector <int> ivec (100); std :: iota (ivec.begin (), ivec.end (), 0);
AKludges

14

İlk tercihim (C ++ 11'de bile) şöyle olacaktır boost::counting_iterator:

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

veya vektör zaten oluşturulmuşsa:

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

Boost'u kullanamıyorsanız: ya std::generate(diğer yanıtlarda önerildiği gibi) ya da counting_iteratorçeşitli yerlerde ihtiyacınız varsa kendiniz uygulayın . (Boost ile, her türden ilginç sekanslar oluşturmak için a transform_iteratorof a kullanabilirsiniz counting_iterator. Boost olmadan, bunların çoğunu, bir jeneratör nesne türü biçiminde veya bir nesneye takabileceğiniz bir şey olarak elle std::generateyapabilirsiniz. elle yazılmış sayma yineleyicisi.)


Kodun ilk parçası, içinde yapıcı bir std::vectordenilmesi ise? Aralık yapıcısı olmalı , ancak boost::counting_iteratordolaylı olarak bir InputIterator'a dönüştürülebilir mi?
CinCout

8

Std :: generate ile yanıtları gördüm, ancak işlevin dışında bir sayaç bildirmek veya bir generator sınıfı oluşturmak yerine lambda içindeki statik değişkenleri kullanarak bunu "geliştirebilirsiniz":

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

Onu biraz daha öz buluyorum


5
staticburada yanlış anlambilim var, IMO. [i = 0]() mutableDeğişkenin, üretilen sınıf türüne değil, lambda'nın belirli bir örneğine göre kapsama alındığı açıkça görülmesi için genelleştirilmiş bir yakalama kullanırdım . Pratikte bir farkın olacağı ve muhtemelen şüpheli bir tasarıma işaret edeceği bir durumu düşünmek zordur, ancak her durumda, bir üye değişkeni kullanarak anlambilimin daha üstün olduğunu düşünüyorum. Artı, daha kısa kodlar sağlar; şimdi lambda'nın gövdesi tek bir ifade olabilir.
underscore_d

Evet bu daha iyi görünüyor; Daha önce lambda yakalamasında başlatılan bir değişken görmemiştim :)
brainsandwich

6

C ++ 11 özelliklerini kullanmak istemiyorsanız, şunları kullanabilirsiniz 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;
    }
}

Bu program 0'dan 9'a kadar yazdırır.


3
@bitmask Elbette lambdalarınız varsa, std::iotayine de var mı?
BoBTFish

1
@bitmask Ama bu C ++ 11 gerektirir :)
juanchopanza

2
Sezgisel olmayan ve basitleştirilmemiş olan şey , for-loop dışında tanımlandığı itve endtanımlandığı gerçeğidir . Bunun için herhangi bir sebep var mı?
Christian Rau

1
@FrerichRaabe İkinci neden kulağa mantıklı geliyor (bu kadar aptal bir VS hatası mantıklı olabilse de, proje ayarlarında değişebilir), ancak ilk nedeni anlamıyorum, neyin yanlış std::vector<int>::const_iterator it = vec.begin(), end = ivec.end()(ne tekrarlanan yazım ne de tekrarlanan arama)? Ve satır daha uzun, şey, C ++ ve yine de ara sıra bazı satır sonlarını atlamazsınız (ve eski 80 karakterli ekranların günleri sona erdi). Ama sanırım bu bir zevk meselesi ve zaten benim oyumu uzun zaman önce almıştın.
Christian Rau

2
@ChristianRau: Tamamen dürüst olmak gerekirse: Uygulamada, düzenlediğim dosyadaki kodun kullandığı stili kullanıyorum, yani tutarlılığa şimdiye kadar bahsettiğimiz avantajlardan ve dezavantajlardan daha yüksek değer veriyorum.
Frerich Raabe

6

Algoritma başlık dosyasında bulunan oluşturma işlevini kullanabiliriz .

Kod Parçacığı:

#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;
}

Bu çok zarif bir cevap ve muhtemelen genel durumda en kısa cevap.
Baykuş

1
IMO nGenelleştirilmiş yakalama yoluyla bir lambda üyesi yapmak daha üstündür [n = 0]() mutable, bu nedenle çevredeki kapsamı kirletmez.
underscore_d

Ben bunu kullanıyorum. Gelişmiş durumlar için kullanışlı olmayabilir ancak c ++ yeni başlayanlar için yeterince iyi olabilir. Lambda kullanabiliyorsa iyi bir çözüm.
Abinash Dash

5

std :: iota bir n, n + 1, n + 2, ... dizisiyle sınırlıdır

Peki ya bir diziyi f (0), f (1), f (2) vb. Genel bir diziyle doldurmak isterseniz? Çoğu zaman, durum izleme jeneratöründen kaçınabiliriz. Örneğin,

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

kareler dizisi üretecek

0 1 4 9 16 25 36

Ancak bu numara diğer kapsayıcılarda çalışmayacaktır.

C ++ 98 ile sıkışıp kalırsanız, aşağıdaki gibi korkunç şeyler yapabilirsiniz:

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

ve sonra

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

Ama bunu tavsiye etmem. :)


1
OP, C ++ 11'i kullanamaz (lambas yok)
yizzlez

2

Güçlendirmeden bahsetmişken:

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

2

bu da işe yarar

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

2
Elbette yineleyicilerle manuel olarak uğraşmak işe yarıyor, ancak OP bunu yapmak isteseydi, bir stdlib algoritması sormazlardı, değil mi?
underscore_d

2

Performans açısından, vektörü aşağıdaki örnekteki gibi fonksiyonlarla reserve()birleştirilmiş kullanımıyla başlatmalısınız push_back():

const int numberOfElements = 10;

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

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

Tüm std::fill, std::generatevb mevcut vektör içeriğinin dizi faaliyet gösteriyor ve bu nedenle vektör bazı veriler önceki ile doldurulmalıdır. Aşağıdakileri yapmak bile: std::vector<int> data(10);tüm öğeleri varsayılan değerine (yani 0 olması durumunda 0) ayarlanmış bir vektör oluşturur int.

Yukarıdaki kod, vektör içeriğini gerçekten istediğiniz verilerle doldurmadan önce başlatmaktan kaçınır. Bu çözümün performansı, büyük veri kümelerinde açıkça görülmektedir.


2

Başka bir seçenek daha var - iota kullanmadan. For_each + lambda ifadesi kullanılabilir:

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

Neden işe yaradığı iki önemli şey:

  1. Lambda dış kapsamı yakalar [&] bu, ifadenin içinde mevcut olacağım anlamına gelir
  2. öğe referans olarak iletildi, bu nedenle vektör içinde değiştirilebilir

1

Eğer varsa gerçekten kullanmak istediğiniz std::fillve C ++ 98 ile sınırlı olan aşağıdakilerden gibi bir şey kullanabilirsiniz,

#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

Bunun eski bir soru olduğunu biliyorum, ancak şu anda tam olarak bu sorunu çözmek için kütüphaneyle oynuyorum . C ++ 14 gerektirir.

#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
Eyvah. Bu, okunması çok zor bir koddur ve yazıldığı gibi küresel kapsamda da geçersiz olacaktır, çünkü ile başlayan tanımlayıcılar _oradaki uygulama için rezervdir.
underscore_d

0

Sequence()Sayı dizileri oluşturmak için basit bir şablon işlevi yarattım . seq()İşlevsellik, R ( bağlantı ) ' daki işlevi takip eder . Bu işlevin güzel yanı, çeşitli sayı dizileri ve türleri oluşturmak için çalışmasıdır.

#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;
}

Örnek kullanım:

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

Tek uyarı, tüm sayıların aynı türden türetilmiş olması gerektiğidir. Diğer bir deyişle, çiftler veya kayan değerler için, gösterildiği gibi tüm girdiler için ondalık sayılar ekleyin.

Güncelleme: 14 Haziran 2018


0

brainsandwich ve undercore_d çok iyi fikirler verdi. Doldurmak içeriği değiştirmek olduğundan, STL algoritmaları arasında en basit olan for_each () da faturayı doldurmalıdır:

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

Genelleştirilmiş yakalama [i=o], lambda ifadesine bir değişmez verir ve onu bilinen bir duruma (bu durumda 0) başlatır. anahtar kelime mutablebu durumun lambda her çağrıldığında güncellenmesine izin verir.

Bir dizi kare elde etmek için sadece küçük bir değişiklik gerekir:

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

R benzeri sekans oluşturmak artık zor değil, ancak R'de sayısal modun aslında iki katı olduğuna dikkat edin, bu nedenle türü parametreleştirmeye gerçekten gerek yoktur. Sadece duble kullanın.

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.