Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I am trying to find an element in an array/list with some strict conditions. I try to find the element again with relaxed conditions if none of the elements satisfy the strict conditions.
for( ele : list) {
if(con1 && cond2 && cond3) {
return ele;
}
}
for( ele : list) {
if(con1 && cond2) {
return ele;
}
}
.....
Should I add a for loop with relaxed conditions each time? Is there a better way?
Better means less coding and good code readability.
Basic answer
I guess this all depends heavily on how many conditions there are and how complex they are. If it's only three separate conditions then yes, I would write something similar to your code. It's simple and easy to read and maintain.
In more complex situations
If there are many more conditions (like 10 or so) or you anticipate there will be more that needs to be added, you might consider using lambdas together with a vector instead:
// sample conditions for integers. Most strict condition is
// an even integer greater than 2 and less than 10
const std::vector<std::function<bool(int)>> conditions{
[](int elem) {return elem < 10;},
[](int elem) {return elem > 2;},
[](int elem) {return elem % 2 == 0;}
// add as many more conditions you wish...
// top condition is the one that will be relaxed first
};
// This will bit-by bit relax the conditions by ignoring more and more of them
auto start_iterator = conditions.begin();
while (start_iterator != conditions.end())
{
for (const auto& elem : list)
{
bool fulfills_all = std::all_of(start_iterator, conditions.end(), [] (std::function<bool(int)> cond) {
// for each active condition, test the condition on the element
return cond(elem);
});
if (fulfills_all)
return elem;
}
// If we get here, that means no element fulfilled all the conditions.
// Relax them by ignoring one more
start_iterator++;
}
// If we reach this point, then no element fulfilled even the most relaxed condition
Haven't really tested this, so some syntax may be a bit rusty but the general idea of it should work. The use of lambdas in std::function wrappers in a vector allow for a large number of conditions only having to be coded once, and std::all_of allows us to iterate over many conditions and verify that each of them are fulfilled for a certain element.
This requires the <functional> and <algorithm> headers.
If you are unfamiliar with std::function or std::any_of then these are helpful sites:
http://en.cppreference.com/w/cpp/utility/functional/function
http://en.cppreference.com/w/cpp/algorithm/all_any_none_of
Your description is pretty ambiguous, but I suspect you're looking for something like (incomplete pseudo-code).
// assuming the relaxed conditions are a subset of the strict conditions
whatever_type relaxed_return_value;
bool relaxed_found = false;
for (auto &element : some_list)
{
if (cond1 && cond2) // check relaxed conditions first
{
if (cond3) // check remaining strict conditions
{
return element; // return immediately on matching strict conditions
}
else if (!relaxed_found)
{
relaxed_return_value = element;
relaxed_found = true;
}
}
}
if (relaxed_found)
return relaxed_return_value;
else
indicate_no_value_found();
The above returns immediately if an element is found that matches the strict conditions, otherwise keeps going and keeps track of the first element that matches the relaxed conditions. If the loop completes, there has been no element matching the strict conditions, and the first result matching relaxed conditions (if any) is identified.
If there is a value of whatever_type (i.e. of an element) that can indicate no data (e.g. it's a pointer and NULL indicates it points at nothing) the logic can be simplified from the above (e.g. no need for bool value to keep track of whether a result has been found for relaxed criteria).
As to whether this is "better", that is completely subjective. Main advantage of this approach over two loops is that no tests will be repeated if no elements meet strict requirements. The downside is additional book-keeping to keep track of the first match of relaxed tests until all elements have been checked against strict criteria.
Related
I'm writing a piece of C++ that checks to see whether particular elements of a vector return true, and uses remove_if() to remove them if not. After this, I use vector.size() to check to see if there are any elements remaining in the vector, and then return the function if not.
At the moment, I do vector.erase() after remove_if(), as it doesn't actually reduce the size of the vector. However, this code needs to run fast, and recursively changing the size of a vector in memory is probably not ideal. However, returning if the vector is zero (instead of running the rest of the function) probably also saves time.
Is there a nice way to check how many elements remain in the vector without erasing?
here's the code:
auto remove = remove_if(sight.begin(), sight.end(), [](const Glance *a) {
return a->occupied;
});
sight.erase(remove, sight.end());
if (sight.size() == 0) {
// There's nowhere to move
return;
}
EDIT:
thanks for the help + the guidance. From the answers it's clear that the wording of the question isn't quite correct: erase() doesn't change the size of the vector in memory, but the capacity. I had mis-remembered the explanation from this post, which nicely articulates why erase() is slower than remove() for multiple removals (as you have to copy the location of the elements in the vector multiple times).
I used Instruments to benchmark the code I had originally against Johannes' suggestion, and the difference was marginal, though Johannes' was consistently slightly faster (~9.8% weight vs ~8.3% weight for the same code otherwise). The linked article should explain why. ✨
You can use std::distance(sight.begin(), remove); to get the number of remaining elements:
auto remove = remove_if(sight.begin(), sight.end(), [](const Glance *a) {
return a->occupied;
});
size_t remaining = std::distance(sight.begin(), remove);
if (remaining == 0) {
// There's nowhere to move
return;
}
But if you are only interested in 0 you can do:
auto remove = remove_if(sight.begin(), sight.end(), [](const Glance *a) {
return a->occupied;
});
if (remove == sight.begin()) {
// There's nowhere to move
return;
}
Instead of erasing elements that satisfy a criteria and then checking the number of remaining elements just to find out how many elements did not satisfy the criteria, you could simply iterate the container and count those elements. The standard library has an algorithm for that: std::count_if.
I have a C++11 list of complex elements that are defined by a structure node_info. A node_info element, in particular, contains a field time and is inserted into the list in an ordered fashion according to its time field value. That is, the list contains various node_info elements that are time ordered. I want to remove from this list all the nodes that verify some specific condition specified by coincidence_detect, which I am currently implementing as a predicate for a remove_if operation.
Since my list can be very large (order of 100k -- 10M elements), and for the way I am building my list this coincidence_detect condition is only verified by few (thousands) elements closer to the "lower" end of the list -- that is the one that contains elements whose time value is less than some t_xv, I thought that to improve speed of my code I don't need to run remove_if through the whole list, but just restrict it to all those elements in the list whose time < t_xv.
remove_if() though does not seem however to allow the user to control up to which point I can iterate through the list.
My current code.
The list elements:
struct node_info {
char *type = "x";
int ID = -1;
double time = 0.0;
bool spk = true;
};
The predicate/condition for remove_if:
// Remove all events occurring at t_event
class coincident_events {
double t_event; // Event time
bool spk; // Spike condition
public:
coincident_events(double time,bool spk_) : t_event(time), spk(spk_){}
bool operator()(node_info node_event){
return ((node_event.time==t_event)&&(node_event.spk==spk)&&(strcmp(node_event.type,"x")!=0));
}
};
The actual removing from the list:
void remove_from_list(double t_event, bool spk_){
// Remove all events occurring at t_event
coincident_events coincidence(t_event,spk_);
event_heap.remove_if(coincidence);
}
Pseudo main:
int main(){
// My list
std::list<node_info> event_heap;
...
// Populate list with elements with random time values, yet ordered in ascending order
...
remove_from_list(0.5, true);
return 1;
}
It seems that remove_if may not be ideal in this context. Should I consider instead instantiating an iterator and run an explicit for cycle as suggested for example in this post?
It seems that remove_if may not be ideal in this context. Should I consider instead instantiating an iterator and run an explicit for loop?
Yes and yes. Don't fight to use code that is preventing you from reaching your goals. Keep it simple. Loops are nothing to be ashamed of in C++.
First thing, comparing double exactly is not a good idea as you are subject to floating point errors.
You could always search the point up to where you want to do a search using lower_bound (I assume you list is properly sorted).
The you could use free function algorithm std::remove_if followed by std::erase to remove items between the iterator returned by remove_if and the one returned by lower_bound.
However, doing that you would do multiple passes in the data and you would move nodes so it would affect performance.
See also: https://en.cppreference.com/w/cpp/algorithm/remove
So in the end, it is probably preferable to do you own loop on the whole container and for each each check if it need to be removed. If not, then check if you should break out of the loop.
for (auto it = event_heap.begin(); it != event_heap.end(); )
{
if (coincidence(*it))
{
auto itErase = it;
++it;
event_heap.erase(itErase)
}
else if (it->time < t_xv)
{
++it;
}
else
{
break;
}
}
As you can see, code can easily become quite long for something that should be simple. Thus, if you need to do that kind of algorithm often, consider writing you own generic algorithm.
Also, in practice you might not need to do a complete search for the end using the first solution if you process you data in increasing time order.
Finally, you might consider using an std::set instead. It could lead to simpler and more optimized code.
Thanks. I used your comments and came up with this solution, which seemingly increases speed by a factor of 5-to-10.
void remove_from_list(double t_event,bool spk_){
coincident_events coincidence(t_event,spk_);
for(auto it=event_heap.begin();it!=event_heap.end();){
if(t_event>=it->time){
if(coincidence(*it)) {
it = event_heap.erase(it);
}
else
++it;
}
else
break;
}
}
The idea to make erase return it (as already ++it) was suggested by this other post. Note that in this implementation I am actually erasing all list elements up to t_event value (meaning, I pass whatever I want for t_xv).
Will std::remove_if always call the predicate on each element in order (according to the iterator's order) or could it be called out of order?
Here is a toy example of what I would like to do:
void processVector(std::vector<int> values)
{
values.erase(std::remove_if(values.begin(), values.end(), [](int v)
{
if (v % 2 == 0)
{
std::cout << v << "\n";
return true;
}
return false;
}));
}
I need to process and remove all elements of a vector that meet certain criteria, and erase + remove_if seems perfect for that. However, the processing I will do has side effects, and I need to make sure that processing happens in order (in the toy example, suppose that I want to print the values in the order they appear in the original vector).
Is it safe to assume that my predicate will be called on each item in order?
I assume that C++17's execution policies would disambiguate this, but since C++17 isn't out yet that obviously doesn't help me.
Edit: Also, is this a good idea? Or is there a better way to accomplish this?
The standard makes no guarantees on the order of calling the predicate.
What you ought to use is stable_partition. You partition the sequence based on your predicate. Then you can walk the partitioned sequence to perform whatever "side effect" you wanted to do, since stable_partition ensures the relative order of both sets of data. Then you can erase the elements from the vector.
stable_partition has to be used here because erase_if leaves the contents of the "erased" elements undefined.
In code:
void processVector(std::vector<int> &values)
{
auto it = std::stable_partition(begin(values), end(values), [](int v) {return v % 2 != 0;});
std::for_each(it, end(values), [](int v) {std::cout << v << "\n";});
values.erase(it, end(values));
}
A bit late to the party, but here's my take:
While the order is not specified, it will involve jumping through hoops to implement an order different from first-to-last, due to the following:
The complexity is specified to be "exactly std::distance(first, last) applications of the predicate", which requires visiting each element exactly once.
The iterators are ForwardIterators, which means that they can only be incremented.
[C++17 and above] To prevent parallel processing, one can use the version that accepts an execution policy, and pass std::execution::seq.
Given the above, I believe that a (non-parallel) implementation that follows a different order will be convoluted and have no advantages over the straightforward case.
Source: https://en.cppreference.com/w/cpp/algorithm/remove
They should be processed in order, but it is not guaranteed.
std::remove_if() moves "removed" items to the end of the container, they are not actually removed from the container until erase() is called. Both operations will potentially invalidate existing iterators in a std::vector.
I am sorry if the title isn't very descriptive, I was having a hard time figuring out how to name this question. This is pretty much the first time I need to use a set, though I've been using maps forever.
I don't think it is possible, but I need to ask. I would like to perform a specific action on a struct when I add it to my std::set, but only if equality is true.
For example, I can use a list and then sort() and unique() the list. In my predicate, I can do as I wish, since I will get the result if 2 values are equal.
Here is a quick example of what my list predicate looks like:
bool markovWeightOrdering (unique_ptr<Word>& w1, unique_ptr<Word>& w2) {
if (w1->word_ == w2->word_) {
w1->weight_++;
return true;
}
return false;
}
Does anyone have an idea how to achieve a similar result, while using a std::set for the obvious gain in performance (and simplicity), since my container needs to be unique anyways? Thank you for any help or guidance, it is much appreciated.
element in set are immutable, so you cannot modify them.
if you use set with pointer (or similar), the pointed object may be modified (but care to not modify the order). std::set::insert returns a pair with iterator and a boolean to tell if element has been inserted, so you may do something like:
auto p = s.insert(make_unique<Word>("test"));
if (p.second == false) {
(*p.first)->weight += 1;
}
Live example
Manipulating a compare operator is likely a bad idea.
You might use a std::set with a predicate, instead:
struct LessWord
{
bool operator () (const std::unique_ptr<Word>& w1, const std::unique_ptr<Word>& w2) {
return w1->key < w2->key;
}
};
typedef std::set<std::unique_ptr<Word>, LessWord> word_set;
Than you test at insert if the word is existing and increment the weight:
word_set words;
std::unique_ptr<Word> word_ptr;
auto insert = words.insert(word_ptr);
if( ! insert.second)
++(insert.first->get()->weight_);
Note: Doing this is breaking const correctness, logically. A set element is immutable, but the unique_ptr enables modifications (even a fatal modification of key values).
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
Why there is a need of 3 different loops : "while", "do-while", and "for" to exist in c/c++, especially when each of them gives you power to do almost anything that the other 2 can do? Other languages lack one or the other.
Is it just for ease of use or to make the code look better in cases, or are there any special purposes that are served by any one of them specifically that can't be accomplished so easily with the other two? If yes, then please mention.
P.S. - In general, do a language support many iteration syntax just to enhance readability?
It's not just readability, it's also the closely-related but distinct maintainability, and concision, and scoping (esp. for files, locks, smart pointers etc.), and performance....
If we consider the for loop, it:
allows some variables to be defined - in the for loop's own scope - and initialised,
tests a control expression before entering the loop each time (including the first), and
has a statement that gets executed after each iteration and before re-testing the control expression, assuming no break/return/throw/exit/failed assert etc., and regardless of whether the last statement in the body executed or whether a continue statement executed; this statement is traditionally reserved for logically "advancing" some state "through" the processing, such that the next test of the control expression is meaningful.
That's very flexible and given the utility of more localised scopes to ensure earlier destructor invocation, can help ensure locks, files, memory etc. are released as early as possible - implicitly when leaving the loop.
If we consider a while loop...
while (expression to test)
...
...it's functionally exactly equivalent to...
for ( ; expression to test; )
...
...but, it also implies to the programmer that there are no control variables that should be local to the loop, and that either the control "expression to test" inherently "progresses" through a finite number of iterations, loops forever if the test expression is hardcoded true, or more complicated management of "progress" had to bed itself controlled and coordinated by the statements the while controls.
In other words, a programmer seeing while is automatically aware that they need to study the control expression more carefully, then possibly look more widely at both the surrounding scope/function and the contained statements, to understand the loop behaviour.
So, do-while? Well, writing code like this is painful and less efficient:
bool first_time = true;
while (first_time || ...)
{
first_time = false;
...
}
// oops... first_time still hanging around...
...compared to...
do
...
while (...);
Examples
While loop:
int i = 23;
while (i < 99)
{
if (f(i)) { ++i; continue; }
if (g(i)) break;
++i;
}
// oops... i is hanging around
For loop:
for (int i = 23; i < 99; ++i)
{
if (f(i)) continue;
if (g(i)) break;
}
Well, C++ has goto and you can use it to implement all three loops, but it doesn't mean that they should be removed. Actually it just increases readability. Of course you could implement any of them yourself.
Some loops are easiest to write using for, some are easiest to write using while, and some are easiest to write using do-while. So the language provides all three.
We have things things like the += operator for the same reason; += doesn't do anything that you can't do with plain +, but using it (where appropriate) can make your code a bit more readable.
In general, when presented with different language constructs that accomplish similar purposes, you should choose the one that more clearly communicates the intended purpose of the code you are writing. It is a benefit that C provides four distinct structured iteration devices to use, as it provides a high chance you can clearly communicate the intended purpose.
for ( initialization ; condition ; iteration-step ) body
This form communicates how the loop will start, how it will adjust things for the next iteration, and what is the condition to stay within the loop. This construct lends itself naturally for doing something N times.
for (int i = 0; i < N; ++i) {
/* ... */
}
while ( condition ) body
This form communicates simply that you wish to continue to perform the loop while the condition remains true. For loops where the iteration-step is implicit to the way the loop works, it can be a more natural way to communicate the intention of the code:
while (std::cin >> word) {
/* ... */
}
do body while ( condition )
This form communicates that the loop body will execute at least once, and then continues while the condition remains true. This is useful for situations where you have already determined that you need to execute the body, so you avoid a redundant looking test.
if (count > 0) {
do {
/* ... */
} while (--count > 0);
} else {
puts("nothing to do");
}
The fourth iteration device is ... recursion!
Recursion is another form of iteration that expresses that the same function can be used to work on a smaller part of the original problem. It is a natural way to express a divide and conquer strategy to a problem (like binary searching, or sorting), or to work on data structures that self-referential (such as lists or trees).
struct node {
struct node *next;
char name[32];
char info[256];
};
struct node * find (struct node *list, char *name)
{
if (list == NULL || strcmp(name, list->name) == 0) {
return list;
}
return find(list->next, name);
}