How to generate vector like list comprehension - c++

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

Related

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

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.

Multiple Call Async for update vector or list in C++

I am stuck in multiple Async problems like that
example:
void updateList(vector<int> &list, int value){
list.push_back(value);
}
int main(){
vector<future<void>> asyncTasks;
vector<int> list;
for(int i = 0; i < 10; i ++){
asyncTasks.push_back(async(launch::async, updateList,i ));
}
for(auto &&f : asyncTasks){
f.get();
}
}
The problem is sometimes it throws errors about insert violent.
Can you give me any ideas ?
Well the problem is that you are doing 2 things at once in updateList:
Calculating a value based on the index given (by calculation I mean just using it)
Adding a value to a container
Doing the second in parallel does not make much sense, since you would have to serialize on the container, otherwise you get data races, which is the reason for your errors.
void updateList(vector<int> &list, int value){
list.push_back(value); //< Data race-> Undefined behavior -> Sometimes Crash
}
But we can do the stuff which can be paralleled easily, namely 1. the calculation of a value.
If we just add dummy zeros in the container, at first, we are allowed to modify the elements in the container i.e. std::vector, since we don't modify the container it self, like count or order, only its members.
So after that you can calculate in parallel, but why not directly use the new parallel algorithms to do that for us? So I added a second solution.
Also this discovery that your work consists of work which can not be paralleled and work which can, can be found in Amdahl's law.
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <execution>
#include <future>
//Your modified solution
void updateList(std::vector<int> &list, int value){
const auto index = value;
//Do the heavy stuff here
list[index] = value;
}
int main(){
std::vector<int> list(10);
std::vector<std::future<void>> asyncTasks;
for(int i = 0; i < 10; i ++){
asyncTasks.emplace_back(std::async(std::launch::async, &updateList, std::ref(list), i));
}
for(auto &f : asyncTasks){
f.get();
}
std::for_each(list.begin(),list.end(), [](auto v) {std::cout << v << " ";});
std::cout << "\n";
}
//Better solution:
int heavy_work_calculation(int input) {
//Do the heavy stuff here
return input;
}
int main(){
std::vector<int> list(10);
std::iota(list.begin(), list.end(), 0);
std::transform(std::execution::par_unseq, list.begin(), list.end(),
list.begin(), heavy_work_calculation);
std::for_each(list.begin(),list.end(), [](auto v) {std::cout << v << " ";});
std::cout << "\n";
}

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";
}

std::find_if on std::vector of custom objects with duplicate data

Consider the following scenario:
typedef struct myStruct
{
int cn;
std::string dn;
} MyStruct;
int main()
{
std::vector<MyStruct> v;
// fill some data
...
...
int c = 1;
std::vector<MyStruct>::iterator it = std::find_if(v.begin(), v.end(),
[c](const MyStruct& m) -> bool { return m.cn == c; });
// use 'it' to do stuff
}
If v contains MyStruct objects such that the member variable cn has a value c (=1) in more than one entries, how to handle that scenario? As std::find_if() returns an iterator to the first element in the range, what about the rest?
find_if find first element in range and returns iterator to it. For find all you can either write loop, that will search each-time from it:
std::vector<MyStruct>::iterator it = v.begin();
while (it != v.end())
{
it = std::find_if(it, v.end(),
[c](const MyStruct& m) -> bool { return m.cn == c; });
if (it != v.end())
{
// process founded item
++it;
}
}
or you can sort your sequence and use equal_range algorithm, that will return std::pair of iterators.
With the current Standard library, you have to write a either a loop over std::find_if with a predicate (lambda if you can use C++11/14), or use std::copy_if to copy every match to a new sequence.
When the Ranges proposal becomes available (in a Technical Specification along with C++17), things get much easier, e.g. you will be able to write one single chain of composable views and actions:
#include <range/v3/all.hpp>
#include <iostream>
#include <vector>
using namespace ranges;
int main()
{
auto const is_even = [](auto x) { return x % 2 == 0; };
auto const print = [&](auto x) { std::cout << x << ","; return x; };
std::vector<int> v { 1, 11, 42, 57, 63, 72 };
v | view::filter(is_even) | action::transform(print);
}
Live On Coliru (already works with the range-v3 library).

Calling a function on every element of a C++ vector

In C++, is there a way to call a function on each element of a vector, without using a loop running over all vector elements? Something similar to a 'map' in Python.
You've already gotten several answers mentioning std::for_each.
While these respond to the question you've asked, I'd add that at least in my experience, std::for_each is about the least useful of the standard algorithms.
I use (for one example) std::transform, which is basically a[i] = f(b[i]); or result[i] = f(a[i], b[i]); much more frequently than std::for_each. Many people frequently use std::for_each to print elements of a collection; for that purpose, std::copy with an std::ostream_iterator as the destination works much better.
Yes: std::for_each.
#include <algorithm> //std::for_each
void foo(int a) {
std::cout << a << "\n";
}
std::vector<int> v;
...
std::for_each(v.begin(), v.end(), &foo);
On C++ 11: You could use a lambda. For example:
std::vector<int> nums{3, 4, 2, 9, 15, 267};
std::for_each(nums.begin(), nums.end(), [](int &n){ n++; });
ref: http://en.cppreference.com/w/cpp/algorithm/for_each
If you have C++11, there's an even shorter method: ranged-based for. Its purpose is exactly this.
std::vector<int> v {1,2,3,4,5};
for (int element : v)
std::cout << element; //prints 12345
You can also apply references and const to it as well, when appropriate, or use auto when the type is long.
std::vector<std::vector<int>> v {{1,2,3},{4,5,6}};
for (const auto &vec : v)
{
for (int element : vec)
cout << element;
cout << '\n';
}
Output:
123
456
The OP mentions the map function in Python.
This Python function actually applies a function to every element of a list (or iterable) and returns a list (or iterable) that collects all results.
In other words, it does something like this:
def f( x ) :
""" a function that computes something with x"""
# code here
return y
input = [ x1, x2, x3, ... ]
output = map( func, input )
# output is now [ f(x1), f(x2), f(x3), ...]
Hence, the closest C++ standard-library equivalent to Python's map is actually std::transform (from the <algorithm> header).
Example usage is as follows:
#include <vector>
#include <algorithm>
using namespace std;
double f( int x ) {
// a function that computes the square of x divided by 2.0
return x * x / 2.0 ;
}
int main( ) {
vector<int> input{ 1, 5, 10 , 20};
vector<double> output;
output.resize( input.size() ); // unfortunately this is necessary
std::transform( input.begin(), input.end(), output.begin(), f );
// output now contains { f(1), f(5), f(10), f(20) }
// = { 0.5, 12.5, 50.0, 200.0 }
return 0;
}
Use for_each:
// for_each example
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void myfunction (int i) {
cout << " " << i;
}
struct myclass {
void operator() (int i) {cout << " " << i;}
} myobject;
int main () {
vector<int> myvector;
myvector.push_back(10);
myvector.push_back(20);
myvector.push_back(30);
cout << "myvector contains:";
for_each (myvector.begin(), myvector.end(), myfunction);
// or:
cout << "\nmyvector contains:";
for_each (myvector.begin(), myvector.end(), myobject);
cout << endl;
return 0;
}
You can use std::for_each which takes a pair of iterators and a function or functor.
Thought I would share std::ranges equivalents for for_each and transform, should anyone prefer them:
std::vector<int> v;
std::ranges::for_each(v,[](const auto& n) {});
const auto squared = v | std::views::transform([](const auto& n) { return n*2; });
Running on godbolt: https://godbolt.org/z/zYME6b