Nested definition: How can I define a function with a passed-in expression?

23

12

Here's a simplified version of what I'm trying to do:

SetAttributes[def, HoldFirst]
def[s_Symbol, v_] := (s[x_] := v)
def[f, x^2]
f[3] (* Expected result: 9 *)
(*
  x^2
*)
?f (* Expected result: f[x_] := x^2 *)
(*
  Global`f

  f[x$_] := x^2
*)

Obviously the x in the x_ pattern gets replaced by x$. Is there a way I can prevent that? That is, from calling def[f,x^2] I want to result the definition f[x_] := x^2. I don't of course care about the name of the variable, so if the resulting function definition reads f[x$_] := x$^2 I'm fine with that, too.

I tried

def[s_Symbol, v_] := With[{x$ = x}, s[x_] := v]
def[s_Symbol, v_] := With[{x = x$}, s[x_] := v]
def[s_Symbol, v_] := (s[x_] := v) /. x :> x$

and

def[s_Symbol, v_] := (s[x_] := v) /. x$ :> x

but none worked.

celtschk

Posted 2012-08-31T09:00:44.903

Reputation: 18 543

2How about SetAttributes[def, HoldFirst]; def[s_Symbol, v_] := With[{temp = v}, s = Function @@ {x, temp}]? – J. M.'s ennui – 2012-08-31T09:28:35.507

@J.M.: Thanks, I didn't think of anonymous functions; that's a solution that indeed works for my case. – celtschk – 2012-08-31T09:36:03.643

@J.M.: I have to retract that it works for my problem: I just noticed that anonymous functions don't seem to support optional arguments. – celtschk – 2012-08-31T09:43:46.910

Answers

20

With your proposed definition style, the user of that function def has to know that v could/should/must depend on x for this to work; x really should be an argument of def. Perhaps something like this were better suited.

ClearAll[def]
ClearAll[f]
(*SetAttributes[def,HoldFirst]*)

def[s_Symbol, v_, vars_List] := 
 With[{h = s @@ (Pattern[#, Blank[]] & /@ vars)}, (h := v)]
def[f, x^2, {x}]

f[3]
(* 9 *)

user21

Posted 2012-08-31T09:00:44.903

Reputation:

This indirect pattern building did the trick! – celtschk – 2012-08-31T09:51:50.003

20

Try for example

SetAttributes[def, HoldAll]
def[s_Symbol, v_] := Function[Null, s[x_] := #, HoldFirst][v]

Unnamed functions just don't care :)

Other alternatives that should also work (but I would use the previous approach)

def[s_Symbol, v_] := Identity[SetDelayed][HoldPattern@s[x_], v];
def[s_Symbol, v_] := Unevaluated[s[x_] := "Hello"] /. "Hello" -> v

Rojo

Posted 2012-08-31T09:00:44.903

Reputation: 40 993

Elegant! +1. (I edited to better respect the semantics of SetDelayed. If you don't like the result, feel free to revert!) – Oleksandr R. – 2012-08-31T21:33:04.763

Thanks @OleksandrR. Your edit is fine. I had done it that way based on the question being HoldFirst, but HoldAll makes more sense to me too – Rojo – 2012-09-01T01:21:04.703

11

I propose these:

SetAttributes[def, HoldAllComplete]

def[s_Symbol, v_] := SetDelayed @@ Hold[s[x_], v]

def[s_Symbol, v_] := With[{L := s[x_]}, L := v]

def[s_Symbol, v_] := Reverse @ Unevaluated[v := s[x_]]

The HoldAllComplete (or SequenceHold) attribute is necessary for an assignment such as:

def[q, Sequence[1, 2, x]]

head[q[5]]
head[1, 2, 5]

Also see:

Mr.Wizard

Posted 2012-08-31T09:00:44.903

Reputation: 259 163