2016-08-26 3 views
2

Предположим, что у меня есть значения double start, double end и шаг double step.Самый простой способ заполнить std :: vector <double> с эквидистантными значениями

Что это самый простой способ для заполнения std::vector<double> со значениями, начиная с start и увеличивающимся на stepsize тех пор, пока текущее значение меньше, чем end?

Я спрашиваю себя, есть ли функция stl, которая делает эту задачу однострочным.

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

петля ......... –

+0

Конечно. Но могу ли я избежать цикла? – Aleph0

+1

Почему .............? –

ответ

3

снова из академического интереса, и, вероятно, сгибая предполагаемый дизайн std::iota до предела:

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

со следующими определение 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; 
}; 
Программа

Тест:

#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, ", ")); 
} 

Ожидаемые результаты:

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

обновление:

или мы можем построить пользовательский итератор, который позволяет нам выразить последовательность действительно в одной строке:

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

as foll РМО:

#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, ", ")); 
} 

Теперь мы даже не нужен промежуточный вектор:

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

Действительно приятное решение. Особенно, поскольку double_iota представляет собой многократно используемое программное обеспечение по сравнению с предыдущим решением. – Aleph0

+0

@FrankSimon, так как вам понравилось, я добавил другое решение, которое использует пользовательский итератор. –

1

Только для академических целей, вы можете:

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

Это то, что я ищу. Как указано выше, это действительно ничего не упрощает. Ни для человека, читающего код, ни для человека, который должен его реализовать. : - | – Aleph0

+1

Точно. Именно поэтому он носит чисто академический характер. – Smeeheey

+1

Это неверно, вы должны: 'std :: vector результат (1, начало);' включить значение 'start'. – 101010

0

Это может быть сделано с std::partial_sum:

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

Несколько лишних забросы необходимы для полного типа но идея такая же.

Пример:

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