ranges::views::group_by-like function applying predicate to consecutive elements? - c++

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.

Related

Extract longest connected subvector fullfilling a condition

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 << ',';
}
}

How do you perform transformation to each element and append the result in c++?

I have a set of integers {1,2}. I want to produce "Transform#1, Transform#2" where each element is tranformed and then result is accumulated with a delimiter.
What would be the easiest way to accomplish this? Do we have "folds", "maps" in c++?
We dont use boost.
You can use std::transform and std::accumulate
int main()
{
std::vector<int> v1 {1,2,3};
std::vector<std::string> v2;
std::transform(begin(v1), end(v1), std::back_inserter(v2), [](auto const& i) {
return std::string("Transform#") + std::to_string(i);
});
std::string s = std::accumulate(std::next(begin(v2)), end(v2), v2.at(0), [](auto const& a, auto const& b) {
return a + ", " + b;
});
std::cout << s;
}
prints Transform#1, Transform#2, Transform#3
You may want to use Range Adaptors. Boost already has them and they are coming to the standard with C++20.
Take a look at the boost::adaptors::transformed example here.
Also, check out the reference to get a better picture of what operations are supported by adaptors.
In the end, you can achieve much cleaner code and the performance difference is negligible (unlike in some other languages, where using this style of programming incurs heavy performance costs).
If you can stand a trailing separator, the following function can transform any iterable range of data { X, ..., Z } to the string "<tag>X<sep>...<sep><tag>Z<sep>".
Code
template <class InputIt>
std::string f(InputIt begin, InputIt end, std::string_view separator = ", ", std::string_view tag = "Transform#")
{
std::stringstream output;
std::transform(begin, end,
std::ostream_iterator<std::string>(output, separator.data()),
[tag](auto const& element){ return std::string{tag} + std::to_string(element); }
);
return output.str();
}
It works by transforming each element from the range into a stream iterator.
Usage
int main()
{
std::set<int> const data{1, 2, 3}; // works with vector, string, list, C-arrays, etc.
std::cout << f(begin(data), end(data)) << '\n';
// prints Transform#1, Transform#2, Transform#3,
}
Live demo
You can perform a fold using simply std::accumulate
#include <set>
#include <string>
#include <iostream>
#include <numeric>
int main()
{
auto transformation = [](int number) { return "Transform#" + std::to_string(number); };
auto transform_and_fold = [&transformation](std::string init, int number) { return std::move(init) + ", " + transformation(number); };
std::set<int> numbers{1, 2};
std::cout << std::accumulate(std::next(numbers.begin()), numbers.end(), transformation(*numbers.begin()), transform_and_fold);
}
Outputs
Transform#1, Transform#2
Assuming that I correctly understand the problem, the following straightforward implementation also looks very simple and easy.
This function works in C++11 and over:
DEMO with 5 test cases
std::string concatenate(
const std::vector<int>& indecies,
const std::string& delimiter = ", ",
const std::string& tag = "Transform#")
{
if(indecies.empty()){
return "";
}
std::string s(tag + std::to_string(indecies[0]));
for(auto it = indecies.begin()+1; it != indecies.cend(); ++it){
s += (delimiter + tag + std::to_string(*it));
}
return s;
}
(BTW, as for this function concatenate, if indecies is empty, the return value is also an empty string, not exceptions (AndreasDM's one) or UB (Everlight's one).
And if indecies has only a single element, for instance indecies={1}, then result is "Transform#1”, not "Transform#1, ”(YSC's one) or ", Transform#1”(sakra's one).
These are different from other answers and this function will be more simpler if this handling is removed.)
Although the performance may not be a focal point, the above function can be slightly optimized by pre-reserving the minimum capacity to save the resulted string by std::basic_string::reserve as follows.
Here +1 in *.size()+1 means the minimum length of a number character.
I also removed delimiter+tag in the for-loop.
This still looks simple:
DEMO with 5 test cases
std::string concatenate_fast(
const std::vector<int>& indecies,
std::string delimiter = ", ",
const std::string& tag = "Transform#")
{
if(indecies.empty()){
return "";
}
std::string s(tag + std::to_string(indecies[0]));
delimiter += tag;
s.reserve((tag.size()+1) + (indecies.size()-1)*(delimiter.size()+1));
for(auto it = indecies.begin()+1; it != indecies.cend(); ++it){
s += (delimiter + std::to_string(*it));
}
return s;
}
I have also tested the performance of these functions and some proposed answers as follows.
These tests are done by Quick C++ Benchmark within gcc-8.2, C++17 and O3 optimization.
Since std::transform_reduce is still not available in Quick C++ Benchmark, I haven’t tested it.
The above concatenate_fast shows best performance at least in these cases and concatenate is second best.
Finally, just personally, taking the balance of the readability and the performance into account, I would like to propose the above concatenate as a solution:
- Performance test with size 2 and 8. (DEMO)
- Performance test with size 16 and 32. (DEMO)
Unless you have some other requirement to preserve the intermediate tranformed list, storing it is suboptimal. You can just call std::accumulate and do both operations on the fly:
#include <cstdio>
#include <iterator>
#include <numeric>
int main ( )
{
int const input [] = { 1, 2, 3, 4, 5, 6 };
// computes sum of squares
auto const add_square = [] ( int x, int y ) { return x + y * y; };
int result = std::accumulate
( std::cbegin (input)
, std::cend (input)
, 0
, add_square
);
std::printf ( "\n%i\n", result );
return 0;
}
If you have the luxury of using C++17, there is a standard library algorithm which does exactly what you need. Here is an example:
#include <iterator>
#include <iostream>
#include <numeric>
#include <string>
int main()
{
auto input = {1, 2, 3};
std::cout << std::transform_reduce(
std::cbegin(input), std::cend(input),
std::string("Result:"),
[](const std::string & left, const std::string & right) { return left + " " + right; },
[](int value) { return "Transform#" + std::to_string(value); }
) << "\n";
}

STL sorted vector find first element less than or equal to given value

I have a vector of pairs. Suppose it is like this:
vector<pair<int,int>> vec = { {1,12}, {1,5}, {1,6}, {1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };
The pairs are sorted by first element.
Given a pair, I need to find the index of the last pair in the vector whose first element is less than or equal to first element of the given pair. If for that last pair, other pairs lie to its left with the same value of the first element, I need the first of all those pairs:
<4,10> => 4 (vec[4] is <3,9>, the elements with the largest first value less than or equal to 4 are those with first element as 3, and there are 4 pairs with a 3 in the first element, at indices 4-7, so return the first of those pairs)
<0,10> => -1, since no element exists to its right.
<1,6> => 0 (vec[0] is <1,12>. There is no pair whose first element is less than 1, and there are 4 pairs, including <1,6> whose first element is 1. So we need the first of these 4 pairs.)
<23,81> => 12 (vec[12] is <20,8>)
Condition: I need to use only standard algorithms like upper_bound, binary_search and lower_bound. I tried this, but it is failing badly:
vector<pair<int,int>> vec = { {1,12}, {1,5}, {1,6},{1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };
auto i = std::lower_bound(vec.begin(), vec.end(), make_pair<int,int>(4,10),
[](const pair<int,int>& f1, const pair<int,int>& f2) { return f1.first < f2.first; });
cout << i-vec.begin();
Since you want the first pair, you maybe want to combine lower and upper bound?
#include <algorithm>
#include <vector>
#include <utility>
#include <iostream>
using namespace std;
int main()
{
vector<pair <int,int> > vec = { {1,12}, {1,5}, {1,6}, {1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };
auto u_it = std::upper_bound(vec.begin(), vec.end(), make_pair<int,int>(4, 10),
[](const pair<int,int>& f1, const pair<int,int>& f2) { return f1.first < f2.first; });
if(u_it == vec.begin())
cout << "-1\n";
auto l_it = std::lower_bound(vec.begin(), u_it, *prev(u_it),
[](const pair<int,int>& f1, const pair<int,int>& f2) { return f1.first < f2.first; });
cout << l_it - vec.begin() << "\n";
return 0;
}
Output:
Georgioss-MacBook-Pro:~ gsamaras$ g++ -Wall -std=c++0x main.cpp
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out
4
PS - answer updated after WhozCraig's comment:
you want to use it = std::upper_bound(beg,end) to find the first strictly-greater element, and if that answers non-begin, then use std::lower_bound(beg,it) to find the lowest-matching ordinal of the element whose value is pulled from (it-1).
The answer now satisfies all the test cases you have provided (I am not showing it here). Hope that helps! :)
Appendix:
Ref for std::lower_bound, std::upper_bound and std::prev. Please notice how the std::lower_bound call uses std::make_pair without an initializing list, so that it lets the compiler come into play and resolve deduce the type.
std::lower_bound returns the first item greater or equal to the given value
you need to
+1 to the input of std::lower_bound
-1 to the result of std::lower_bound
to find the value (or you can use std::upper_bound)
and use std::lower_bound again to find the right pair
example
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int my_find(const vector<pair<int,int>>& vec, int value){
auto comparer = [](const pair<int,int>& f1, int value) { return f1.first < value; };
auto i = std::lower_bound(vec.begin(), vec.end(), value+1, comparer);
if(i==vec.begin()){return -1;}
i = std::lower_bound(vec.begin(), vec.end(), (i-1)->first, comparer);
return i-vec.begin();
}
int main(){
vector<pair<int,int>> vec = { {1,12}, {1,5}, {1,6},{1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };
cout << my_find(vec,-1) << '\n';
cout << my_find(vec,3) << '\n';
cout << my_find(vec,10) << '\n';
cout << my_find(vec,100) << '\n';
}
BTW, you don't need to provide pair to lower_bound
and if you only use lower_bound, you need only one comparer

How to generate vector like list comprehension

In C++11,
vector<string> blockPathList;
for(int i = 0; i < blockNum; i++)
{
blockPathList.push_back(desPath + "part" + to_string(i));
}
Is it possible to re-write the code above like list comprehension, or shorter and more concise?
Do you want to use third-party libraries? Eric Niebler's range-v3 allows for:
std::vector<string> blockPathList =
view::ints(0, blockNum)
| view::transform([&desPath](int i) {
return desPath + "part" + std::to_string(i);
});
That's about as functional list-comprehension-y as you're going to get in C++.
Not pretty either, but should also get the job done:
int cur = 0;
std::vector<std::string> blockPathList(blockNum);
std::generate(blockPathList.begin(), blockPathList.end(),
[&](){ return destPath + "part" + std::to_string(cur++); });
Unfortunately this
Requires the vector to be pre-sized
Requires an external iteration variable (since the std::generate Generator does not take any arguments.
You can also use std::for_each:
std::vector<int> nums(blockNum);
std::iota(nums.begin(), nums.end(), 0);
std::for_each(nums.begin(), nums.end(), [&](int c) {
blockPathList.push_back(destPath + "part" + std::to_string(c));
});
but again this is uglified because std::iota doesn't generate ranges. It populates an existing range with an iterator, rather than acting as a numeric iterator in-itself (of course you can solve that by implementing or using something which generates those iterators)
Another example (c++14):
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
template<typename CONTAINER, typename LAMBDA>
auto comprehension(CONTAINER&& container, LAMBDA&& lambda){
std::vector<decltype(lambda(*container.begin()))> w;
std::transform(container.begin(),container.end(),std::back_inserter(w), lambda);
return w;
}
int main(){
auto&& ints = {1,2,3,4,5};
auto&& squares = comprehension(ints,[](auto i){ return i*i; });
for( auto s : squares){ std::cout << s << ' '; }
std::cout << '\n';
}
Output:
1 4 9 16 25

Any built-in function to test if 4 is in [1,2,3,4] (vector)

In Ruby I can do:
[1,2,3,4].include?(4) #=>True
In Haskell I can do :
4 `elem` [1,2,3,4] #=> True
What should I do in C++?
Here an example using find:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> Num(4);
//insert values
Num[0]=1;
Num[1]=2;
Num[2]=3;
Num[3]=4;
std::vector<int>::iterator p = find(Num.begin(), Num.end(), 4);
if (p == Num.end())
std::cout << "Could not find 4 in the vector" << std::endl;
else
std::cout << "Have found 4 in the vector" << std::endl;
return 0;
}
There isn't a built-in function doing exactly that.
There is std::find which comes close, but since it doesn't return a bool it is a bit more awkward to use.
You could always roll your own, to get syntax similar to JIa3ep's suggestion, but without using count (which always traverses the entire sequence):
template <typename iter_t>
bool contains(iter_t first, iter_t last, typename iter_t::value_type val){
return find(first, last, val) != last;
}
Then you can simply do this to use it:
std::vector<int> x;
if (contains(x.begin(), x.end(), 4)) {...}
If the vector is ordered, you can also use std::binary_search.
std::binary_search(vec.begin(), vec.end(), 4) // Returns true or false
To get similar syntax as in OP's question:
std::vector<int> x;
if ( count( x.begin(), x.end(), VAL_TO_FIND ) ) {
// found
} else {
// not found
}
You could use std::set This has a find() method.