|
Only because you agree with their logic. Not everyone does. The ambiguity is the issue, not whether you or I agree with the decision.
|
|
|
|
|
The trouble is that you're explaining the logic of the implementation, whereas the question concerns the logic of the outcome. The outcomes of .Any() and .All() on empty collections are logically inconsistent. Logically the answer to whether anything in an empty collection or everything in the same collection meet some criterion is "No" in both cases. Similarly a null exception is absurd.
My guess is that whoever coded and reviewed one of the two methods didn't first grasp the behaviour of the other. It happens a lot, in my experience.
|
|
|
|
|
Exactly. A lack of communication.
The specs for the methods should have specified what the result should be for an empty set -- probably false.
|
|
|
|
|
Peter Moore - Chicago wrote: If I look at an empty room and ask "Are all the people in there aliens?" the answer I expect is apparently YES?
Maybe MS's approach is, "nobody said no".
|
|
|
|
|
I'm with MS on this one: All is basically saying "If any of the people in this room do not match this condition return false"
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
There are certainly some odd decisions in the .NET class libraries. But I don't agree with you on this one. From a purely logical perspective, checking whether all members of an empty set satisfy a particular condition should always return true - there are no members which don't satisfy the condition, so returning false would be senseless.
If you want an example of a nonsensical decision, look no further than the System.Text.Json.JsonElement 's TryGet... methods, which will throw an exception if the "JSON type" of the element is wrong.
So TryGetInt64 will return true for { "id": 42 } ; return false for { "id": 3.1415 } ; and throw an exception for { "id": "42" } .
Given the usual TryParse pattern, you might expect these methods to return false for any invalid input. But that's not what they do. They return false for some kinds of invalid input, and throw an exception for other kinds of invalid input.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Would you think otherwise if the method were called Every ?
|
|
|
|
|
No.
Maybe its a mathematical mindset. "Is this condition true for every member of this (empty) set?" has to return true , since there are no members of the set where the condition is not true.
Similarly, "Is this condition true for any member of this (empty) set?" has to return false , since there are no members of the set where the condition is true.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I don't have a horse in this race, but I agree with your logic.
>64
It’s weird being the same age as old people. Live every day like it is your last; one day, it will be.
|
|
|
|
|
You're not wrong.
But you're not right either.
Maths vs. Words
|
|
|
|
|
Perhaps you should be programming in Q#[^]?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
|
So if you asked your sergeant if your platoon was all present and correct when in fact they'd all been blown to smithereens and the platoon was now devoid of soldiers, you would still expect the answer "Yes"?
|
|
|
|
|
In your example, the sergeant is probably asking about the set of soldiers assigned to the platoon (regardless of status). In which case, the set isn't empty, and the answer is "no".
However, if the sergeant was asking if all soldiers in your platoon who were capable of showing up were there, then the answer is "yes". Note: technically, the set still isn't empty, as you are presumably in your own platoon. However, the logic would still apply if the question was asked about another platoon where no one was left alive - everyone who was capable of showing up would be there...
|
|
|
|
|
The officer doing the asking is enquiring about the present state of his platoon, not what it might have been. He wants to know how many men he has left in that platoon, if any (he's a practical chap). The Sergeant isn't necessarily a member of the platoon in question.
It's a valid question before possibly sending the platoon out on another patrol, one which requires a practical answer. A platoon now devoid of soldiers wouldn't perform well on patrol.
|
|
|
|
|
Exactly! The officer needs to know how many men are available (a platoon of one wouldn't perform well either). He could have asked, "How many are available?" But he didn't (in this example). Presumably he can see there's no one there, so he effectively asked, "Is this everyone in the platoon?" The correct answer at that point is "Yes", at which point he knows what he has to work with (even if it's zero).
To answer No at this point would imply that there are others who are not present, which isn't the case.
If we were to write that in code, it would be Platoon.AreAllPresent(), and the result would be True as long as no members of the Platoon are absent (even if there are no members of the Platoon).
|
|
|
|
|
Here's a more obscure one.
HashAlgorithm.TransformBlock is for computing things like MD5 and SHA hashes on streamable data. TransformFinalBlock needs to be called at the end, even if there's no data left to include in the hash. Notably you have to give it both an array AND the length.
You can give it an empty byte[] and specify it has 0 length, and it's perfectly happy.
But give it a null buffer - even if you specify 0 length! - and it throws on you. I'm explicitly telling it I have no data to give it, but I still have to give it the empty array, for reasons.
|
|
|
|
|
In logic, ALL(P(x)) would be equivalent to NOT ANY(NOT P(x)) . So if you agree on that [Empty].Any(...) would always be false, then it logically follows that [Empty].All(...) would be true.
|
|
|
|
|
Yes, we have no bananas....
how about malloc( 0 ) not returning NULL.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
That was interesting.
Apparently that is undefined so implementation dependent.
I wonder why. I suspect some memory allocators would have trouble with allocating an empty block so perhaps that is the reason to allow null.
What is additionally interesting was that I was thinking it would return just a pointer to the heap block tracker. That is so free still works.
But of course it could actually return real space. So for example if the allocator just always sizes up to a block (say 16 bytes) it might be valid space.
|
|
|
|
|
I brought it up a while back and there was some back and forth on it.
Apparently, malloc( 0 ) not returning NULL is deliberate.
Here is what GCC does with some short bit of code
printf("Hello world!\n");
printf( "call malloc(0)\n" );
sz = (char*)malloc(ZERO);
printf( "Errno %d\n", errno );
if( sz == NULL ) printf( "returned NULL allocated zero bytes\n");
else printf( "NULL not returned from malloc, allocated 8 bytes\n" );
Hello world!
call malloc(0)
Errno 0
NULL not returned from malloc, allocated 8 bytes
Apparently this is allowed by GCC.
The argument is that it was successful
Haven't tried it in VS.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
jmaida wrote: Apparently, malloc( 0 ) not returning NULL is deliberate.
Standard says either is allowed.
jmaida wrote: allocated 8 bytes
Without the actual standard, which costs money, I suspect the following is authoratative enough.
MEM04-C. Beware of zero-length allocations - SEI CERT C Coding Standard - Confluence[^]
Keep in mind that without that reference I thought it might have been undefined. Which means it could throw a system exception too. So probably better the way it is.
modified 1-Mar-24 18:14pm.
|
|
|
|
|
I agree. I have seen this cert referenced before.
I wrap malloc with a check for <1 memory request argument and return NULL and an error message.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
To add to this, what universality (All) is really saying is that "nothing exists that violates this constraint", whereas existentiality (Any) is really saying "at least one thing exists that obeys this constraint."
With [].All(constraint), nothing exists to violate the constraint, so All() returns true.
With [].Any(constraint), nothing exists to obey the constraint, so Any() returns false.
|
|
|
|
|
I agree. I find the behavior of All() on an empty collection strange.
If I'm told "all the members of a list of integers are greater than zero" I would expect Any(p => p > 0) on that list to return true . But if the list is empty, Any(p => p > 0) returns false . That seems wrong.
/ravi
|
|
|
|