|Welcome to the Monastery|
You make an interesting observation, and it sounds like a documentation patch may be in order, as you suggest, liz.
The POD for exists states that 'exists' returns truth if a hash or array element has been initialized, even if its value is 'undef'.
So if the hash element's value is undefined, but the element exists, exists correctly detects the existance of an element.
defined can be a little tricky if you don't think it through. For existant keys with undef value, defined will return false, telling you that 'Second' is not defined. However, defined will also return false for elements that don't exist ('Third', for example). So defined is not the right way to check for the existance of an element, because it will return false for nonexistant elements and for existant elements with undef value. This is old news, but worth mentioning in such discussions as this.
Now for each (the real point to this followup): each knows which elements exist, even if the element's value is undef. So iterating over a hash with each will iterate over all existant elements, regardless of their value (or lack thereof). The POD says it's generally a bad idea to add or delete elements while iterating over a hash with each. But there is an exception though, which is also documented in the POD. Per the POD for each: It is always safe to delete the item most recently returned by each()...
It turns out this is useful. Consider the following code which will remove existing hash elements with undef value: