Is there any efficient and idiomatic way to perform the following operation?
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
for (std::size_t i = 0 ; i < a.size() ; ++i)
{
a[i] += b[i];
}
I am trying to avoid the brackets/index notation and only use iterators in order for the operation to work with any container with forward iterators. I thought of the following solution:
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
std::transform(a.begin(), a.end(),
b.begin(),
a.begin(),
std::plus<int>());
However, there is the redundancy of a.begin() and I only get to use + and not +=. Is there some algorithm in the standard library that would allow me to use iterators without having any redundancy or do I have to write the full loops by hand?
Perhaps something that was intended to become idiomatic, but never quite did:
std::valarray<int> a = { 1, 2, 3, 4 };
std::valarray<int> b = { 5, 6, 7, 8 };
Now you can do these
std::valarray<int> c = a + b; //apply '+' element-wise; store result in c
a += b; //apply '+=' element-wise
See the documentation of std::valarray for more details.
What's wrong with the redundancy of a.begin()?
If you're not happy with it, just invent your own algorithm: transform_inplace
template <class InputIterator, class OutputIterator, class BinaryOperator>
OutputIterator transform_inplace (InputIterator first,
InputIterator last,
OutputIterator result,
BinaryOperator op)
{
while (first != last) {
*result = op(*result, *first);
++result;
++first;
}
return result;
}
If you were to use this more than once, and you were interested in a simple interface in the spirit of the Standard Library, you could create a simple template class for the specific use case (which I refer to as "Range Increment"), and write something like:
#include<vector>
#include<algorithm>
#include<iostream>
template<typename InputIt>
InputIt range_increment(InputIt dbeg, InputIt dend, InputIt sbeg) {
while(dbeg!=dend) {
*(dbeg++) += (*sbeg++);
}
return dbeg;
}
int main() {
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
range_increment(a.begin(), a.end(), b.begin());
for(auto x:a) {
std::cout<<x<<std::endl;
}
}
Which yields:
6
8
10
12
Not sure I'd call it "idiomatic", but:
assert(a.size()==b.size());
auto bi = b.begin();
for (auto& i : a) {
i += *(bi++);
}
is pretty concise.
I couldn't find the kind of generic function I was looking for and finally went with the following function that I named range_map ("map the given function element-wise with two given ranges"). As the comments point, it is actually no more than a binary std::for_each:
template<class InputIt1, class InputIt2, class BinaryOperation>
void range_map(InputIt1 first1, InputIt1 last1,
InputIt2 first2, BinaryOperation binary_op)
{
while (first1 != last1) {
binary_op(*first1++, *first2++);
}
}
I created the class plus_assign the following way:
template<typename T>
struct plus_assign
{
void operator()(T &lhs, const T &rhs) const
{
lhs += rhs;
}
};
And then my code becomes:
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
range_map(a.begin(), a.end(),
b.begin(),
plus_assign<int>());
There is also the unary counterpart of the function range_map, to map a given functor to a range:
template<class InputIt, class BinaryOperation>
void range_map(InputIt first, InputIt last,
UnaryOperation unary_op)
{
while (first != last) {
unary_op(*first1++);
}
}
Use operator overload
#include <vector>
std::vector<int>& operator += (std::vector<int>& a, std::vector<int> const& b)
{
for(size_t i = 0; i != a.size(); ++i)
a[i] += b[i];
return a;
}
int main(int argc, char * argv[])
{
std::vector<int> a { 1, 3, 5, 7, 9};
std::vector<int> b { 2, 4, 6, 8, 10};
a += b;
return 0;
}
Related
Given two sorted containers and std::set_union, we can provide a predicate to determine when two elements are equal. I would like to provide an additional predicate that will merge the equal elements (the intersection of the containers) and insert the result into the output container.
Please note in the 'Expected output' section below how the vectors for set_union and unknown_func differ.
Is there a single algorithm that emulates the behavior described by the 'Expected output' below? If there are only more complex ways to have this behavior, can you please suggest where I may get started in doing so? Preferably the final solution only makes use of functionality provided by the std/stl libraries.
Sample Code
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
struct CustomStruct{
CustomStruct(const int f1, const int f2) : field_1(f1), field_2(f2) {}
int field_1;
int field_2;
};
void print_vector(const std::string& str, const std::vector<CustomStruct>& vec){
std::cout << str << std::endl;
for (const auto& val: vec){
std::cout<< val.field_1 << ", " << val.field_2 << std::endl;
}
}
int main()
{
std::vector<CustomStruct> vec_a;
std::vector<CustomStruct> vec_b;
std::vector<CustomStruct> vec_set_union;
std::vector<CustomStruct> vec_unknown_func;
for (int i = 0; i < 4; ++i){ vec_a.emplace_back(i, 2); }
for (int i = 2; i < 4; ++i){ vec_b.emplace_back(i, 3); }
print_vector("VEC_A", vec_a);
print_vector("VEC_B", vec_b);
const auto compare = [](const CustomStruct& lhs, const CustomStruct& rhs){
return lhs.field_1 < rhs.field_1;
};
std::set_union(vec_a.begin(), vec_a.end(),
vec_b.begin(), vec_b.end(),
std::back_inserter(vec_set_union),
compare
);
print_vector("VEC_SET_UNION", vec_set_union);
const auto merge_duplicate = [](const CustomStruct& lhs, const CustomStruct& rhs){
return CustomStruct(lhs.field_1, lhs.field_2 + (rhs.field_2*rhs.field_2));
};
// std::unknown_func(vec_a.begin(), vec_a.end(),
// vec_b.begin(), vec_b.end(),
// std::back_inserter(vec_unknown_func),
// compare,
// merge_duplicate
// );
// THE COMMENTED CODE ABOVE WOULD NEED TO ALLOW 'VEC_UNKNOWN_FUNC' to have
// the 'Expected output' supplied as part of this question
print_vector("VEC_UNKNOWN_FUNC", vec_unknown_func);
}
Expected output
VEC_A
0, 2
1, 2
2, 2
3, 2
VEC_B
2, 3
3, 3
VEC_SET_UNION
0, 2
1, 2
2, 2
3, 2
VEC_UNKNOWN_FUNC
0, 2
1, 2
2, 11
3, 11
Thanks for your time, let me know if I can give further clarifications.
As #Useless suggests in the comments, to do extra things over an <algorithm>, you should write something based on that algorithm.
Adapted from the possible implementation:
template<class InputIt1, class InputIt2,
class OutputIt, class Compare,
class BinaryOp>
OutputIt set_union_transform(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
OutputIt d_first, Compare comp,
BinaryOp binary_op)
{
for (; first1 != last1; ++d_first) {
if (first2 == last2)
return std::copy(first1, last1, d_first);
if (comp(*first2, *first1)) {
*d_first = *first2++;
} else if (comp(*first1, *first2)) {
*d_first = *first1++;
} else {
*d_first = binary_op(*first1++, *first2++);
}
}
return std::copy(first2, last2, d_first);
}
Just bashing at the keyboard but I think you want something like this:
std::vector<CustomStruct> vec_set_intersection1;
std::vector<CustomStruct> vec_set_intersection2;
// Find the duplicate objects in the first vector
std::set_intersection(vec_a.begin(), vec_a.end(),
vec_b.begin(), vec_b.end(),
std::back_inserter(vec_set_intersection1),
compare);
// Find the duplicate objects in the second vector
std::set_intersection(vec_b.begin(), vec_b.end(),
vec_a.begin(), vec_a.end(),
std::back_inserter(vec_set_intersection2),
compare);
// Apply the transformation
std::transform(vec_set_intersection1.begin(), vec_set_intersection1.end(),
vec_set_intersection2.begin(), vec_set_intersection2.end(),
std::back_inserter(vec_unknown_func),
merge_duplicate);
I was given the task to write a function template that takes an interval as the first 2 arguments, and a comparison operator as the 3rd argument, and decide if it's monotonic or not using this comparison operator.
Examples for usage:
1.
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
monotonic(a, a+10, std::greater<int>())
const char *a[] = { "apple", "cherry", "plum", "pickle" };
std::vector<std::string> s(a, a+4);
std::ostream_iterator<std::string> os(cout, ", ");
std::copy(s.begin(), s.end(), os);
monotonic(s.begin(), s.end(), std::greater<std::string>())
I've been Googleing a lot, and figured out that the template is supposed to look something like this:
template<class Iter, typename Comparator>
bool monotonic(Iter first, Iter last, Comparator comp){
}
However, I cannot figure out how I'm supposed to use this comp to actually compare the numbers, characters, etc.
Best way to design API and write code is by using tests.
template<typename T, typename F = std::less<typename std::iterator_traits<T>::value_type>>
bool isMonotonic(T b, T e, F cmp = {})
{
if (b == e) return true;
auto last = b++;
while (b != e) {
if (!cmp(*last, *b)) return false;
last = b++;
}
return true;
}
TEST_CASE("isMonotonic") {
constexpr int growingTale[] = { 1, 2, 7, 10, 22};
constexpr int notDecrasingTale[] = { 2, 2, 2, 10, 22};
SECTION("Empty range is alawyas monotonic") {
auto sameBeginEnd = std::begin(growingTale);
CHECK(isMonotonic(sameBeginEnd, sameBeginEnd));
CHECK(isMonotonic(sameBeginEnd, sameBeginEnd, std::greater{}));
}
SECTION("One element range is always monotonic") {
auto b = std::begin(growingTale);
CHECK(isMonotonic(b, b + 1));
CHECK(isMonotonic(b, b + 1, std::greater{}));
}
SECTION("growing table is monotonic for less operator, but is not for greater operator") {
auto b = std::begin(growingTale);
auto e = std::end(growingTale);
CHECK(isMonotonic(b, e));
CHECK(!isMonotonic(b, e, std::greater{}));
}
SECTION("Not decrasing range is not monotonic, unless <= operator is used") {
auto b = std::begin(notDecrasingTale);
auto e = std::end(notDecrasingTale);
CHECK(!isMonotonic(b, e));
CHECK(!isMonotonic(b, e, std::greater{}));
CHECK(isMonotonic(b, e, std::less_equal{}));
}
}
https://godbolt.org/z/5zbETxTqx
The example below shows how to compute the intersection of two sets. Does the STL provide tools that allow to do this not only for 2 but for N sets?
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> v1 = { 1,2,9,3,4,5 };
std::vector<int> v2 = { 9,4,2,7,4,1 };
std::vector<int> v(v1.size() + v2.size());
std::vector<int>::iterator it;
std::sort(v1.begin(), v1.end());
std::sort(v2.begin(), v2.end());
it = std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
v.resize(it - v.begin());
std::cout << "Both sets have " << (v.size()) << " elements in common:\n";
for (it = v.begin(); it != v.end(); ++it)
{
std::cout << *it << ' ';
}
std::cout << '\n';
return 0;
}
Does the STL provide tools that allow to do this not only for 2 but
for N sets?
No. But you can easily make one, by providing a recursive Variadic template like as follows.
The if constexpr part need c++17 support. However, there are many examples, how you could do it for prior to c++17. In addition, due to recursive call the argument must be passed opposite ordered, to get the behavior you were trying.
(See Online Demo)
#include <vector>
#include <algorithm> // std::set_intersection
#include <iterator> // std::back_inserter
template<typename Container, typename... Rest>
Container NSetIntersections(
const Container& container1, const Container& container2, Rest&&... rest) noexcept
{
if constexpr (sizeof...(Rest) == 0)
{
Container result;
std::set_intersection(container1.begin(), container1.end(),
container2.begin(), container2.end(), std::back_inserter(result));
return result;
}
else
{
Container result;
std::set_intersection(container1.begin(), container1.end(),
container2.begin(), container2.end(), std::back_inserter(result));
return NSetIntersections(result, std::forward<Rest>(rest)...);
}
}
int main()
{
// sorted vectors
std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
std::vector<int> v2 = { 2, 3, 4, 7, 8, 9 };
std::vector<int> v3 = { 3, 4, 7, 200 };
std::vector<int> v4 = { 4, 100, 200, 300 };
std::vector<int> v5 = { 4, 100, 200 };
// call the function like
const auto res1 = NSetIntersections(v2, v1); // 2 3 4
const auto res2 = NSetIntersections(v3, v2, v1); // 3 4
const auto res3 = NSetIntersections(v4, v3, v2, v1); // 4
const auto res4 = NSetIntersections(v5, v4, v3, v2, v1); // 4
return 0;
}
In order to pass the arguments to the NSetIntersections function in natural way, I would suggest following helper functions manner. As a plus, it will also handle the case of passing single arguments (in case, by mistake!) to the NSetIntersections, and c++11 compatible.
(See Online Demo)
#include <vector>
#include <algorithm> // std::set_intersection
#include <iterator> // std::back_inserter
namespace helper { // helper NSetIntersections functions
template<typename Container>
Container NSetIntersections(const Container& container1) noexcept {
return container1;
}
template<typename Container>
Container NSetIntersections(const Container& container1, const Container& container2) noexcept
{
Container result;
std::set_intersection(container1.begin(), container1.end(),
container2.begin(), container2.end(), std::back_inserter(result));
return result;
}
template<typename Container, typename... Rest>
Container NSetIntersections(
const Container& container1, const Container& container2, Rest&&... rest) noexcept
{
return helper::NSetIntersections(
helper::NSetIntersections(container1, container2), std::forward<Rest>(rest)...);
}
}
template<typename... Containers>
auto NSetIntersections(Containers&&... rest) noexcept
-> decltype(helper::NSetIntersections(std::forward<Containers>(rest)...))
{
return helper::NSetIntersections(std::forward<Containers>(rest)...);
}
Now you could call the function with args like this:
// sorted vectors
std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
std::vector<int> v2 = { 2, 3, 4, 7, 8, 9 };
std::vector<int> v3 = { 3, 4, 7, 200 };
std::vector<int> v4 = { 4, 100, 200, 300 };
std::vector<int> v5 = { 4, 100, 200 };
// call the function like
const auto res1 = NSetIntersections(v1); // 1 2 3 4 5 6
const auto res2 = NSetIntersections(v1, v2); // 2 3 4
const auto res3 = NSetIntersections(v1, v2, v3); // 3 4
const auto res4 = NSetIntersections(v1, v2, v3, v4); // 4
const auto res5 = NSetIntersections(v1, v2, v3, v4, v5); // 4
Side note: The bench mark done in quick-bench.com shows (almost) same performance (for 5 sorted containers), when we would have done N times std::set_intersection.
(See Online Quick-bench)
You could put all the vectors you want an intersection of in another vector and then create a function that will loop through them all and calculate the intersection of v1 and v2 and compare their intersection to v3 and then compare their intersection to v4 etc...
Here is a function that does it for you.
using V = std::vector<std::vector<int>>;
std::vector<int> intersections(V vectors)
{
int largest = vectors[0].size();
for (int i = 0; i < vectors.size(); i++)
{
std::sort(vectors[i].begin(), vectors[i].end());
if (vectors[i].size() > largest)
largest = vectors[i].size();
}
std::vector<int> res(largest);
std::vector<int>::iterator it;
for (int i = 0; i < vectors.size() - 1; i++)
{
it = std::set_intersection(vectors[i].begin(), vectors[i].end(),
vectors[i + 1].begin(), vectors[i + 1].end(),
res.begin()
);
res.resize(it - res.begin());
vectors[i + 1].resize(res.size());
std::copy(res.begin(), res.end(), vectors[i + 1].begin());
}
return res;
}
Note: I have only done some very basic testing but it should work.
And this is how you call it
std::vector<int> v1 = { 1,2,9,3,5 };
std::vector<int> v2 = { 9,4,2,7,4,1 };
std::vector<int> v3 = { 4,2,7 };
V vectors = { v1,v2, v3 };
auto res = intersections(vectors);
for (int i = 0; i < res.size(); i++)
std::cout << res[i] << std::endl;
I have two sets and I want to know how many elements there are at least in one set. It is a function set_union in <algorithm> which writes the union in another set, but I want only the number. Can I find it using stl without saving elements?
I agree with Marshall Clow; I don't believe there is an off-the-shelf algorithm to do this. Here's an idea I've been toying with. It is a simple class that provides a push_back method that just increments a counter. You use it with a std::back_inserter as an output iterator.
#include <initializer_list>
#include <iterator>
#include <iostream>
#include <algorithm>
template <typename T>
class CountingPushBack
{
public:
using value_type = T;
void push_back(T const &) {++count;}
std::size_t get_count() const {return count;}
private:
std::size_t count = 0;
};
int main()
{
std::initializer_list<int> il1 = { 0, 1, 2, 3, 4 };
std::initializer_list<int> il2 = { 0, 2, 4, 6, 8 };
CountingPushBack<int> cp;
std::set_union(il1.begin(), il1.end(),
il2.begin(), il2.end(),
std::back_inserter(cp));
std::cout << cp.get_count() << std::endl;
}
I don't know of such an algorithm. That being said, you can use the guts of set_union to write your own to do that; like this:
#include <iostream>
#include <set>
// Counts the number of elements that would be in the union
template <class Compare, class InputIterator1, class InputIterator2>
size_t set_union_size(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
Compare comp)
{
size_t __result = 0;
for (; first1 != last1;)
{
if (first2 == last2)
return __result + std::distance(first1, last1);
if (comp(*first2, *first1))
{
++__result;
++first2;
}
else
{
++__result;
if (!comp(*first1, *first2))
++first2;
++first1;
}
}
return __result + std::distance(first2, last2);
}
int main () {
std::set<int> s1 = { 0, 1, 2, 3, 4 };
std::set<int> s2 = { 0, 2, 4, 6, 8 };
std::cout
<< set_union_size(s1.begin(), s1.end(), s2.begin(), s2.end(), std::less<int>())
<< std::endl;
}
And this prints 7, which is what you would expect.
Although the solution by SCFrench is fine, it does require a container, while we only need a back_insert_iterator. Here is an example of an implementation.
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
template <typename T>
class count_back_inserter {
size_t &count;
public:
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
typedef std::output_iterator_tag iterator_category;
count_back_inserter(size_t &count) : count(count) {};
void operator=(const T &){ ++count; }
count_back_inserter &operator *(){ return *this; }
count_back_inserter &operator++(){ return *this; }
};
You can use it by passing a size_t variable to the constructor that will be incremented for every element that is 'added' to the 'underlying container'.
int main(){
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = { 3, 4, 5, 6, 7};
size_t count = 0;
set_union(v1.begin(), v1.end(),
v2.begin(), v2.end(),
count_back_inserter<int>(count));
std::cout << "The number of elements in the union is " << count << std::endl;
}
What's name of the following described algorithm?
description:
How to test if a sequence is a conform to another in a given pattern?
For example:
The pattern: the number appears in the same order.
bool isOrderValid(vector<int>& mainVec, vector<int>& subVec) {
// how to implement this function?
}
test#1: isOrderValid({1, 2, 3, 4}, {2, 4}); // should return true
test#2: isOrderValid({1, 2, 3, 4}, {4, 2}); // should return false
Explanation:
test #1: the sub sequence is 2, 4; in the main sequence, 2 appear ahead of 4, so the order is correct.
test #2: the sub sequence is 4, 2; in the main sequence, however, 4 appear after 2, so the order is incorrect.
Note: there might be duplicate entry in both array. For example:
isOrderValid({3, 6, 3, 1, 2, 3}, {3, 1, 3}); // should return true
This can be implemented pretty simple (i use function-objects here):
class base_pattern_matcher{
public:
virtual bool operator()(const vector<int>& a , const vector<int>& b) = 0;
}
class in_order_pattern_matcher : public base_pattern_matcher{
public:
bool operator()(const vector<int>& a , const vector<int>& b){
vector<int>::iterator iter_a = a.begin() , iter_b = b.begin();
while(iter_b != b.end() && iter_a != a.end()){
if(*iter_b == *iter_a)
//we found an occurence in a that matches the searched element in b
//--> search for the next element
iter_b++;
//check if the next element matches the searched element
iter_a++;
}
//we have found all elements of b in the given order
return iter_b == b.end();
};
}
isOrderValid(const vector<int>& a , const vector<int>& b , const base_pattern_matcher& matcher){
return matcher(a , b);
}
You could write a standard library style algorithm for this. This example takes two iterator pairs and returns a bool:
#include <iostream>
#include <vector>
template <typename InIt1, typename InIt2>
bool is_subsequence(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2)
{
if (first2 == last2) {
return false; // sub empty (should this return true?)
}
for (; first1 != last1; ++first1) {
if (*first1 == *first2) {
if (++first2 == last2) {
return true; // sub exhausted
}
}
}
return false; // seq exhausted
}
int main()
{
std::vector<int> a = { 1, 2, 3, 4 }, b = { 2, 4 }, c = { 4, 2 };
std::vector<int> d = { 3, 6, 3, 1, 2, 3 }, e = { 3, 1, 3 };
std::cout << is_subsequence(a.begin(), a.end(), b.begin(), b.end()) << '\n';
std::cout << is_subsequence(a.begin(), a.end(), c.begin(), c.end()) << '\n';
std::cout << is_subsequence(d.begin(), d.end(), e.begin(), e.end()) << '\n';
}
Executed on ideone.com
If you like writing generic functions you can make this more flexible and easier to use:
#include <iostream>
#include <list>
#include <vector>
template <typename InIt1, typename InIt2, typename Compare = std::equal_to<>>
bool is_subsequence(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, Compare cmp = Compare{})
{
if (first2 == last2) {
return false; // sub empty (should this return true?)
}
for (; first1 != last1; ++first1) {
if (cmp(*first1, *first2)) {
if (++first2 == last2) {
return true; // sub exhausted
}
}
}
return false; // seq exhausted
}
template <typename Seq, typename Sub, typename Compare = std::equal_to<>>
bool is_subsequence(const Seq &seq, const Sub &sub, Compare cmp = Compare{})
{
return is_subsequence(std::begin(seq), std::end(seq), std::begin(sub), std::end(sub), cmp);
}
int main()
{
std::vector<int> a = { 1, 2, 3, 4 }, b = { 2, 4 };
std::list<int> c = { 4, 2 };
std::vector<int> d = { 3, 6, 3, 1, 2, 3 };
int e[] = { 3, 1, 3 };
std::cout << is_subsequence(a, b) << '\n';
std::cout << is_subsequence(a, b, [](int lhs, int rhs) { return lhs == rhs; }) << '\n';
std::cout << is_subsequence(a, c) << '\n';
std::cout << is_subsequence(d, e) << '\n';
}
Executed on ideone.com