4
$\begingroup$

I thought that I remembered a way to write a function definition using "|", but can't find documentation for it. So for example, I'd want to define:

f[1, x_] := x^2;
f[2, x_] := x*12;

I thought there was a syntax, kind of like a "select case" or "switch" statement that used "|". Is there?

$\endgroup$
14
  • 1
    $\begingroup$ Are you thinking of Condition? (look it up in the docs) $\endgroup$
    – Szabolcs
    Mar 15, 2014 at 14:00
  • $\begingroup$ In this particular case, it does not seem appropriate to use such a construct anyway, because your r.h.sides are different. But also in general, the use of Alternatives in the top-level function definitions is not allowed. $\endgroup$ Mar 15, 2014 at 14:01
  • $\begingroup$ @LeonidShifrin Just to clarify, f[1, x_] | f[3, x_] := x^2 is not allowed, but f[1 | 3, x_] := x^2 is allowed. (It's not clear to me which the OP is trying to recall, if either.) $\endgroup$
    – Michael E2
    Mar 15, 2014 at 14:23
  • $\begingroup$ @MichaelE2 I meant the first one, that's why I said "top-level". I agree that I could've made that more clear. $\endgroup$ Mar 15, 2014 at 14:37
  • $\begingroup$ @LeonidShifrin I figured that out, but I was thinking of the second one when I read the question. I really was trying to clarify it for the OP, just in case. $\endgroup$
    – Michael E2
    Mar 15, 2014 at 14:39

3 Answers 3

8
$\begingroup$

It sounds like you are describing pattern guards or pattern-matching syntax found variously in ML/SML/OCAML/Haskell/F#. For example, in Haskell one could write:

-- This is Haskell, *not* Mathematica

f a x
    | a == 1 = x ^ 2
    | a == 2 = x * 12

Mathematica does not support this kind of syntax using a pipe symbol. The definitions exhibited in the question are idiomatic. We can write code that looks vaguely like the Haskell syntax using explicit pattern matching:

f[a_, x_] := a /.
    { 1 :> x ^ 2
    , 2 :> x * 12
    , _ :> $Failed
    }

This is not very idiomatic. It could possibly be convenient for more elaborate argument list shapes and conditions. The $Failed case was added to point out that we would otherwise have the (surprising?) behaviour of returning a unchanged if it is not 1 or 2. An Message / Abort sequence might be more appropriate, depending upon the application (and mimicking Haskell's built-in behaviour).

Other answers to this question show how one can avoid repeating argument lists across function definitions using features such as Switch or Alternatives.

$\endgroup$
14
$\begingroup$

You may be remembering a way that I sometimes write function definitions or replacement rules using what I call vanishing patterns, using Alternatives (short form |). Some examples:

This technique can considerably condense some code, but at the expense of clarity for those not familiar with it, and often a slight decrease in performance. I do not see any (simple) way to apply it to the example in the question, and shoehorning the method into code where it does not naturally fit (for one accustomed to it) surely hurts clarity and should be avoided outside of "code golf" games.

To provide a simple example of the method where I believe it does fit consider:

f[a_Real, x_] := x*a
f[b_Integer, x_] := x^b

This could instead be written:

f[a_Real | b_Integer, x_] := (x*a)^b

Whichever pattern (a_Real or b_Integer) does not match is left out of the right-hand-side, and because of the single-argument behavior of Times and Power this is handled correctly.

Another place the method works very well is inserting elements into an expression at specific places based on pattern. For example, suppose you want an function than takes a list and an integer, and appends the integer if it is odd and "prepends" the integer if it is even. This can be written cleanly as:

 g[{x___}, e_?EvenQ | o_?OddQ] := {e, x, o}

Test:

Fold[g, {}, Range@10]
{10, 8, 6, 4, 2, 1, 3, 5, 7, 9}

More simply you can use Alternatives to match different input forms, e.g.:

$\endgroup$
6
$\begingroup$

There is a Switch statement, but the syntax to do what you need is

f[s_,x_]:= Switch[s,
    1, x^2,
    2, x*12,
    True, "something else"
 ]

The third option *which I forgot in my first edit) takes care of what happens when s is neither 1 or 2.

Otherwise you could use the functional overloading approach you mentioned in your question. It works because Mathematica will order your definitions from the more specific to the more general, and after defining f[1,x_] and f[2,x_] you can add a definition for the general case f[s_,x_]. I like this approach better, since it can leave the result unevaluated when x is not 1 or 2.

The 'pipe' (i.e. Alternatives) can be used to assign values that are the same for a finite set of a certain parameter's values, like in

g[1 | 2, x_] := x^("1 or 2")
g[y_,x_]:=x^"something else"

It means that the function have the same output for alternative values of input.

$\endgroup$

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.