5 ms·
Not clear what you're suggesting here. Can you implement reject! using your propose more general interface? If so, how? Is it more efficient than just creati
by marvy 10y ago
Not clear what you're suggesting here. Can you implement reject! using your propose more general interface? If so, how? Is it more efficient than just creating a copy and then overwriting the original, like this?
ary = ary.reject {|x| x > 2}
If there's a language agnostic insight to be had here, I'd love to see it. Actually, even if it's language dependent, I'd like to see it.
- catnaroek 10y agoHave something akin to C++'s `std::copy_if`. auto first = vec.begin(); auto last = vec.end(); last = std::copy_if(first, last, first, [](int x) { return x > 2; }); auto size = std::distance(first, last); vec.resize(size); (Ironically, in C++ this doesn't always work, because not all types are copyable or movable. But Ruby has no such problems.)
- marvy 10y agoOkay, but with copy_if, you need an extra resize step at the end, which is annoying in C++ and that Ruby users currently have the luxury of avoiding. And this seems fundamental. The reason that copy_if can't call resize for you is that it assumes nothing about the underlying container; indeed there may not even be one. You say this is a good thing, and there are certainly advantages, but it doesn't seem to solve this problem, unless I'm missing something. Or maybe you're saying that the need to call resize is worth it, since you get a more general interface for the price?
- catnaroek 10y agoThe resize step at the end is a defect of how C++'s collections work. Ideally, I'd like to be able to say that there are two collections, possibly stored in the same buffer so long as they don't overlap: (0) A collection of elements to be processed (given by an InputIterator range). (1) A collection of already processed elements (given by an OutputIterator range). When the algorithm starts running, (1) is empty. With each iteration of the algorithm's loop, (0) becomes smaller and (1) becomes larger [maybe]. When the algorithm halts, (0) is empty. Notice that, while both collections share the same underlying buffer, each has its own size. So there is no need to perform a `resize()` step at the end.
- marvy 10y agoThat sounds super neat, and also super hard to specify and implement, in either C++ or Ruby.
- smitherfield 10y agoYou can simplify that code a bit, since the contents of a `std::vector` are guaranteed to be contiguous in C++03 and later, and taking a pointer offset is therefore well-defined and correct. const auto last{ std::copy_if(vec.cbegin(), vec.cend(), vec.begin(), [](const auto x) { return x > 2; }) }; vec.resize(last - vec.cbegin()); Edit: As someone mentioned below, C++ also provides `std::remove_if()`, for a more exact analogue to Ruby `Enumerable#reject`. http://en.cppreference.com/w/cpp/algorithm/remove http://en.cppreference.com/w/cpp/algorithm/remove const auto last{ std::remove_if(vec.cbegin(), vec.cend(), vec.begin(), [](const auto x) { return x <= 2; }) }; vec.resize(last - vec.cbegin());
- catnaroek 10y ago`std::remove_if` only takes two iterator arguments, and it's too ad hoc in exactly the same way Ruby's `reject!` is too ad hoc: it's the result of specializing `std::copy_if` to the case when the zeroth and second iterator arguments agree.
- smitherfield 10y agoMy mistake; I read about it (http://en.cppreference.com/w/cpp/algorithm/remove http://en.cppreference.com/w/cpp/algorithm/remove) too fast and thought it was the simple inverse of copy. I wasn't aware of it before this thread. In the unlikely event some future reader is copy-pasting these snippets, here's the corrected version: const auto last{ std::remove_if(vec.begin(), vec.end(), [](const auto x) { return x <= 2; }) }; vec.resize(last - vec.cbegin());
- marvy 10y agoremove_if is a convenience method; for full generality you can use remove_copy_if. I don't see how any of this allows you to achieve your idea of two independent containers backed by the same storage and thereby eliminate the extra step of calling resize.