LoginSignup
9
1

More than 5 years have passed since last update.

for文とif文を投げ捨ててRangeの力でプログラミングしてみた

Posted at

for文とif文を使わないでプログラミングしてみた

という記事が最近出てました。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作るの心が折れそう・・・。

結論

  • せっかくifforを排除したなら、意味論的なアルゴリズム関数に投げましょう
  • Range-v3使おう
9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1