7

I'm not very good at working with anonymous functions, a fact that has now come back to bite me. Suppose I have a function defined as follows:

Clear[g]
g[x_] := Sin[x] /; x < 2

Then, for example,

{g[0.4], g[2.4]} = {0.389418, g[2.4]}

Now suppose I want to make g into an anonymous function. Naively, I would write

Clear[g]
g := Function[u, Sin[u] /; u < 2]

But then

{g[0.3], g[2.4]} = {0.29552 /; 0.3 < 2, 0.675463 /; 2.4 < 2}

which is clearly not correct. How do I handle this situation?

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
user26718
  • 366
  • 1
  • 2

5 Answers5

5

Using Defer:

ClearAll[g];

g = If[# < 2, Sin[#], Defer[g[#]], Defer[g[#]]] &;

{g[0.4], g[2.4], g[x]}

{0.389418, g[2.4], g[x]}
Plot[g[x], {x, -Pi, 2 Pi}]

output

 $Version
"12.3.1 for Microsoft Windows (64-bit) (June 19, 2021)"

Of course, g here isn't a pure (or "anonymous") function as Michael E2 stresses in his answer: it is a Symbol with the value of a Function containing in its body this Symbol itself.


UPDATE

Here is a version that works correctly with replacements (an answer to Michael E2's concern):

ClearAll[g];

g /: Defer[g[x_]] := Block[{g$inDefer = True}, g[x]] /; Not[TrueQ@g$inDefer] g = If[TrueQ[# < 2], Sin[#], Defer[g[#]]] &;

{g[0.4], g[2.4], g[x]} {g[0.4], g[2.4], g[x]} /. x -> 1.

{0.389418, g[2.4], g[x]}
{0.389418, g[2.4], 0.841471}
Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
5

An "anonymous" function should have no name (symbol) attached. What result should you want from g[2.4] if the result should not contain the symbol g? I picked up my programming on the street, so take with a grain of salt: My claim is that is should not be g[2.4]; rather, if g = func[...], then it should be func[...][2.4], where func is a version of Function that conditionally evaluates its input. See an example below.

A so-called function like g[x_] := Sin[x] /; x < 2 defines a conditional pattern replacement in an expression with head g. The pattern language is rather complex, even if we stick to down values. Exactly what sort of pattern replacements do you want to turn into anonymous functions? Well, in the example below, I will keep it simple and implement a single conditional test equivalent to g[x_,...] := body /; test like in the OP. I'm not sure it's worth being able to turn g[Sin[Optional[v_?NumericQ]*u_]] := u + v /; v > 0 into an anonymous function.

Function[{x,...}, body][x1,...] makes a literal, unconditional replacement in the expression body, in which each literal occurrence of x is replaced by x1 and so on; this is then evaluated after Function returns. Viewed as a pattern replacement, it's very limited. It seems ill-suited to adapting to complicated patterns.

Examples.

First approach.

It's probably better to remove attr from the definition. See below.

ClearAll[fFunction];
SetAttributes[fFunction, HoldAll];
fFunction[v_, body_, attr_, test_][a___] :=  
  Function[v, body, attr][a] /; Function[v, test, attr][a];

Test case:

g = fFunction[u, Sin[u], {}, u < 2]
(* fFunction[u, Sin[u], {}, u < 2]  *)

{g[0.3], g[2.4], g[x]} % /. x -> 1.2 (* {0.29552, fFunction[u, Sin[u], {}, u < 2][2.4], fFunction[u, Sin[u], {}, u < 2][x]}

{0.29552, fFunction[u, Sin[u], {}, u < 2][2.4], 0.932039} *)

Hey, do attributes really work?

HoldAll. No, attributes don't really work. Here's a way to fix it for HoldAll and similar. It destroys somewhat the desired behavior when test is not True. Since attributes are not applied to subvalues, fFunction[x, body, HoldAll, x > 10][a] won't hold a. We have to take advantage of the fact that heads are evaluated first to grab Hold of a before it evaluates.

ClearAll[fFunction];
SetAttributes[fFunction, HoldAll];
fFunction[v_, body_, attr_, test_] := 
  Function[Null,
   fFunction[v, body, attr, test, Hold][Hold[##]], 
   HoldAll];
fFunction[v_, body_, attr_, test_, Hold][Hold[a___]] :=
  Function[v, body, attr][a] /; Function[v, test, attr][a];

Now what about Listable? Well, that's tricky because we have to thread the evaluation of test and use it to block or allow the evaluation of body -- that is, fFunction should be listable, in the case where Listable is in attr....Hmm, I'm reminded of something someone one wrote:

...once you run into a limitation like this, it is best to reconsider the design and find some other approach to the problem... -- Leonid Shifrin, How do you set attributes on SubValues?

Remarkably, there's an obvious alternative. Use the standard way of defining functions with patterns. :)

Michael E2
  • 235,386
  • 17
  • 334
  • 747
3
$Version

(* "12.3.1 for Mac OS X x86 (64-bit) (June 19, 2021)" *)

Clear["Global`*"]

g := If[#1 < 2, Sin[#1], HoldForm[g[#1]], HoldForm[g[#1]]] &

{g[0.4], g[2.4], g[x]}

enter image description here

Bob Hanlon
  • 157,611
  • 7
  • 77
  • 198
3

Will this work for you?

g[x_ /; x < 2] := Function[u, Sin[u]][x]
{g[0.4], g[2.4]}
(* {0.389418, g[2.4]} *)
m_goldberg
  • 107,779
  • 16
  • 103
  • 257
2

Another approach (from here). It gives the "proper" result but I'm not sure you really want this result...

g = If[TrueQ[#1 < 2], Sin[#1], Unevaluated @@ Hold[#0[#1]]] &;
{g[0.4], g[2.4], g[x]}

{0.389418, Unevaluated[(If[TrueQ[#1 < 2], Sin[#1], Unevaluated @@ Hold[#0[#1]]] &)[2.4]], Unevaluated[(If[TrueQ[#1 < 2], Sin[#1], Unevaluated @@ Hold[#0[#1]]] &)[x]] }

b3m2a1
  • 46,870
  • 3
  • 92
  • 239