6 ms·
> After instructions, participants were given printouts of sample code they could refer to while solving tasks. Group Lambda got code of a C++ program using lam
by porges 10y ago
> After instructions, participants were given printouts of sample code they could refer to while solving tasks. Group Lambda got code of a C++ program using lambda expressions and group Iterator received code of the same program written using iterators. They then had time to study the samples before starting the tasks and could refer to these samples later.
These samples do not appear in the paper, so we don't know what they saw.
The “iterators” discussed are Java-/C#-style iterators, not C++ ones (as I expected reading the abstract).
In a C++ context I would have expected lambdas vs iterators to be something like:
// lambda
float retVal = 0;
std::for_each(mb.cbegin(), mb.cend(), [&](item x) { retVal += item.price; });
return retVal;
// pure iterator
float retVal = 0;
for (auto it = mb.cbegin(); it != mb.cend(); ++it)
{
retVal += it->price;
}
return retVal;
... and the first would be better off as:
return std::accumulate(mb.cbegin(), mb.cend(), 0f,
[](float acc, item x) { return acc + x.price; });
I think the need to use ref-capture (since you only get a side-effecting `std::function` to play with in their sample) would be the thing most likely to throw people off – as it’s something that should be avoided in most code, anyway ;)
- MaulingMonkey 10y agoYeah - from what I can see, neither interface looks like idiomatic C++. EDIT: Looks like you beat me to the punch on some of these ;) Instead of: float getSum(marketBasket mb) { float retVal = 0; // Implement solution here // --------- marketBasket::iterator iter = mb.begin(); while (iter.hasNext()) { retVal += iter.get().price; iter.next(); } // --------- return retVal; } I'd rather see real SC++L compatible iterators (as hopefully taught) and saner naming: float getSum(marketBasket market) { float sum = 0; // Implement solution here // --------- for (marketBasket::iterator item = market.begin(); item != market.end(); ++item) { sum += item->price; } // --------- return sum; } And instead of being pre-provided with a function <void(item)>, if I'm reading the pdf correctly: float getSum(marketBasket mb) { float retVal = 0; // Implement solution here // --------- function <void(item)> func = [&](item theItem) { retVal += theItem.price; }; mb.iterateOverItems(func); // --------- return retVal; } I'd rather see: float getSum(marketBasket market) { float sum = 0; // Implement solution here // --------- market.for_each([&](item theItem) { sum += theItem.price; }); // --------- return sum; } Or venturing into the far more functional style, where lambdas start to shine for me, personally: float getSum(std::vector<item> market) { // Implement solution here // --------- return std::accumulate(market.begin(), market.end(), 0.0f, [&](float sum, item theItem) { return sum + theItem.price; }); // --------- } It looks a bit better in C# where your selection of standard functions is a little less anemic and a little nicer to use: float GetSum(MarketBasket market) { // Implement solution here // --------- return market.Sum(item => item.Price); // --------- }
- deleted 10y ago[deleted]
- duneroadrunner 10y ago"Idiomatic" doesn't necessarily mean better. I think objectively it's hard to argue that "item != market.end()" is superior to "iter.hasNext()". The latter accurately reflects the programmer's intent, while the former specifies an unnecessarily specific (and poor) implementation of the intent. First of all, using something like "market.cend() != const_iter" instead is arguably better practice (imagine you unintentionally omit the "!"). But programmers shouldn't need to consider whether the iterator is const or not when they just want to know if the loop is done. Also, consider the case where the vector is being modified (items inserted or deleted) inside the loop. It might be problematic either way, but "item != market.end()" is particularly bad in that situation. Shameless plug: http://duneroadrunner.github.io/SaferCPlusPlus/#msevector http://duneroadrunner.github.io/SaferCPlusPlus/#msevector
- lorenzhs 10y ago> But programmers shouldn't need to consider whether the iterator is const or not when they just want to know if the loop is done. and they don't: http://en.cppreference.com/w/cpp/container/vector/end http://en.cppreference.com/w/cpp/container/vector/end - there's an overload returning a const_iterator. You don't need to use 'cend'. And since insertion and deletion potentially invalidate iterators, 'hasNext()' is just as bad.
- duneroadrunner 10y agoYeah, maybe I didn't think the const thing through. I guess I was thinking that the benefit of using cend() is the double-check to make sure the iterator was declared as a const_iterator (when appropriate). But using "hasNext()", just like using "end()", you would lose the double-check. Perhaps I could instead claim that "item != market.end()" redundantly specifies the vector, "market", and thus presents an unnecessary opportunity for mistakes? For example: std::vector<double> x_coords; std::vector<double> y_coords; ... for (auto y_iter = y_coords.begin(); y_iter != y_coords.end(); y_iter++) { for (auto x_iter = x_coords.begin(); x_iter != y_coords.end(); x_iter++) { ... } } Notice the "x_iter != y_coords.end()" bug. I assume this will usually trip an assert if it is encountered in debug mode, but not in release mode. Of course you could just as easily mix up "x_iter.hasNext()" with "y_iter.hasNext()", but the removal of redundancy means one less opportunity for a potential mistake. Right? Hmm, I guess that's really an argument for losing the iterators altogether, which I guess was kind of the point of the study. So then wrt iterator invalidation, I have a question. Consider this contrived scenario: for (auto y_iter = y_coords.begin(); y_iter != y_coords.end(); y_iter++) { if (5 == std::distance(y_coords.begin(), y_iter)) { y_coords.resize(3); } } With conventional implementations of std::vector, the "y_coords.resize(3)" will presumably "invalidate" y_iter. And the "y_iter != y_coords.end()" will result in undefined behavior. But you could imagine "safer" implementations of vector<> that would instead throw an exception (or terminate or whatever). (Or you could actually download one of them at the link I gave.) So the question is, if this "safer" implementation supported "y_iter.hasNext()", would it be better for it to throw an exception (or whatever) in this case, or just return false?
- Karliss 10y agoFirst two tests used lambda with custom for each method. If authors used real c++ iterators and for each loop it would become obvious that comparing iterators and lambdas at iterating is the same as comparing lambdas with if statements at being if statements.
- deleted 10y ago[deleted]
- 72deluxe 10y agoI agree that the "iterators" in the paper are not C++ iterators as found in the STL or language spec.
- deleted 10y ago[deleted]