Suppose I have an expression and want to match it against successive patterns and return an expression corresponding to the first pattern that matched. In that case, I can use

  pat1, val1,
  pat2, val2,

For example,

  _Integer, "This is an integer"],
  _Real , "This is a real"],
  _, "This is something else"]

However, the vali cannot refer to parts of the pati by name. i.e. I cannot do:

  i_Integer, ToString[i] <> " is an integer",
  x_Real , ToString[x] <> " is a real",
  e_, ToString[e] <> " is something else"]

Ocaml has the match function, which is used as:

match expr in:
| pat1 -> val1
| pat2 -> val2

where the vali can refer to named parts of the pati. How can this most elegantly be accomplished in Mathematica?

  • 4
    $\begingroup$ I think a better example should use named parts (not the whole expression). Otherwise the problem is trivially solved by Switch[x, _Integer, ToString[x] <> " is an integer",...] $\endgroup$ Apr 7 '14 at 12:21

I recently realised that the Replace function essentially solves this problem, but it is not the sort of function you tend to associate with conditional constructs. It also might surprise readers of the code, as it is not a common idiom. This solution is:

  {pat1 :> val1,
   pat2 :> val2,
   _ :> valD}]


  {i_Integer :> ToString[i] <> " is an integer",
   x_Real :> ToString[x] <> " is a real",
   e_ :> ToString[e] <> " is something else"}]

It might be useful to define this as a new function which automatically added the default pattern to raise an error in case you forget.

I have sometimes seem /. (ReplaceAll) used for this, but the syntax is quite different from Switch, and it also unnecessarily descends into subexpressions if you forget to add the default pattern, which could lead to hard-to-debug errors.

  • 1
    $\begingroup$ Replace is a really common idiom (in my experience it's much more common than Switch) and should really be preferred to ReplaceAll, which I tend to avoid as much as possible because of the performance implications. $\endgroup$
    – Carlo
    Jul 21 '14 at 15:00
  • $\begingroup$ @Carlo I use ReplaceAll for this when I know it will match so it shouldn't recurse down. That's mainly because it has the short form /. with a nice precedence, and while I'd like to use Replace, avoiding [] or () is usually nice $\endgroup$
    – Rojo
    Aug 9 '14 at 4:16

I like your elegant answer, and...

Switch[#, pat : _Integer /; (val = pat; True), Print[val, " is integer"],
   pat : _Real /; (val = pat; True), Print[val, " is real"], 
   pat_ /; (val = pat; True), Print[val, " is none of the above"]] & /@ {1, 1.1, a}


1 is integer
1.1 is real
a is none of the above


In this situation I might prefer to define this as a separate function.

whatIsThis[x_Integer] := ToString[x] <> " is an Integer."
whatIsThis[x_Real] := ToString[x] <> " is a Real."
whatIsThis[x_] := ToString[x] <> " is something else."

In[4]:= whatIsThis[4]

Out[4]= "4 is an Integer."

In[5]:= whatIsThis[4.1]

Out[5]= "4.1 is a Real."

In[6]:= whatIsThis["Spaghetti"]

Out[6]= "Spaghetti is something else."

Mathematica will try to apply the most specific definition.


In line with beliarius's comment your particular example might also be realized with:

ToString[#] <>
  _Integer, " is an integer",
  _Real, " is a real",
  _, " is something else"] & /@ {1, 1.5, Pi}
{"1 is an integer", "1.5 is a real", "Pi is something else"}

I contest your assertion that using ReplaceAll is incorrect. Because of how it traverses expressions it will always attempt to match the entire expression first, therefore if you include the else pattern _ it will not "look" at deeper levels. Again applied to the specific example:

ToString[#] <> (# /. {
      _Integer -> " is an integer",
      _Real    -> " is a real",
      _        -> " is something else"
    }) & /@ {1, 1.5, Pi}
{"1 is an integer", "1.5 is a real", "Pi is something else"}

Unlike this example pattern matching can be used for destructuring so its use extends far beyond Switch.


You could define your own smthSwitch:

SetAttributes[BindSwitch, HoldRest];

, form_ , case_
, rest___] :=
With[{attempt = Cases[expr, form :> case, {0}]},
If[attempt === {}
, BindSwitch[expr, rest]
, First@attempt]]


BindSwitch[RandomChoice@Join[Range@4, N /@ Range@4, {n}]
,    x_Real , ToString@x <> " is a real"
, x_Integer , ToString@x <> " is an integer"
,        x_ , ToString@x <> " is neither real nor integer"] <> "."

Standard color mark-up for patterns is probably more difficult to implement, though. @IanHinder's solution is free from this issue but this one allows you to add named pattern-based test into existing Switches faster.

Note that control flow is safe: case is never evaluated until there's a match.

There's no error-checks for incomplete BindSwitch expressions but it's easy to add those you'd feel comfortable with.


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.