2016-08-26 2 views
2

Angenommen, ich habe die Werte double start, double end und eine stepsize double step.Einfachste Art und Weise zu füllen std :: Vektor <double> mit äquidistanten Werten

Was ist der einfachste Weg std::vector<double> mit Werten bei start beginnen zu füllen und Erhöhen von stepsize solange der aktuelle Wert kleiner als end?

Ich frage mich, ob es eine STL-Funktion gibt, die diese Aufgabe zu einem Einzeiler macht.

std::vector<double> fill(double start, double end, double step) { 
    // Code 
} 

main() { 
    auto ret=fill(0.2, 2.3, 0.2); 
    // ret = {0.2, 0.4, 0.6, ... , 2.2} 
} 
+0

eine Schleife ......... –

+0

Natürlich. Aber kann ich die Schleife vermeiden? – Aleph0

+1

Warum .............? –

Antwort

3

wieder aus akademischem Interesse, und wahrscheinlich die beabsichtigten Gestaltung std::iota zum Zerreißen Biegen:

std::iota(x.begin(), x.end(), double_iota(step, min)); 

Mit folgenden Definition von double_iota:

struct double_iota 
{ 
    double_iota(double inc, double init_value = 0.0) : _value(init_value), _inc(inc) {} 

    operator double() const { return _value; } 
    double_iota& operator++() { _value += _inc; return *this; } 
    double _value; 
    double _inc; 
}; 

Testprogramm:

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

struct double_iota 
{ 
    double_iota(double inc, double init_value = 0.0) : _value(init_value), _inc(inc) {} 

    operator double() const { return _value; } 
    double_iota& operator++() { _value += _inc; return *this; } 
    double _value; 
    double _inc; 
}; 

int main() 
{ 
    double min = 1.0; 
    double max = 2.3; 
    double step = 0.2; 

    std::vector<double> x(std::size_t(((max + step - std::numeric_limits<double>::epsilon()) - min)/step)); 
    std::iota(x.begin(), x.end(), double_iota(step, min)); 

    std::copy(x.begin(), x.end(), std::ostream_iterator<double>(std::cout, ", ")); 
} 

erwartete Ergebnisse:

1, 1.2, 1.4, 1.6, 1.8, 2, 2.2, 

Update:

oder wir können eine benutzerdefinierte Iterator bauen, die uns die Sequenz wirklich in einer Linie zum Ausdruck bringen kann:

std::vector<double> x(double_inc_iterator(min, step), double_inc_iterator(max)); 

wie folgt ows:

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


struct double_inc_iterator : std::iterator<std::forward_iterator_tag, double> 
{ 
    double_inc_iterator(double initial, double inc = 1.0) : _value(initial), _inc(inc) {} 
    value_type operator*() const { return _value; } 
    double_inc_iterator& operator++() { _value += _inc; return *this; } 

    bool operator==(double_inc_iterator const& r) const { return _value >= r._value; } 
    bool operator!=(double_inc_iterator const& r) const { return !(*this == r); } 

    value_type _value; 
    value_type _inc; 
}; 

int main() 
{ 
    double min = 1.0; 
    double max = 2.3; 
    double step = 0.2; 

    std::vector<double> x(double_inc_iterator(min, step), double_inc_iterator(max)); 

    std::copy(x.begin(), x.end(), std::ostream_iterator<double>(std::cout, ", ")); 
} 

Jetzt brauchen wir nicht einmal den Zwischenvektor:

#include <vector> 
#include <numeric> 
#include <iterator> 
#include <limits> 
#include <cstddef> 
#include <stdexcept> 
#include <iostream> 

namespace detail { 
    template <typename T, typename S> 
    constexpr bool is_end_reachable(const T start, const T end, const S step) noexcept 
    { 
     return (end > start && step > 0) || start == end || (end < start && step < 0); 
    } 

    template <typename T, typename S> 
    constexpr std::size_t num_values(const T start, const T end, const S step) noexcept 
    { 
     // Add one as start is always included. Add epsilon to avoid floating point errors. 
     return (((end - start) + std::numeric_limits<T>::epsilon())/step) + 1; 
    } 
} // namespace detail 

template <typename T, typename S> 
std::vector<T> range(const T start, const T end, const S step = 1) 
{ 
    if (!detail::is_end_reachable(start, end, step)) { 
     throw std::invalid_argument {"end not reachable from start"}; 
    } 
    std::vector<T> result(detail::num_values(start, end, step), step); 
    result.front() = start; 
    std::partial_sum(std::cbegin(result), std::cend(result), std::begin(result)); 
    return result; 
} 

Ein paar zusätzliche Würfe sind für die vollständige Art benötigt:

std::copy(double_inc_iterator(min, step), 
      double_inc_iterator(max), 
      std::ostream_iterator<double>(std::cout, ", ")); 
+0

Wirklich schöne Lösung. Insbesondere, da double_iota eine wiederverwendbare Software im Vergleich zur vorherigen Lösung darstellt. – Aleph0

+0

@FrankSimon Da es Ihnen gefallen hat, habe ich eine andere Lösung hinzugefügt, die einen benutzerdefinierten Iterator verwendet. –

1

nur für akademische Zwecke, könnten Sie:

std::vector<double> result; 
std::generate_n(std::back_inserter(result), (size_t)((end-start)/step), [&start, step](){ auto ret=start; start+=step; return ret; }); 
+1

Das ist, was ich suche. Wie oben ausgeführt, vereinfacht es eigentlich nichts. Weder für eine Person, die den Code liest, noch für eine Person, die sie implementieren muss. : - | – Aleph0

+1

Genau. Deshalb ist es rein akademisch. – Smeeheey

+1

Das ist falsch, Sie sollten: 'std :: vector result (1, start);' um 'start' zu enthalten. – 101010

0

Dies kann mit std::partial_sum erfolgen Robustheit, aber die Idee ist die gleiche.

Beispiel:

int main() 
{ 
    const auto v = range(-0.1, -2.7, -0.3312); 
    std::copy(std::cbegin(v), std::cend(v), std::ostream_iterator<double> {std::cout, " "}); 
    std::cout << std::endl; 
} 

Live On Coliru

Verwandte Themen