You're confusing $_ with the return from
shift.
shift will assume
@_ if it isn't given an argument, but it will not assign its return value to
$_. None of the functions will ever assign their return value to
@_ or
$_. If there is no-one to receive the value, the return value is discarded.
Now, the reason that foreach(@array) works the way you think it does is because that's a property of foreach. while does not have this property. That may have been the piece of info you were missing.