|No such thing as a small change|
Perhaps a small seemingly off topic diversion might make my point clearer. Consider the beginning of Louis Carroll's "The Jabberwocky":
Twas brillig, and the slithy toves Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe.
Even though almost none of the words will ever be found in the dictionary, my guess is that you know that "brillig" is an adjective, "toves" are some sort of animal or animate being, and "outgrabe" is a verb. You know all this because as a skilled English speaker your brain can use the structure of the sentence and the morphology of a word to guess its approximate meaning.
In the same way, an experienced programmer who has never read Perl before in their life might well guess that at the meaning of $#foo when he or she sees for (0..$#foo). He or she knows that "for" clauses generally have starting and ending boundary conditions. 0 looks like a starting condition. ".." is a common symbol for a series in both mathematics and English. By process of elimination $#foo is likely to be the final boundary condition.
However the ability to guess at the meaning is not quite the same thing as $#foo having inherent meaning on its own. Were one to encounter my $x=$#foo one would be lacking the structural hints and $#foo would be opaque in meaning if you didn't have prior knowledge. The # suggests it might have something to do with numbers, but there is nothing that says what number it refers to: first index, last index, count, or something else entirely? You'd have to look it up.
That same programming background can also trip people up if they assume too much. !@somearray only makes sense once you've got the notion of context determining data type. For someone coming from a computer language where that doesn't exist as a concept !@somearray just looks like nonsense or an always false statement. Negating an array makes no sense at all to someone coming from a VB background. For someone coming from C/C++ or Java, it would likely be interpreted as always false. @somearray is an array rather than a reference so presumably it "exists", i.e. already has an address reserved. If it has an address, how can it be "not" even if the array itself is completely empty? Zero-length is not the same as non-existent or null. Even if you argue that every piece of that chain of logic is completely wrong in Perl or even in C, you are only driving the point home further: for !@somearray to make sense you first have to think in a Perlish way. If you don't you are left confused trying to shoehorn it (unsuccessfully) into the concepts of other languages.
On the other hand, once you do understand Perl, the language holds together well and has good mnemonics. The "#" in $# is a good mnemonic for that "number thing" associated with an array, but it only helps once you already know the meaning and just need a little memory jog.