True but personally don’t always think !any is clearer. And sometimes you have a long chain of iterator methods so adding ! way at the start is confusing.
Instead of adding
! at the start of a long chain, you can use the
Not trait and add
I personally think that
any(f).not() is sufficient.
Also, in terms of pure vocabulary, when reading
none(), I expect to somehow see an empty iterator.
I didn't know about
.not(). That does make things a bit more ergonomic I guess. I still don't think the readability is as good though.
I also don't think that just because you can combine two existing methods doesn't mean we shouldn't add this as a convenience. We have
.find_map(f) which is just
One could of course also use a local variable. That's very readable and everyone will understand what is going on.
Personally I like Ruby's style of offering logical duals consistently.
unless. Yes, one is the negation of the other, and there are a variety of ways to write code that does the same thing (negation, importing the
Not trait to use
.not() [although I think most folks may not be aware this is possible]), but I personally think writing
none is clearer (and less likely to do wrong) than the alternatives.
Good to know I'm not alone
I don't see Ruby as "consistent" in that sense, because there's no negation of
While I think it can be overdone (like
unless), I see a benefit in offering explicitly named operations in cases like
Iterator, because it's not a dual there,
none form a related triple.
It's a bit like having
reject instead of
filter, because just counting humanity's time wasted on figuring out whether the argument "filters things in" or "filters things out" makes it a good idea.
Looking at other language's, it seems to be pretty common to offer all three:
I'd say it's more common for languages to offer all three or no such operations at all; rather than two.
if you want to be complete, then you would need something that acts like
iter.any(f) == iter.none(f).not(); iter.none(f) == iter.any(f).not(); iter.all(f) == iter.foo(f).not(); iter.foo(f) == iter.all(f).not();
No, this is incorrect.
iter.all(f).not() should return true if the iterator may have some elements that pass the test, but not all of the elements pass the test.
iter.all(f) == iter.foo(f).not() == iter.none(|x| f(x).not()) == iter.any( |x| f(x).not()).not() iter.any(f) == iter.none(f).not() == iter.foo( |x| f(x).not()) == iter.all( |x| f(x).not()).not() iter.none(f) == iter.any(f).not() == iter.all( |x| f(x).not()) == iter.foo( |x| f(x).not()).not() iter.foo(f) == iter.all(f).not() == iter.any( |x| f(x).not()) == iter.none(|x| f(x).not()).not()
It's kind of like
any_false where the current
any is like
FWIW, one could replace these with their boolean equivalent:
all ~ and any ~ or none ~ nor foo ~ nand
Might even make sense to have these, next to
product for values.
Just as another data point of prior art, the C++ STL also has
I assume that the fourth variant was deemed to rare to deserve its own function.
Just to make it even more clear, here's a little working example showing the difference between
any and what I've called
any_not: Code on Playground
Teeechnically there's more:
|1||1||0||or||any / some|
Alright, here's an updated Playground example covering all the cases. Functions already provided are commented out. Two of the functions have an added
Copy bound which would be avoided in a real-world implementation, but which is placed here to simplify the example: Playground example
These implementations are not correct.
let v = vec![2, 1]; let r = v.iter().any_not_all(is_odd); assert_eq!(r, true); // panics let v = vec![1, 2]; let r = v.iter().all_or_none(is_odd); assert_eq!(r, false); // panics
That's because the
all methods consume items from the iterator, so when one returns early, the other only checks the remainder of the elements.
Normally that would be an argument in favour of inclusion, but… how often does one need
any_not_all, really? I don't see any good use case for it. And the others can already be succinctly expressed in terms of
!, or if you insist, Borat conditions. They aren't any more expressive, clear or readable than that, and I'd argue they are less: I'd be less confident reading code that freely interchanges those combinators, because I'd have to memorise them all to be sure they perform as they are named (hello,
#define FIVE 42). If they aren't there, not only there'd be less to memorise, but the logical structure of the condition would be much more apparent, making it easier to notice how code could be refactored.
Where is @H2CO3 when you need him?
(I know, the real @H2CO3 was the friends we made along the way… Wait, wrong cliché.)
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.