5
\$\begingroup\$

This program calculates the sum of all integers in the range \$[1, 1000)\$ which are multiples of either 3 or 5 or both.

Inspired by x86-64 Assembly - Sum of multiples of 3 or 5 the other day, and also drawing from some of what I learned through Calculate the centroid of a collection of complex numbers I decided to try to tackle this using C++20 ranges. By heavy use of constexpr, I had hoped to find a solution that would calculate everything at compile time, and indeed this does as you can see if you try it online.

This version is inspired by this talk C++20 Ranges in Practice - Tristan Brindle - CppCon 2020.

I'm interested in general improvements.

euler1.cpp

#include <iostream>
#include <concepts>
#include <ranges>
#include <iterator>
#include <functional>
#include <numeric>

template <std::ranges::input_range R,
    typename Init = std::ranges::range_value_t<R>>
constexpr Init accumulate(R&& rng, Init init = Init{}) 
{
    return std::reduce(std::ranges::begin(rng), std::ranges::end(rng),
        std::move(init));
}

int main() {
    constexpr auto div3_or_5 = [](int i){ return i % 3 == 0 || i % 5 == 0; };
    std::cout << accumulate(std::ranges::iota_view{1, 1000}
        | std::views::filter(div3_or_5)
        | std::views::common)
    << '\n';
}
\$\endgroup\$
1
  • \$\begingroup\$ That hat really does have your name on it.... :_) \$\endgroup\$ – Toby Speight Dec 24 '20 at 11:24
3
\$\begingroup\$

The optional init argement is never used, so we could omit that for this application, and use the two-argument form of std::reduce().

That would solve the other issue neatly - failure to include <utility> before using std::move().

\$\endgroup\$
4
\$\begingroup\$

I see you already solved your own questions from before your latest edit. Just two very minor things that can be improved: you can omit repeating the type name Init in two places:

template <std::ranges::input_range R, typename Init = std::ranges::range_value_t<R>>
constexpr auto accumulate(R&& rng, Init init = {}) 
{
    return std::reduce(std::ranges::begin(rng), std::ranges::end(rng), std::move(init));
}

And you don't need to use std::views::common in main().

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.