We have as input a vector, as an example
std::vector<std::int32_t> allnumbers{1,2,-3,4,5,-6,7,8,9};
We have a boolean condition, as an example output numbers have to be larger 3.
What we want is as output the longest subvector fullfilling the condition. All elements of the output have to have been connected in the input.
std::vector<std::int32_t> allnumbers{4,5,7,8,9};
Is wrong, as 5 and 7 have not been adjacent before (-6 between them).
std::vector<std::int32_t> allnumbers{4,5};
Is wrong, as it is not the longest subvector.
std::vector<std::int32_t> allnumbers{7,8,9};
Is finally correct.
How to write the algorithm elegantly with C++17 standard, possibly without using the Boost library? By elegantly I mean few lines of code with good readability. Utilizing prefarably as much as possible. Performance or memory consumption is here less of an issue. I think the "brute force" solution I post below already has here enough performance. One time iterate through input and only few iterators to keep track during exectuion.
The following is a working "brute force" solution:
#include <functional>
#include <iostream>
#include <stdint.h>
#include <vector>
std::vector<std::int32_t> longestConnectedVectorFullfillingPredicate(
std::function<bool(const std::int32_t)> predicate,
std::vector<std::int32_t> &inputVector)
{
auto currentIt = inputVector.begin();
auto endIt = inputVector.end();
auto beginLongestConnectedSubvector = endIt;
auto endLongestConnectedSubvector = endIt;
auto longestConnectedSubvectorLength = 0;
while (currentIt != endIt)
{
const auto currentBeginConnectedSubvector = std::find_if(
currentIt, endIt, [predicate](const std::int32_t &value) { return predicate(value); });
const auto currentEndConnectedSubvector = std::find_if(
currentBeginConnectedSubvector, endIt, [predicate](const std::int32_t &value) {
return !predicate(value);
});
const auto currentConnectedSubvectorLength =
std::distance(currentBeginConnectedSubvector, currentEndConnectedSubvector);
if (currentConnectedSubvectorLength > longestConnectedSubvectorLength)
{
longestConnectedSubvectorLength = currentConnectedSubvectorLength;
beginLongestConnectedSubvector = currentBeginConnectedSubvector;
endLongestConnectedSubvector = currentEndConnectedSubvector;
}
currentIt = currentEndConnectedSubvector;
}
return std::vector<std::int32_t>(beginLongestConnectedSubvector, endLongestConnectedSubvector);
}
int main()
{
const auto largerThree = [](std::int32_t value) { return value > 3; };
std::vector<std::int32_t> allnumbers{1, 2, -3, 4, 5, -6, 7, 8, 9};
auto result = longestConnectedVectorFullfillingPredicate(largerThree, allnumbers);
for (auto res : result)
{
std::cout << res << std::endl;
}
return 0;
}
This is quite a few lines... Would like to shorten it without loosing much readability.
You might like this, which does the following
include needed headers, define useful namespace aliases, and a couple of useful function objects
pipes allnumbers into group_by which puts all adjacent numbers greater than 3 in a range/chunk (all other numbers remain in singleton range each)
pipes that into filter which says goodby to the singletons with the number not greater than 3
then finds the longest range by using max_element to which an appropriate lambda is passed
#include <boost/hana/functional/on.hpp>
#include <boost/hana/functional/partial.hpp>
#include <boost/hana/functional/reverse_partial.hpp>
#include <boost/range/numeric.hpp>
#include <functional>
#include <iostream>
#include <range/v3/algorithm/max_element.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/group_by.hpp>
#include <range/v3/view/transform.hpp>
#include <vector>
using boost::hana::on;
using boost::hana::reverse_partial;
using namespace ranges::views;
using namespace ranges;
auto both = [](bool x, bool y){ return x && y; };
auto greater_than_3 = reverse_partial(std::greater<>{}, 3);
int main() {
std::vector<int> allnumbers{1,2,-3,4,5,-6,7,8,9};
auto chunks
= allnumbers
| group_by(both ^on^ greater_than_3)
| filter([](auto v){ return greater_than_3(v.front()); })
| transform(to_vector)
| to_vector;
auto result = *max_element(
chunks,
std::less<>{} ^on^ std::mem_fn(&decltype(allnumbers)::size));
for (auto i : result) {
std::cout << i << ',';
}
}
Related
I'm given a CSV file with two elements per line:
1,2
12,40
11,7
...
which I want to read into a std::map<int, int>.
How can I do that, using anything from Ranges library and Range-v3?
At the moment this is where I've got (with the help of this answer):
#include <boost/hof/lift.hpp>
#include <iostream>
#include <range/v3/istream_range.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/istream.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/chunk.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/split.hpp>
#include <string>
using ranges::istream;
using ranges::to;
using namespace ranges::views;
constexpr auto splitAtComma = [](auto const& r) { return r | split(','); };
constexpr auto rngToString = [](auto const& r) { return r | to<std::string>; };
constexpr auto strToInt = BOOST_HOF_LIFT(std::stoi);
constexpr auto parseCoords = transform(splitAtComma)
| join
| transform(rngToString)
| transform(strToInt)
| chunk(2);
int main() {
auto lines = istream<std::string>(std::cin);
auto coords = lines | parseCoords;
std::cout << coords << std::endl;
}
which, invoked like this
./main <<END
1,2
12,40
11,7
END
gives this output
[[1,2],[12,40],[11,7]]
The point is that now I don't know how convert that range-of-ranges into a map. Also, I have the feeling that I'm in a dead-end street, because each element of a std::map<int, int> comes from 2 ints, but in the range-of-ranges above [1,2], [12,40], and [11,7] are just ranges of ints, not encoding at compile tiime that there's 2 ints only.
There is no need to use join here because r | split(',') already gives you a range with two elements. Prefer std::from_chars over std::stoi.
You can do this only using the standard library's <ranges>
#include <ranges>
#include <charconv>
#include <fmt/ranges.h>
#include <sstream>
auto toInt = [](auto r) {
int i = 0;
std::from_chars(std::to_address(r.begin()), std::to_address(r.end()), i);
return i;
};
auto parseCoords =
std::views::transform(
[](auto r) { return std::move(r) | std::views::split(','); })
| std::views::transform(
[](auto r) { return std::pair{toInt(r.front()), toInt(*++r.begin())}; });
int main() {
auto in = std::istringstream{R"(
1,2
12,40
11,7
)"};
auto coords = std::views::istream<std::string>(in)
| parseCoords;
fmt::print("{}\n", coords);
}
Demo
Note that the std::move(r) in views::transform is necessary because we need to construct an owning_view to avoid the dangling issue.
I've been using extensively boost::algorithm::find_if_backward to get a forward iterator to the last element in a range satisfying a predicate.
How do I accomplish the same task using Range-v3?
This is my attempt, which looks a bit clunky; and I'm not even sure is robust enough. Actually, as suggested in a comment, the code is not robust enough, because when no element is found, range_it_to_last_2 ends up being std::next(v.begin(), -1), which is undefined behavior, I believe.
#include <algorithm>
#include <boost/algorithm/find_backward.hpp>
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/algorithm/find_if.hpp>
#include <range/v3/view/reverse.hpp>
using boost::algorithm::find_if_backward;
using ranges::find_if;
using ranges::views::reverse;
auto constexpr is_2 = boost::hana::partial(std::equal_to<>{}, 2);
int main() {
std::vector<int> v{0,1,2,2,2,3};
// What I have been doing so far:
auto boost_it_to_last_2 = find_if_backward(v, is_2);
// The Range-v3 analogous I could come up with, but it's ugly:
auto range_it_to_last_2 = std::next(find_if(v | reverse, is_2).base(), -1);
for (auto it = v.begin(); it <= boost_it_to_last_2; ++it) {
std::cout << *it << ' ';
} // prints 0 1 2 2 2
std::cout << std::endl;
for (auto it = v.begin(); it <= range_it_to_last_2; ++it) {
std::cout << *it << ' ';
} // prints 0 1 2 2 2
std::cout << std::endl;
}
Assuming you always know that a match is found, why not simplify to the following, getting the identical output:
Live On Godbolt
#include <algorithm>
#include <boost/algorithm/find_backward.hpp>
#include <boost/hana/functional/partial.hpp>
#include <fmt/ranges.h>
#include <range/v3/algorithm/find_if.hpp>
#include <range/v3/view/subrange.hpp>
#include <range/v3/view/reverse.hpp>
using boost::algorithm::find_if_backward;
using ranges::find_if;
using ranges::views::reverse;
using ranges::subrange;
auto constexpr pred = boost::hana::partial(std::equal_to<>{}, 2);
int main() {
std::vector<int> v {0,1,2,2,2,3};
auto boost_match = find_if_backward(v, pred);
auto range_match = find_if(v | reverse, pred).base();
static_assert(std::is_same_v<decltype(boost_match), decltype(range_match)>);
fmt::print("boost: {}\nrange: {}\n",
subrange(v.begin(), boost_match+1),
subrange(v.begin(), range_match));
}
Prints
boost: {0, 1, 2, 2, 2}
range: {0, 1, 2, 2, 2}
(Some toy respellings for fun: https://godbolt.org/z/ccPKeo)
In the following small example I was trying to group elements by the difference between consecutive elements being 1. As the output shows, however, group_by's predicate is evaluated between the current element and the first element of the group being processed.
#include <iostream>
#include <range/v3/view/group_by.hpp>
int main() {
using ranges::views::group_by;
std::vector<int> v{1,2,3,6,7,8,9,12,14,15};
for (auto i : v | group_by([](auto x2,auto x1){ return x2 - x1 == 1; })) {
std::cout << i << '\n';
}
}
Does Range-v3 offer a way to evaluate the predicate between consecutive elements of the group?
I once asked the same question for Haskell, and it turned out a namesake function is on Hackage.
group_by has been deprecated in favor of chunk_by, which does exactly what you want:
Given a source range and a binary predicate, return a range of ranges where each range contains contiguous elements from the source range such that the following condition holds: for each element in the range apart from the first, when that element and the previous element are passed to the binary predicate, the result is true. In essence, views::chunk_by groups contiguous elements together with a binary predicate.
using ranges::views::chunk_by;
std::vector<int> v{1,2,3,6,7,8,9,12,14,15};
for (auto i : v | chunk_by([](auto l, auto r){ return r - l == 1; })) {
std::cout << i << '\n';
}
Prints
[1,2,3]
[6,7,8,9]
[12]
[14,15]
This is the ugly result of my attempt to compose what's already in Range-v3 to get what I wanted.
#include <iostream>
#include <range/v3/view/group_by.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/concat.hpp>
#include <range/v3/view/zip_with.hpp>
constexpr auto elem_and_distance_from_previous
= [](int x, int y){ return std::make_pair(x,x - y); };
constexpr auto second_is_1 = [](auto,auto x){ return x.second == 1; };
constexpr auto get_first = [](auto x){ return x.first; };
using namespace ranges::views;
int main() {
std::vector<int> v{1,2,3,6,7,8,9,12,14,15};
auto w = zip_with(elem_and_distance_from_previous,v,concat(iota(0,1),v))
| group_by(second_is_1)
| transform([](auto x){ return x | transform(get_first); });
std::cout << w << '\n';
}
// outputs [[1,2,3],[6,7,8,9],[12],[14,15]]
Probably as #einpoklum suggested in a comment, a copy-paste-edit of the original group_by would be much better. However I asked the question because I wanted to know if there was a way already in Range-v3 to do what I meant.
I am playing with boost::range and boost::lambda with following example to compare two numbers and get the element out which has same number.
#include <iostream>
#include <boost/optional.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/utility/compare_pointees.hpp>
template <class Range, class Predicate>
boost::optional<typename boost::range_value<Range>::type>
search_for(const Range& r, Predicate pred)
{
BOOST_AUTO (it, boost::find_if(r, pred));
if (it == boost::end(r))
return boost::none;
return *it;
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 3;
std::vector<int*> m = {&a, &b, &c};
if (boost::optional<int*> number =
search_for(m, boost::equal_pointees(???, &d))) {
std::cout << "found:" << (*number.get()) << std::endl;
}
else {
std::cout << "not found" << std::endl;
}
}
What should I use for ??? above in search_for function?
I believe it could be very simple but don't know how to do it. I can use the boost::bind or std::bind2d, etc to compare but was thinking if there is any elegant way to do it. Also, this code sample could be restructured to much simpler one but I am just learning.
With boost::lambda, it looks like this:
namespace ll = boost::lambda;
search_for(m, *ll::_1 == d)
Which is far less complicated than taking a pointer to d just so you can use equal_pointees.
I would like to loop through two collections using iterators, modifying one based on a (sufficiently complex) algorithm involving the other. Consider the following minimal example:
#include <iostream>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/range/combine.hpp>
#include <boost/tuple/tuple.hpp> // tie
using namespace std;
using namespace boost;
int main(void) {
// input data; never mind how these get filled
int aa[] = {2, 3, 5, 8, 13, 21};
int bb[] = {1, 0, 1, 1, 0, 1};
vector<int> a (&aa[0], &aa[sizeof(aa)/sizeof(aa[0])]);
vector<int> b (&bb[0], &bb[sizeof(bb)/sizeof(bb[0])]);
// output storage; assume it has always correct dim.
vector<int> c (a.size());
// iterate through two coll., reading from both
int p, q;
BOOST_FOREACH (tie(p,q), combine(a,b)) { // loop1
cout << p << "*" << q << "=" << p*q << endl;
}
// iterate through one coll., writing to it
BOOST_FOREACH (int& r, c) { // loop2
r = 42;
}
// iterate through two coll., reading from one, writing to the other?
BOOST_FOREACH (??? p, s ???, combine(a,c)) { // loop3
s = p * 2;
}
return 0;
}
How do I declare the part between the ???s (or otherwise change the parameters in loop3)?
The value type of a zip_range is a tuple of references to elements:
#include <iostream>
#include <vector>
#include <boost/range.hpp>
#include <boost/range/combine.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/foreach.hpp>
int main(int ac,char* av[])
{
// input data; never mind how these get filled
int aa[] = {2, 3, 5, 8, 13, 21};
int bb[] = {1, 0, 1, 1, 0, 1};
std::vector<int> a(boost::begin(aa), boost::end(aa));
std::vector<int> const b(boost::begin(bb), boost::end(bb));
// output storage; assume it has always correct dim.
std::vector<int> c (a.size());
typedef boost::tuple<int const&, int&> val_t;
BOOST_FOREACH(val_t const& v, boost::combine(a, c)) {
v.get<1>() = v.get<0>() * 2;
}
}
IMO, you're overusing BOOST_FOREACH to an almost shocking degree. Like std::foreach, this should be one of your last choices of algorithm.
Your third loop should almost certainly be written using std::transform. It's intended to take an input range, transform it, and deposit the result in an output range (or take two input ranges, combine them, and put the result in a third, such as in your first (mis)use of BOOST_FOREACH).
Using this, your third loop comes out something like:
// c[i] = a[i] * 2, i = 0..N-1
std::transform(begin(a), end(a), begin(c), [](int i) { return i * 2; });
As for your second, it looks like you really want std::fill_n.
std::fill_n(begin(c), end(c), 42);
Now, it's certainly true that something based on ranges (e.g., the algorithms in Boost Range) could make this a bit simpler by replacing each begin(X), end(X) pair with a single parameter. Nonetheless, these are already clearly superior to the BOOST_FOREACH versions with miscellaneous ties and combines to try force your square pegs into round holes.