9
$\begingroup$

Why is it that pattern test works only with pure functions?

Cases[Range[0, 70], _?(Divisible[#, 7] &)]

Cases[Range[0, 70], x_?(Divisible[x, 7])]

I understand that in the second case /; should be used but why does ? fail in technical sense?

$\endgroup$
8
  • 3
    $\begingroup$ Because to each element, say 10, you apply a test of the form Divisible[x, 7][10], with x not being bound to anything (or, being bound to some global value, if x had a global value). It just doesn't make sense. PatternTest expects a function, that would take your agrument and evaluate to True or False (with anything else than True interpreted by a pattern-matcher as False), while you provide to it some expression, which doesn't behave like a function. $\endgroup$ Sep 8, 2015 at 2:14
  • 3
    $\begingroup$ I see. f[x_] := Divisible[x, 7]; Cases[Range[0, 70], _?f]. Thanks. $\endgroup$
    – Al Guy
    Sep 8, 2015 at 2:23
  • $\begingroup$ Yep. Exactly right. Was glad to help. $\endgroup$ Sep 8, 2015 at 2:38
  • $\begingroup$ @LeonidShifrin This would be good to have as an answer. Would be a shame to have no actual answer, and a bigger shame to close it as a "simple mistake", because it raises a subtle point that's worth having a note about in MSE. $\endgroup$ Sep 9, 2015 at 6:42
  • $\begingroup$ @DanielLichtblau You are right. But it was closed, so can't add an answer. Voted for reopening. You could add a vote too. $\endgroup$ Sep 9, 2015 at 12:27

1 Answer 1

10
$\begingroup$

The two ways of attaching a test to a pattern, Condition and PatternTest, have fundamentally different semantics. Your mistake was that you tried to use the semantics of Condition in PatternTest.

Condition

When you test with condition, you can attach a name to a pattern, and use that name also in the code in the second argument in Condition - and that name will be bound to the name of the pattern - this is how Condition works:

Cases[Range[0, 70], x_/;Divisible[x, 7]]

This is a general construct, so you can use it also with several pattern variables:

Cases[Partition[Range[10],2,1], {x_,y_} /; x > 3 && OddQ[y]]

PatternTest

With PatternTest (?), the situation is different.

First, PatternTest expects a function as a second argument, not an expression. What this means is that, given a pattern

_?f

The test in say Cases[{1,2,3}, _?f] will be performed as f[1], f[2], f[3]. If the result evaluates to True, the pattern-matcher considers the element to match the pattern. For any other outcome, pattern-matcher considers the element to not match the pattern.

Second, for PatternTest it makes no sense to name the pattern. If you use

x_?f

then there is no way you can use x inside f, this binding is ignored by f, simply because f is considered a function that is anyway applied to the matched element. The only reason to name patterns used with PatternTest is to use these names on the r.h.s. of the rule, for example:

fun[x_?f]:= x^2

or, may be, when you want to apply further conditions in some other places:

fun[x_?f, y_?f] /; x > y := Null

The case at hand

In your case, using

Cases[Range[0, 70], x_?(Divisible[x, 7])]

means that the tests will form "function calls" of the form

Divisible[x,7][elem]

where elem is one of 1, 2 ,..., 70, and since these won't evaluate further (e.g. Divisible[x,7][10]), the pattern-matcher considers the elements to not match the pattern. Note that x in the above will not be bound to x in the pattern x_, as I explained above - rather, it will be a symbol taken from the surrounding environment, and its presence wouldn't make much sense.

As you noted in comment, the way to make this work (one of them), is then to define

f[x_] := Divisible[x, 7]

and then use it as

Cases[Range[0, 70], _?f]

In this case, f has semantics of a function, and binding between x in x_ and x in Divisible[x, 7] is provided by SetDelayed.


In any case, the bottom line is that:

  • Your mistake was to supply an expression where a function was expected
  • Pattern name binding works in Condition, which expects an expression as a second argument, but not for PatternTest, which expects a function as a second argument, and for which therefore such a binding makes no sense.
$\endgroup$
6
  • 1
    $\begingroup$ That's four bottom lines.. $\endgroup$ Sep 9, 2015 at 17:48
  • 1
    $\begingroup$ @DanielLichtblau I have an excuse of not being a native speaker :) $\endgroup$ Sep 9, 2015 at 18:14
  • $\begingroup$ Leonid, I feel that this question is close enough to (1835) to warrant closing as a duplicate. However I think your answer should not be buried. I could Merge to move your answer there, but some rewriting would be necessary. Please give me your thoughts on this. $\endgroup$
    – Mr.Wizard
    Sep 9, 2015 at 20:06
  • $\begingroup$ @Mr.Wizard I don't think it's a dupe, because in that case, the OP knew the right syntax for both cases, and was interested in more subtle differences, while here the OP was confused with the syntax. Those who face similar basic problems but come to the post you linked to, will get little help from it, since it is, in a way, more advanced. $\endgroup$ Sep 9, 2015 at 20:09
  • $\begingroup$ I do see your point but I feel that these questions are closely linked in that they both deal with how/why one would use one over the other. I also feel that this answer would ultimately have more exposure if posted in that Q&A as it has become somewhat canonical. I will respect your choice however. $\endgroup$
    – Mr.Wizard
    Sep 9, 2015 at 20:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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