10
$\begingroup$

I don't understand why

 MatchQ[
  HoldComplete[Times[3, 2, 2]],
  HoldComplete[Times[a_]]
  ]

is returning True. I thought that Times[a_] would match only one argument (i.e. Times[3]) while Times[a__] would match all of them.

Edited question: is there a way I can force it to match only where only one argument is present? My first thought would be to use ;/

$\endgroup$
2
  • 6
    $\begingroup$ There is some mention of this in the ref guide under Flat. $\endgroup$ Nov 10, 2014 at 18:59
  • 3
    $\begingroup$ This has to do with the Flat attribute of Times. $\endgroup$ Nov 10, 2014 at 18:59

4 Answers 4

11
$\begingroup$

The observed behaviour will appear in any expression whose symbolic head has the attribute Flat.

Under normal circumstances, with no attributes in play, we see the usual expected behaviour:

MatchQ[f[1], f[1]]            (* True *)
MatchQ[f[1], f[a_]]           (* True *)
MatchQ[f[1], f[f[1]]]         (* False *)
MatchQ[f[1], f[f[a_]]]        (* False *)
MatchQ[f[1, 2, 3], f[a_]]     (* False *)

But when we introduce the Flat attribute, our normal intuition no longer holds:

SetAttributes[g, Flat]

MatchQ[g[1], g[1]]            (* True *)
MatchQ[g[1], g[a_]]           (* True *)
MatchQ[g[1], g[g[1]]]         (* True *)
MatchQ[g[1], g[g[a_]]]        (* True *)
MatchQ[g[1, 2, 3], g[a_]]     (* True *)

What is happening?

The purpose of Flat is to flatten out any nested expressions. That is, g[g[1, 2, 3]] is to be treated as equivalent to g[1, 2, 3]. The key point is that this equivalence works both ways. So when we ask whether g[1, 2, 3] matches the pattern g[a_], then this is equivalent to asking whether g[g[1, 2, 3]] matches g[a_]. Which of course it does. That is why the MatchQ expression in the question returns True. As does MatchQ[g[g[g[g[g[1, 2, 3]]]]], g[a_]]

Can we turn it off?

A simple way to perform the exact match requested by the question is to temporarily remove the Flat attribute from Times. Block will strip Times of all of its attributes:

Block[{Times}
, MatchQ[
    HoldComplete[Times[3, 2, 2]]
  , HoldComplete[Times[a_]]
  ]
]
(* False *)

If, for some reason, the application is such that we wish to retain the other attributes of Times, we can use Internal`InheritedBlock:

Attributes[Times]
(* {Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected} *)

Internal`InheritedBlock[{Times}
, Unprotect[Times]
; ClearAttributes[Times, Flat]
; Protect[Times]
; { MatchQ[
      HoldComplete[Times[3, 2, 2]]
    , HoldComplete[Times[a_]]
    ]
  , Attributes[Times]
  }
]
(* {False, {Listable, NumericFunction, OneIdentity, Orderless, Protected}} *)
$\endgroup$
3
  • $\begingroup$ Nice answer,I have seen Internal package for some times, BTW, I'd like to know what's the main purpose (functionality) of Internalpackage? or is there any detailed introduction to this package .:-) $\endgroup$
    – xyz
    Nov 11, 2014 at 6:32
  • $\begingroup$ @ShutaoTang The Internal package is undocumented, and we use its features at our own risk since it carries no guarantees and could be withdrawn at any time. As the name implies, it is used internally by the Mathematica implementation. There is no official documentation on this package, but this forum is rapidly becoming the repository for the collective findings of the community -- whether about Internal or many of the other undocumented features. And even the documented ones too :) $\endgroup$
    – WReach
    Nov 11, 2014 at 14:41
  • $\begingroup$ OK,@WReach thanks very much!:-) $\endgroup$
    – xyz
    Nov 11, 2014 at 15:34
3
$\begingroup$

Times has attributes Flat, Orderless, and OneIdentity to cater for Associativity and Commutativity.

These attributes affect patterns; in your case to ensure that x_.y_.z_ not only matches the pattern x, but also y and z. This is in line with the commutative property of multiplication.

$\endgroup$
3
$\begingroup$

As WReach says in his answer, the issue is with attributes. Another way to turn off the Flat attribute in pattern matching is to use Verbatim:

MatchQ[HoldComplete[Times[3, 2, 2]], HoldComplete[Verbatim[Times][_]]]

False

$\endgroup$
1
$\begingroup$

I have found some other ways (than Block[...]) to "force" the matching, but with some slight modifications of the code.

1. Whatever the choice you make here :

{f1, f2} = {Hold, Hold};

or

{f1, f2} = {HoldComplete, HoldComplete};

or

{f1, f2} = {Unevaluated, HoldPattern};

the two following approaches work (and give the same results) :

-> Using /; : (this is actually the more compact solution)

MatchQ[f1[Times[3, 2, 2]], f2[Times[p : __ /; Length@{p} == 1]]]
MatchQ[f1[Times[3, 2, 2]], f2[Times[p : __ /; Length@{p} == 3]]]
(*False*)
(*True*)

-> Using a "trick" with /.

MatchQ @@ ({f1[Times[3, 2, 2]], f2[Times[a_]]} /. Times -> foo)
MatchQ @@ ({f1[Times[3, 2, 2]], f2[Times[_, _, _]]} /. Times -> foo)
(*False*)
(*True*)

Of course, instead of Times[_,_,_] you could use conditional /; or even pattern test ?, to specify how many arguments Times should have (like in the previous solution) but this would produce a longer and less readable code.

2. All the previous code works not only when the arguments are numbers (Times[3,2,2]) but also when they are just symbolic variables (Times[a,b,c])

For symbolic variables only, this also works :

Remove[a, b, c];
(**)
MatchQ[Times[a, b, c], _Times?(Length@# == 1 &)]
MatchQ[Times[a, b, c], _Times?(Length@# == 3 &)]
(*False*)
(*True*)

In comparison, neither this works :

MatchQ[Times[a, b, c], Times[x_, y_, z_]]
MatchQ[Times[a, b, c], Times[x_, y_]]
MatchQ[Times[a, b, c], Times[x_]]
(*True*)
(*True*)
(*True*)

nor the original test :

MatchQ[HoldComplete[Times[a, b, c]], HoldComplete[Times[x_, y_, z_]]]
MatchQ[HoldComplete[Times[a, b, c]], HoldComplete[Times[x_]]]
(*True*)
(*True*)
$\endgroup$

Your Answer

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

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