という記事が最近出てました。Sprout使えよ、とかそういう思いはありつつ、Rangeの力で書いたらどうなるかなと思いました。
元のコード
#include <ioistream>
int main()
{
int radix; //基数
int index; //指数
//基数、指数の入力
std::cin >> radix >> index;
//答えを格納する変数
int ans = 1;
for (int i = 0; i < index; ++i)
{
ans *= radix;
}
//答えの出力
std::cout << ans << std::endl;
return 0;
}
range-v3
RangeといえばC++20の・・・はとりあえずおいておいて、range-v3の力を借りて行きましょう。
#include <iostream>
#include <range/v3/all.hpp>
int main()
{
std::uint32_t radix; //基数
std::uint16_t index; //指数
//基数、指数の入力
std::cin >> radix >> index;
const auto re = ranges::accumulate(ranges::view::ints(std::uint16_t{},index), 1, [radix](auto re, auto) { return re * radix; });
std::cout << re << std::endl;
}
やっぱり合計したりする系はaccumulateにやらせたいですよね。スッキリしました。
ちなみにWandboxではrange-v3が使えますって話は
君はまだWandboxの真の実力を知らない~C++er目線で~
でしましたね。
range-v3を使えない状況下でもRangeの力を借りたい
まあつまり自分でRangeを作るわけで、つまりiteratorを作ることになります。こんなときは
イテレータの解説をするなんて今更佳代
の出番ですね!
で結果がこちら
#include <iostream>
#include <numeric>
#include <type_traits>
#include <cstdint>
#include <cassert>
#include <limits>
template<typename Range, typename T, typename BinaryOperation>
T accumulate(Range&& r, T init, BinaryOperation&& op)
{
return std::accumulate(std::begin(r), std::end(r), init, std::forward<BinaryOperation>(op));
}
template<typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
class integer_range_iterator {
T begin_;
T end_;
T n_;
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = T;
using pointer = T*;
using reference = T&;
constexpr integer_range_iterator(T begin, T end, T n) noexcept : begin_(begin), end_(end), n_(n) {}
constexpr integer_range_iterator next() const noexcept
{
assert(this->n_ < this->end_);
return { this->begin_, this->end_, this->n_ + 1};
}
constexpr integer_range_iterator prev() const noexcept
{
assert(this->begin_ < this->n_);
return { this->begin_, this->end_, this->n_ - 1};
}
constexpr value_type operator*() const noexcept { return this->begin_ + this->n_; }
// constexpr pointer operator->() const noexcept;
constexpr integer_range_iterator& operator++() noexcept
{
assert(this->n_ < this->end_);
++this->n_;
return *this;
}
constexpr integer_range_iterator operator++(int) noexcept
{
assert(this->n_ < this->end_);
const auto re = *this;
++this->n_;
return re;
}
constexpr integer_range_iterator& operator--() noexcept
{
assert(this->begin_ < this->n_);
--this->n;
return *this;
}
constexpr integer_range_iterator operator--(int) noexcept
{
assert(this->begin_ < this->n_);
const auto re = *this;
--this->n;
return re;
}
constexpr integer_range_iterator operator+(difference_type n) const noexcept
{
assert(this->begin_ < this->n_ + n && this->n_ + n < this->end_);
return { this->begin_, this->end_, this->n_ + n};
}
constexpr integer_range_iterator operator-(difference_type n) const noexcept
{
assert(this->begin_ < this->n_ - n && this->n_ - n < this->end_);
return { this->begin_, this->end_, this->n_ - n};
}
constexpr integer_range_iterator& operator+=(difference_type n) noexcept
{
assert(this->begin_ < this->n_ + n && this->n_ + n < this->end_);
this->n += n;
return *this;
}
constexpr integer_range_iterator& operator-=(difference_type n) noexcept
{
assert(this->begin_ < this->n_ - n && this->n_ - n < this->end_);
this->n -= n;
return *this;
}
constexpr value_type operator[](difference_type n) const noexcept
{
assert(this->begin_ < this->n_ + n && this->n_ + n < this->end_);
return this->begin_ + this->n_ + n;
}
};
template<typename T1, typename T2>
inline constexpr bool operator==(const integer_range_iterator<T1>& l, const integer_range_iterator<T2>& r) noexcept
{
return *l == *r;
}
template<typename T1, typename T2>
inline constexpr bool operator!=(const integer_range_iterator<T1>& l, const integer_range_iterator<T2>& r) noexcept
{
return !(l == r);
}
template<typename T1, typename T2>
inline constexpr bool operator<(const integer_range_iterator<T1>& l, const integer_range_iterator<T2>& r) noexcept
{
return *l < *r;
}
template<typename T1, typename T2>
inline constexpr bool operator>(const integer_range_iterator<T1>& l, const integer_range_iterator<T2>& r) noexcept
{
return r < l;
}
template<typename T1, typename T2>
inline constexpr bool operator<=(const integer_range_iterator<T1>& l, const integer_range_iterator<T2>& r) noexcept
{
return !(r < l);
}
template<typename T1, typename T2>
inline constexpr bool operator>=(const integer_range_iterator<T1>& l, const integer_range_iterator<T2>& r) noexcept
{
return !(l < r);
}
template<typename T1, typename T2>
inline constexpr auto operator-(const integer_range_iterator<T1>& l, const integer_range_iterator<T2>& r) noexcept -> decltype(l.ptr() - r.ptr())
{
return l.ptr() - r.ptr();
}
template<typename T>
inline constexpr integer_range_iterator<T> operator+(typename integer_range_iterator<T>::difference_type n, const integer_range_iterator<T>& it) noexcept
{
return it + n;
}
template<typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
class integer_range {
public:
using iterator = integer_range_iterator<T>;
private:
T begin_;
T end_;
public:
constexpr integer_range(T begin, T end) : begin_(begin), end_(end) {};
iterator begin() const { return { this->begin_, this->end_, this->begin_}; }
iterator end() const { return { this->begin_, this->end_, this->end_}; }
};
static_assert(std::is_trivially_copyable_v<integer_range<int>>);
int main()
{
std::uint32_t radix; //基数
std::uint16_t index; //指数
//基数、指数の入力
std::cin >> radix >> index;
const auto ans = accumulate(integer_range<std::uint16_t>(0, index), std::uint64_t{1}, [radix](auto re, auto) { return re * radix; });
//答えの出力
std::cout << ans << std::endl;
return 0;
}
長い。C++でiterator作るの心が折れそう・・・。
結論
- せっかく
if
とfor
を排除したなら、意味論的なアルゴリズム関数に投げましょう - Range-v3使おう