4

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?

m_goldberg
  • 107,779
  • 16
  • 103
  • 257
Mitchell Kaplan
  • 3,696
  • 22
  • 34
  • 1
    Are you thinking of Condition? (look it up in the docs) – Szabolcs Mar 15 '14 at 14:00
  • 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. – Leonid Shifrin Mar 15 '14 at 14:01
  • @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.) – Michael E2 Mar 15 '14 at 14:23
  • @MichaelE2 I meant the first one, that's why I said "top-level". I agree that I could've made that more clear. – Leonid Shifrin Mar 15 '14 at 14:37
  • @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. – Michael E2 Mar 15 '14 at 14:39
  • @MichaelE2 In fact, there have been a few times where I really wanted the top-level Alternatives to work. I don't know what was the rationale behind this limitation / design decision, but probably there was some good reason for this. – Leonid Shifrin Mar 15 '14 at 14:42
  • @Leonid What head with the definition be attached to in that case? – Mr.Wizard Mar 15 '14 at 14:57
  • @Mr.Wizard I only meant the special case where all heads of the top-level alternatives are the same. In general, I agree that this is not very well-defined. – Leonid Shifrin Mar 15 '14 at 14:59
  • @Leonid There's your answer I think. :-) – Mr.Wizard Mar 15 '14 at 15:06
  • @LeonidShifrin Like this: f[PatternSequence[1, x_] | PatternSequence[3, x_]] := x^2? – Michael E2 Mar 15 '14 at 15:07
  • @Mr.Wizard This is a general answer, for things like f[x_]|g[x_]:=... - this I anticipated before. But this does not explain why not to make an exception for things like f[patt1]|f[patt2]:=.... – Leonid Shifrin Mar 15 '14 at 15:11
  • @MichaelE2 I rather meant something like f[1,x_]|f[2,x_]:=x^2. – Leonid Shifrin Mar 15 '14 at 15:11
  • @LeonidShifrin Sorry, I meant would the PatternSequence method suit some cases for you. But I think we may be drifting away from the present question? – Michael E2 Mar 15 '14 at 15:13
  • @MichaelE2 Oh I see. Well, PatternSequence does cover some cases, but not all. I don't remember now whether those needs I had in the past would've been satisfied with PatternSequence. Re: drifting - I guess so... – Leonid Shifrin Mar 15 '14 at 15:19

3 Answers3

14

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.:

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
8

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.

WReach
  • 68,832
  • 4
  • 164
  • 269
6

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.

Peltio
  • 5,516
  • 3
  • 27
  • 27