12
$\begingroup$

I am a bit amazed by this behavior of VectorQ[ expr, test ]:

VectorQ[ {}, NumberQ ]

True

It seems that the test itself is never applied and thus does not matter, so NumericQ or OddQ for example will also return True.

I find this behavior quite unfortunate, given that the documentation states:

VectorQ[ expr, NumberQ] tests whether expr is a vector of numbers.

One might also note that NumberQ[ Nothing ] or NumericQ[{}] return False and that NumberQ /@ {} returns {}.

Is this a bug or a feature (true to mathematics)?

$\endgroup$
  • 2
    $\begingroup$ I guess it is applying the test as many times as there are elements of the list. Since there are zero elements, it applies the test zero times. $\endgroup$ – Jason B. Mar 30 '16 at 11:41
  • $\begingroup$ @JasonB Yes, but then the description in the documentation is utterly misleading because the empty set/list is not a vector of numbers: FreeQ[ {}, e_ /; NumberQ[e] ] (* True *). $\endgroup$ – gwr Mar 30 '16 at 11:49
  • 2
    $\begingroup$ This is exactly as it has to be. VectorQ[list, test] will only return False when there is an element in list for which the test fails. For an empty list, such an element does not exist, so the result will be True. (For the same reason, the statement "all elements of the empty set are green" is true.) $\endgroup$ – Fred Simons Mar 30 '16 at 11:51
  • 2
    $\begingroup$ Relevant: And[] (* True *) and Or[] (* False *) $\endgroup$ – Martin Ender Mar 30 '16 at 11:54
  • 2
    $\begingroup$ @gwr I think it's valid in the sense that it's an empty vector of numbers. In other words, VectorQ tests whether your input is in $\mathbb{C}^n$, where $n$ is a non-negative integer. It's implementation seems to be essentially, ListQ @ expr && And @@ test /@ expr, which definitely makes the result (mathematically) consistent. You're right though that the documentation could be a bit clearer about that. $\endgroup$ – Martin Ender Mar 30 '16 at 12:40
19
$\begingroup$

Others have argued in the comments that this behaviour makes sense mathematically, and I fully agree. But further than that, it is also very practical.

Mathematica's functions are usually designed to give reasonable results for edge cases in the sense that if you put these functions together and write some more complex calculation, this compound function will also give sensible results for edge cases, without having to handle them manually. What is a sensible result of course depends on the particular application. In some cases it is unavoidable to manually handle edge cases. But generally we want a behaviour which is practical in most common applications.

VectorQ is often used for checking function arguments. For example,

f[vec_?(VectorQ[#, NumericQ]&)] := vec + 1

Functions which operate on lists of numbers can usually be generalized for the empty list. It would be very annoying if the empty list had to be handled manually.

This f function returns a new list where each element is incremented by 1. If it works at all for an empty list, the only sensible result is another empty list. This works automatically.

This sort of behaviour is common in Mathematica. For example, Total[{}] gives 0 and so does Sum[k, {k,{}}]; Max[{}] gives -Infinity; {}*3 gives {}; and so on. All these could alternatively throw errors, but that behaviour would not be as useful as the current one. To be consistent with these, VectorQ[{}, False&] must be True. These choices are not suitable for every application, but they try to be useful for most applications. For the rest, we just need to handle edge cases manually.

I think that this behaviour is a very useful and practical feature from a programmer's point of view (even if we ignore mathematics completely). It reduces the number of situations where edge cases need to be considered separately, so we can spend less time on coding.

$\endgroup$
  • $\begingroup$ Great answer, very well articulated. I am glad I did not write my own. +1. $\endgroup$ – Leonid Shifrin Mar 30 '16 at 13:32
  • $\begingroup$ This is a very reasonable and balanced answer, making it an easy accept. Such clarity would nicely become the documentation, which imo here is wanting. Note how And[] and Or[] are "hidden" in examples while a clear design principle is being followed. $\endgroup$ – gwr Mar 30 '16 at 19:57
  • 5
    $\begingroup$ Yes. Zero is a perfectly good number, and a zero length vector is a perfectly good vector. One minor instance where Mathematica falls down here is Transpose. Its model of tensor algebra does not distinguish between row and column vectors (good), but then sometimes I want to define a function that works on matrices, vectors, or scalars. I find that these things would generalize properly if Transpose of a vector or scalar simply yielded the vector or scalar. Of course, this isn't hard to fix with a custom function. $\endgroup$ – John Doty Mar 30 '16 at 21:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.