# Using patterns in pure functions

Pure functions may be handy if you don't want to assign your function a name. For example I would calculate $x(x-1)$ for some numbers $x$ by

In: #(#-1) & /@ {1, 2, 3}
Out: {0, 2, 6}


Is it possible to use patterns in this construction? For example something like

f[x_Integer] := x(x-1)


but then without defining a function f first.

-
P.S Do I use the term pure function here correctly? I feel I don't, but I wouldn't know how to call this construction otherwise. – sjdh Mar 19 '12 at 8:32
Yes, the documentation uses the term pure function for these. – Szabolcs Mar 19 '12 at 9:13
In other languages, they're called anonymous functions or lambda functions – F'x Mar 19 '12 at 21:32

You could write something like this:

# /. x_Integer :> x (x - 1) & /@ {1, 2, 3}


{0, 2, 6}

-
Dang, I was trying to go for this but managed to add an additional layer of complexity :-). – Timo Mar 19 '12 at 14:01
+1. I would however use Replace rather than ReplaceAll, to prevent the rule to apply on deeper levels in case when it does not apply to the top-level. – Leonid Shifrin Mar 19 '12 at 17:45

To complement WReach's answer, I suggest that you are actually looking for replacement rules. A function with patterns is effected with replacement rules:

f[x_Integer] := x(x-1)

DownValues[f]

{HoldPattern[f[x_Integer]] :> x (x - 1)}


You you don't need to actually set this definition (DownValue) to use the same rule.

Clear[f]

f /@ {1, 2, Sqrt[7], 0.3} /. {f[x_Integer] :> x (x - 1)}

{0, 2, f[Sqrt[7]], f[0.3]}


I suggest doing the replacement directly, and I recommend using Replace rather than ReplaceAll (/.) if you want something analogous to a pure function. Using /. would result in Sqrt[42] in this example:

Replace[{1, 2, Sqrt[7], 0.3}, {x_Integer :> x (x - 1), x_ :> f[x]}, {1}]

{0, 2, f[Sqrt[7]], f[0.3]}


You can of course do something else with arguments that do not match x_Integer besides wrapping in f. If you want a pure function to map onto individual elements you could use:

Replace[#, x_Integer :> x (x - 1)]& /@ {1, 2, Sqrt[7], 0.3}

{0, 2, Sqrt[7], 0.3}

-
+1 for the note on Replace (I noted that too). Local replacement rules (their semantics) are not entirely equivalent to global rules / definitions. This was partly why I did not take the road taken by @WReach (although I accept that as an alternative and voted for his answer). Whether or not the OP will be satisfied with local replacement rules depends largely on how closely he wants to follow the semantics of functions based on global rules / definitions, I think. – Leonid Shifrin Mar 19 '12 at 17:54

No, this is not possible, for the following reason (in my opinion):

When you define

f[x_Integer] := ...


and evaluate f[Pi] (where Pi is not of type Integer), then the function will stay unevaluated in the form f[Pi]. If you could use a pure function in the hypothetical form of Function[x_Integer, x(x+1)], then of course it could stay unevaluated in the form Function[x_Integer, x(x+1)][Pi], but I am not sure this would be useful. (Well, it might be if you pass it a symbol that you may later replace with an integer.)

Anyway, it is not possible.

There are some ways one can make use of patterns in pure functions (for example through replace rules). So now the question is: what do you want to achieve by using a pattern in a pure function? If it is preventing evaluation, it's not possible. If it's something else, it might be.

-
I would say that foo & is an anonymous function regardless of whether foo contains replacement rules, If[] statements or other inbuilt functions. The main point being that they are not assigend as a downvalue for a symbol. – Timo Mar 19 '12 at 14:40
@Timo Of course, I didn't claim otherwise. Please tell me know if my post is confusing in any way. – Szabolcs Mar 19 '12 at 15:02
@Timo If the downvote was given by you, please explain in detail why. I don't understand how your comment relates to my post. – Szabolcs Mar 19 '12 at 15:03
The downvote is from me. The OP question was about using pattern based matching in anonymous functions and your answer starts out with No and you reiterate "not possible" later. As many (myself included) point out this is not the case. WReach has the best example (I even removed my earlier answer that was a less elegant implementation of the same principle). Your comment regarding leaving the function unevaluated is valid but not really applicable in the case of pure functions (as you point out yourself). Anyway those are my reasons. If they are unreasonable (sry pun) let me know. – Timo Mar 19 '12 at 16:48
@Timo The pattern the OP gave as an example simply causes the function to stay unevaluated for non-integer arguments. This is not possible with pure functions. Please not that I explicitly say that while evaluation can't be prevented, it is possible to use replacement rules. I asked the OP for clarification about this. I did not give your solution because it is not clear that this is the effect he wants to achieve. – Szabolcs Mar 19 '12 at 17:38

Map is unnecessary in this case, so the most succinct expression is:

{1, 2, 3} /. x_Integer :> x (x - 1)

-

You can return a closure with encapsulated pattern-defined function using some custom assignment operator, like this one:

ClearAll[deff];
SetAttributes[deff, HoldAll];
deff[lhs_ :> rhs_] :=
Module[{f},
f[lhs] := rhs;
f[##] &]


This can be trivially generalized to many rules. You can then use it as

With[{fn = deff[x_Integer :> x*(x - 1)]},
Map[fn, Range[10]]]

(*    {0, 2, 6, 12, 20, 30, 42, 56, 72, 90}  *)


One problem you still have to solve in this approach is a proper garbage-collection of such functions. You may also want to use some form of memoization for deff, so that it does not produce a brand new pure function every time when called on the same rule (modulo pattern variables names). Both problems are solvable, if not completely trivial. Finally, note that wrapping in a pure function restricts the set of Attributes which can be used, since not all possible Attributes for symbols make sense for a pure function (I did not include attributes here for simplicity).

All in all, while I can see some cases where this may be nice to have, in most cases this is probably more work than it's worth. In fact, in the above approach, it is much easier for deff to return just the generated symbol f itself, which is probably a better solution if you want some auto-generated functions and don't want to bother with the naming. This will also automatically take care of the Attributes issue.

-
I'll never say not possible again :-) +1 – Szabolcs Mar 19 '12 at 10:33
@Szabolcs Well, strictly speaking, you were right. I just considered the problem a little broader. – Leonid Shifrin Mar 19 '12 at 10:34
I wonder what advantage it would have jumping through all those hoops compared to just defining the function... – Sjoerd C. de Vries Mar 19 '12 at 13:22
I've noticed whenever I answer something on the site now, and I am about to write "not possible" or something similar, I now delete that, because I don't want to keep writing "Leonid will probably find a way" :) – tkott Mar 19 '12 at 14:40
@Sjoerd There may be cases when one need to automatically generate potentially many functions. Also, not having to name a function can be a big advantage, because you delay the funcion-creation from definition-time to run-time, which often adds flexibility. – Leonid Shifrin Mar 19 '12 at 17:48

Four ways from If:

If[IntegerQ@#, # (# - 1), #] & /@ {3, \[Pi]}
(* {6, \[Pi]} *)

If[# \[Element] Integers, # (# - 1), #] & /@ {3, \[Pi]}
(* {6, \[Pi]} *)

If[Head@# === Integer, # (# - 1), #] & /@ {3, \[Pi]}
(* {6, \[Pi]} *)

If[MatchQ[#, _Integer], # (# - 1), #] & /@ {3, \[Pi]}
(* {6, \[Pi]} *)


The last two work also with user-defined heads. The 2nd argument to MatchQ can be any pattern.

If you like, you can return the pure function unevaluated on arguments that don't match the pattern:

If[MatchQ[#, _Integer], # (# - 1), Hold[#0][#]] & /@ {3, \[Pi]}
(* {6, Hold[If[MatchQ[#1, _Integer], #1 (#1 - 1),
Hold[#0][#1]] &][\[Pi]]} *)


although I don't see a use for it.

I'm not advocating using If, just pointing out what is possible.

-