I'm trying to rewrite some loops in my code as standard library functions. The following snippet seems to follow a general model for early termination and I'm wondering how to express that without explicit loops?
auto sum = 0;
for (const string& key : keys) {
if (my_map.find(key) == my_map.end())
return nullptr;
sum += my_map[key];
}
return make_unique<int>(sum);
Here's one possible approach: compute sum as the side-effect of an algorithm that terminates early. e.g.
int sum = 0;
auto f = std::find_if(keys.begin(), keys.end(), [&](string const &key) {
if (my_map.find(key) == my_map.end())
return true;
sum += my_map[key];
});
return f == keys.end() ? make_unique(sum) : nullptr;
Related
I have some operation that I would like to use with std::accumulate, but it may fail for some elements, in which case the accumulation should be aborted. With exceptions, I could throw an exception in case of failure, but I need to build without exceptions. With exceptions, this would look like this (the operation being greatly simplified):
std::optional<int> sum_accumulate_with_exceptions(
std::vector<int> const& aVec) {
try {
return std::accumulate(aVec.begin(), aVec.end(), 0,
[](int oldSum, int current) {
if (current > 42)
throw std::logic_error{"too large"};
return oldSum + current;
});
} catch (std::logic_error const&) {
return std::nullopt;
}
}
Actually, even with the possibility of using exceptions, this appears quite wasteful, as I am not interested in the particular exception thrown, and so the overhead of exceptions is unnecessarily large.
Using std::accumulate, I could use an error flag like this:
std::optional<int> sum_accumulate_without_exceptions(
std::vector<int> const& aVec) {
bool errored = false;
int res = std::accumulate(aVec.begin(), aVec.end(), 0,
[&errored](int oldSum, int current) {
if (errored) return 0;
if (current > 42) {
errored = true;
return 0;
}
return oldSum + current;
});
return errored ? std::optional<int>{} : res;
}
However, this is clearly bad, since this always iterates over the whole container, which might be large.
I came up with my own variant of std::accumulate:
template <typename It, typename T, typename Op>
std::optional<T> accumulate_shortcircuit(It aBegin, It aEnd, T aInit,
const Op& aOp) {
std::optional<T> res = std::move(aInit);
for (auto it = aBegin; it != aEnd; ++it) {
res = aOp(*res, *it);
if (!res) break;
}
return res;
}
This can be used nicely for the example case like this:
std::optional<int> sum_accumulate_shortcircuit(std::vector<int> const& aVec) {
return accumulate_shortcircuit(aVec.begin(), aVec.end(), 0,
[](int oldSum, int current) {
if (current > 42) {
return std::optional<int>{};
}
return std::optional<int>{oldSum + current};
});
}
However, I would prefer using std::accumulate (or any other standard library algorithm [edit:] or combination of them) itself, instead of using a replacement. Is there any way to achieve this?
While I was using C++17's std::optional in the example, ideally this would only use C++14 standard library algorithms, but I am also interested in solutions from newer/future standard versions.
[edit:] Based on #NathanOliver's answer, accumulate_shortcircuit could be implemented like this without having the range TS:
template <typename It, typename T, typename Op>
std::optional<T> accumulate_shortcircuit(It aBegin, It aEnd, T aInit,
const Op& aOp) {
std::optional<T> res = std::move(aInit);
std::all_of(aBegin, aEnd, [&](const T& element) {
return static_cast<bool>(res = aOp(*res, element));
});
return res;
}
You need an algorithm that has short circuiting built in. The first one that comes to to mind is std::any_of. You can use a lambda to do the sumation, and then return true to it once you've reached the point where you want to return. That would give you a function like
int sum_accumulate_shortcircuit(std::vector<int> const& aVec)
{
int sum = 0;
std::any_of(aVec.begin(), aVec.end(),
[&](auto elm) { if (elm > 42) return true; sum += elm; return false; });
return sum;
}
For future reference, this type of composition of algorithms/operations will be much easier in C++20 (with the inclusion of the ranges TS). This is an example from the current TS using accumulate and view::take_while:
auto sum = ranges::accumulate(my_vec | view::take_while([] (auto i) -> i <= 42), 0);
I am trying to solve the following question https://www.interviewbit.com/problems/largest-number/ : Given a list of non negative integers, arrange them such that they form the largest number.
For example:
Given [3, 30, 34, 5, 9], the largest formed number is 9534330.
Note: The result may be very large, so you need to return a string instead of an integer.
I have been able to solve it and implemented it, using comparison based sorting technique. That is, given two numbers X and Y, I compare two numbers XY (Y appended at the end of X) and YX (X appended at the end of Y). If XY is larger, then X should come before Y in output, else Y should come before. The following is the code:
string Solution::largestNumber(const vector<int> &A) {
// Do not write main() function.
// Do not read input, instead use the arguments to the function.
// Do not print the output, instead return values as specified
// Still have a doubt. Checkout www.interviewbit.com/pages/sample_codes/ for more details
vector<string> myvec;
for (int i = 0; i < A.size(); i++)
{
string s = to_string(A[i]);
myvec.push_back(s);
}
sort(myvec.begin(),myvec.end(),mycomp());
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
}
struct mycomp
{
inline bool operator() (const string &p1, const string &p2)
{
string s1 = p1.append(p2);
string s2 = p2.append(p1);
if (s1.compare(s2) < 0)
return false;
else
return true;
}
};
But, the problem is, I have to merge the two functions into a single one because I just have to implement the single function. I cannot define one more function since I have no control over the entire piece of code (look at the link's submission part). Therefore, my ask is, how can I use the comparator by defining it inside the function string Solution::largestNumber(const vector<int> &A). Thanks!
This is a perfect place for a lambda.
sort(myvec.begin(), myvec.end(), [](const string &p1, const string &p2) {
string s1(p1 + p2);
string s2(p2 + p1);
return s1.compare(s2) >= 0;
});
I changed your code to not call append() on the strings, since you accept them as references to const objects, and p1.append(p2) tries to modify p1, but that's not allowed on a const object. Further, avoid constructs like if(x) return true else return false; and instead just return x;
Also, this
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
Can be condensed to:
string s;
for (auto const& e : myvec)
s += e;
return s;
(Assuming you have a c++11 compiler or later)
I have some trouble to convert my function into a std::find_if lamnda.
below you can see my function
bool Room::ItemInRoomPresent(std::string & item)
{
bool isPresent = false;
for (std::vector<Item>::iterator i = m_RoomItems.begin(); i !=m_RoomItems.end(); i++)
{
if (i->GetName() == item)
{
item == i->GetName();
isPresent = true;
break;
}
}
return isPresent;
}
Can some one help me?
As noted in the comments, if you aren't interested in the position at which an item is found, just that there is at least one matching item anywhere in the sequence, you can use std::any_of and make this essentially a one-liner:
return std::any_of(m_RoomItems.begin(),
m_RoomItems.end(),
[&](Item const& x) { return x.GetName() == item; });
The passed lambda is identical to Kerrek's version but the return value can be used directly.
Like this:
auto it = std::find_if(m_RoomItems.begin(), m_RoomItems.end(),
[&](const Item& x) { return x.GetName() == item; });
return it != m_RoomItems.end();
(The statement item == i->GetName(); in your if statement has no effect, so I omitted it.)
I have used std::vector for making my algorithm. I would like to replace the vectors by linked lists.
In order to do so, I was thinking of using the std::list, but I have no idea how to do this, for example I have tried following example for finding a value within a vector/list:
void find_values_in_vector(const std::vector<int>& input_vector, int value, int &rv1, int &rv2)
{
if (input_vector[0] >= value) { // too small
rv1 = 0; rv2 = 0; return;
}
int index = (int)input_vector.size() - 1;
if (input_vector[index] <= value) { // too big
rv1 = index; rv2 = index; return;
}
// somewhere inside
index = 0;
while (input_vector[index] <= value) {
index++;
}
rv1 = index - 1; rv2 = index; return;
}
void find_values_in_list(const std::list<int>& input_list, int value, int &rv1, int &rv2)
{
if (*input_list.begin() >= value) { // too small
rv1 = 0; rv2 = 0; return;
}
if (*input_list.end() <= value) { // too big
rv1 = (int)input_list.size() - 1; rv2 = (int)input_list.size() - 1; return;
}
// somewhere inside
int index = 0; int temp = *input_list.begin();
while (temp <= value) {
temp = *input_list.next(); index++;
}
rv1 = index - 1; rv2 = index; return;
}
This seems not to work, as the member function next() is not existing. However I remember that browsing through a linked list is done by going to the beginning, and moving further to the next element until the a certain point is reached. I have seen that there is a way to get this done by using an interator in a for-loop, but I wonder what's wrong with my approach? I was under the impression that a std::list was a standard implementation of a double-directional linked list, or am I wrong and in that case, what std class is the implementation of a linked list (it does not need to be a double-directional linked list)?
The standard way to iterate through containers is like this:
for(std::list<int>::iterator it = input_list.begin();
it != input_list.end();
it++)
{
....
}
This also works for vectors,maps,deque,etc. The Iterator concept is consistently implemented throughout the STL so it's best to get used to this concepts.
There are also iterator operations like std::distance and std::advance etc. for the different types of iterators (I suggest you read up on them and their advantages/limitations)
If you have C++ 11 available you can also use this syntax (may not be useful for your problem though.)
for(const auto& value : input_list)
{
...
}
This also works throughout the STL container.
This should work for vector, list, deque, and set (assuming the contents are sorted).
template <class T>
void find_values_in_container(const T& container, int value, int &rv1, int &rv2)
{
rv1 = rv2 = 0; // Initialize
if (container.empty() || container.front() >= value)
{
return;
}
for (const auto& v : container)
{
rv2++;
if (v > value)
{
break;
}
rv1++;
}
return;
}
I have the following class (which obviously does not yet work as intended):
class A
{
private:
std::vector<int> firstVector, secondVector;
public:
std::vector<int>::iterator begin(){
return firstVector.begin();
}
std::vector<int>::iterator end(){
return secondVector.end();
}
};
How can I define an iterator which will go over the two member containers subsequently, e.g. after firstVector.end()-1 secondVector.begin() is returned and going all the way to secondVector.end() ?
Basically you need to define some custom iterator that internally checks for the end of the first range, then goes on to the next one.
However, this sort of stuff occurs a lot. Eventually you'd ask why an iterator for two vectors, why an iterator for two vectors, why a sequence of the same container type, and so on. Nir Tzachar & I have written a C++ port of Python itertools that does this sort of common stuff. In this case, you'd just use
chain(firstVector, secondVector)
It can be downloaded from this bitbucket repo.
Nothing to stop you from rolling your own. Can even make it random access!
struct chain_iterator
: std::iterator<std::random_access_iterator_tag, int>
{
using it = std::vector<int>::iterator;
std::pair<it, it> v1, v2;
bool first;
it cur;
};
We keep the initial iterator pairs so that we can do random access correctly.
Incrementing is what you'd expect:
chain_iterator& operator++() {
++cur;
if (first && cur == v1.second) {
first = false;
cur = v2.first;
}
return *this;
}
Dereference is trivial:
int& operator*() { return *cur; }
Advance has to do some extra checking:
chain_iterator& operator+=(size_t n) {
if (!first) {
// trivial case
cur += n;
}
else {
size_t d = v1.second - cur;
if (d < n) {
cur += n;
}
else {
first = false;
cur = v2.first + (d - n);
}
}
return *this;
}
I'll leave the rest of the operations as an exercise.
You could write your own function to do the incrementing:
std::vector<int>::iterator& inc(std::vector<int>::iterator& it) {
++it;
if (it == firstVector.end())
it = secondVector.begin();
return it;
}
This is also a good indication to others that the increment doesn't happen normally.