Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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

Switch[expr,
  pat1, val1,
  pat2, val2,
  …]

For example,

Switch[x,
  _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:

Switch[x,
  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?

share|improve this question
1  
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",...] –  belisarius Apr 7 at 12:21

4 Answers 4

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:

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

e.g.

Replace[x,
  {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.

share|improve this answer
    
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. –  Carlo Jul 21 at 15:00

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

*)
share|improve this answer

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

ToString[#] <>
 Switch[#,
  _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.

share|improve this answer

You could define your own smthSwitch:

SetAttributes[BindSwitch, HoldRest];

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

Example:

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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