30
$\begingroup$

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.

$\endgroup$
3
  • $\begingroup$ 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. $\endgroup$
    – sjdh
    Mar 19, 2012 at 8:32
  • $\begingroup$ Yes, the documentation uses the term pure function for these. $\endgroup$
    – Szabolcs
    Mar 19, 2012 at 9:13
  • 1
    $\begingroup$ In other languages, they're called anonymous functions or lambda functions $\endgroup$
    – F'x
    Mar 19, 2012 at 21:32

9 Answers 9

22
$\begingroup$

You could write something like this:

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

{0, 2, 6}

$\endgroup$
2
  • $\begingroup$ Dang, I was trying to go for this but managed to add an additional layer of complexity :-). $\endgroup$
    – Timo
    Mar 19, 2012 at 14:01
  • 9
    $\begingroup$ +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. $\endgroup$ Mar 19, 2012 at 17:45
18
$\begingroup$

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}
$\endgroup$
2
  • $\begingroup$ +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. $\endgroup$ Mar 19, 2012 at 17:54
  • $\begingroup$ With the operator forms (new since V10?), we can simplify the code: Replace[x_Integer :> x (x - 1)] /@ {1, 2, Sqrt[7], 0.3} $\endgroup$
    – Michael E2
    Jun 18, 2022 at 18:26
9
$\begingroup$

Four ways from If:

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

If[# ∈ Integers, # (# - 1), #] & /@ {3, π}
 (* {6, π} *)

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

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

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, π}
 (* {6, Hold[If[MatchQ[#1, _Integer], #1 (#1 - 1), 
 Hold[#0][#1]] &][π]} *)

although I don't see a use for it.

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

$\endgroup$
0
8
$\begingroup$

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.

$\endgroup$
6
  • $\begingroup$ 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. $\endgroup$
    – Timo
    Mar 19, 2012 at 14:40
  • $\begingroup$ @Timo Of course, I didn't claim otherwise. Please tell me know if my post is confusing in any way. $\endgroup$
    – Szabolcs
    Mar 19, 2012 at 15:02
  • $\begingroup$ @Timo If the downvote was given by you, please explain in detail why. I don't understand how your comment relates to my post. $\endgroup$
    – Szabolcs
    Mar 19, 2012 at 15:03
  • 1
    $\begingroup$ 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. $\endgroup$
    – Timo
    Mar 19, 2012 at 16:48
  • 1
    $\begingroup$ +1 for pointing out the unevaluated behaviour. $\endgroup$
    – JxB
    Mar 19, 2012 at 21:40
7
$\begingroup$

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

{1, 2, 3} /. x_Integer :> x (x - 1)
$\endgroup$
6
$\begingroup$

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.

$\endgroup$
8
  • $\begingroup$ I'll never say not possible again :-) +1 $\endgroup$
    – Szabolcs
    Mar 19, 2012 at 10:33
  • $\begingroup$ @Szabolcs Well, strictly speaking, you were right. I just considered the problem a little broader. $\endgroup$ Mar 19, 2012 at 10:34
  • 5
    $\begingroup$ I wonder what advantage it would have jumping through all those hoops compared to just defining the function... $\endgroup$ Mar 19, 2012 at 13:22
  • 2
    $\begingroup$ 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" :) $\endgroup$
    – tkott
    Mar 19, 2012 at 14:40
  • $\begingroup$ @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. $\endgroup$ Mar 19, 2012 at 17:48
6
$\begingroup$

This is very similar to the answer by Mr.Wizard, but avoids to introduce a named expression and therefore rewriting the function by using Condition:

Replace[_, {_ /; IntegerQ[#] :> # (# - 1), _ :> #}] & /@ {1, 2, 3, x}

{0, 2, 6, x}


A more direct way is to use Switch:

Switch[#,
   _Integer, # (# - 1),
   _, #] & /@ {1, 2, 3, x}

{0, 2, 6, x}

$\endgroup$
3
  • 1
    $\begingroup$ I personally prefer to avoid using both Map and Replace as both can take a levelspec as needed. I would instead use If[IntegerQ[#], # (# - 1), #] & /@ {1, 2, 3, x} if avoiding named patterns for some reason. $\endgroup$
    – Mr.Wizard
    May 1, 2016 at 23:05
  • $\begingroup$ @Mr.Wizard I agree and as a single line of code I would not use it as well. But using Map is only part of the example and not the main "patterns in pure function" question. $\endgroup$
    – Karsten7
    May 2, 2016 at 8:34
  • $\begingroup$ Well the OP already knew about Slot and Function so, bluntly, I don't see the point of the first method. The Switch code however is a good example of the use of a pattern inside a pure function as requested, so +1 for that. $\endgroup$
    – Mr.Wizard
    May 3, 2016 at 15:57
2
$\begingroup$

You can write a pure function whose application to an argument not matching the pattern you specified is returned unevaluated, using a trick with Defer and #0:

(If[MatchQ[_Integer][#1], #1 (#1 - 1), Evaluate[Defer][#0][#1]] &)[5]
(* 20 *)

(If[MatchQ[_Integer][#1], #1 (#1 - 1), Evaluate[Defer][#0][#1]] &)[x]
(* (If[MatchQ[_Integer][#1], #1 (#1 - 1), Evaluate[Defer][#0][#1]] &)[x] *)

(If[MatchQ[_Integer][#1], #1 (#1 - 1), Evaluate[Defer][#0][#1]] &)[1.5 + 2.7]
(* (If[MatchQ[_Integer][#1], #1 (#1 - 1), Evaluate[Defer][#0][#1]] &)[4.2] *)

FullForm[%]
(* Defer[Function[If[MatchQ[Blank[Integer]][Slot[1]], 
     Times[Slot[1], Plus[Slot[1], -1]], 
     Evaluate[Defer][Slot[0][Slot[1]]]]][0.1`]] *)

As you can see, there is a head Defer (invisible in the standard form) that is automatically stripped if you edit or copy the output and use it as a new input. If you edit the argument to be an integer (as required by the pattern) and evaluate the expression, it will evaluate to an integer, otherwise it will return unevaluated again.


Alternatively, you can construct a pure function that explicitly wraps its application into Hold if it is to stay unevaluated. Or you can use HoldForm that has the same effect as Hold, but it is not displayed in the standard form (although, unlike Defer, it is not stripped automatically if the output is edited or copied). Both Hold and HoldForm can be explicitly unwrapped using ReleaseHold:

(If[MatchQ[_Integer][#1], #1 (#1 - 1), Hold[#0[#1]]] &)[x]
(* Hold[(If[MatchQ[_Integer][#1], #1 (#1 - 1), Hold[#0[#1]]] &)[x]] *)

ReleaseHold[%]
(* evaluates to the same held result again *)

ReleaseHold[% /. x -> 5] (* substitute 5 as an argument before releasing hold *)
(* 20 *)

Or, you can use Inactivate instead, whose effect can be reversed by Activate:

(If[MatchQ[_Integer][#1], #1 (#1 - 1), Inactivate[#0][#1]] &)[x]
(* The result is displayed in the standard form as

   (If[MatchQ[_Integer][#1], #1 × (#1 + -1), Inactivate[#0][#1]] &)[x]

   with inactive symbols tan-colored, which is actually

   Inactive[Function][
    Inactive[If][Inactive[MatchQ][_Integer][#1], 
     Inactive[Times][#1, Inactive[Plus][#1, -1]], 
      Inactive[Inactivate][#0][#1]]][x] *)

Activate[%]
(* evaluates to the same inactive result *)

Activate[% /. x -> 5] (* substitute 5 as an argument before activating *)
(* 20 *)
$\endgroup$
1
$\begingroup$

First make an Association between the Head of a your input and your input. The Keys of such Association will serve as the pattern:

#Integer (#Integer - 1) &@<|ToString@Head@# -> #|> & /@ {1, 2, 3}
$\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.