9

Here is a very long and complicated expression, which we abbreviate as a. I store it using SetDelayed because I want to perform algebraic manipulations on it:

ClearAll[a];
a := 1 + 1

Here is a really complicated function f with attributes HoldFirst that operates on its first argument. It counts the number of times 1 appears in its first argument.

ClearAll[f];
SetAttributes[f, HoldFirst];

f[input_] := Module[{expr=Hold[input]},
  Count[expr,1,{0,Infinity}]
]

As you can see, directly inserting the complicated expression works, but not if you insert the abbreviation a:

f[1+1]
(*2*)      (* good *)

f[a]
(*0*)      (* not good *)

The reason it doesn't work is because Hold doesn't allow inserting a definition. So, in the second example, Count is seeing the symbol a and not the expression 1+1 to which it points.

Question: How do I insert OwnValues verbatim inside a held expression without evaluating it?


SIMPLE EXAMPLE

ClearAll[a];
a := 1 + 1

Here is a sample held expression containing symbols which may or may not have OwnValues:

Hold[a + b + c]

How do I insert the RHS of the definition of a verbatim into the held expression, so that the result is this?:

Hold[(1 + 1) + b + c]

I have the following (which may or may not be fruitful):

Hold[a + b + c] /. (symb_Symbol /; OwnValues[symb] =!= {} :> 
   RuleCondition[First[OwnValues[symb]]])

(*  Hold[(HoldPattern[a] :> 1 + 1) + b + c] *)
QuantumDot
  • 19,601
  • 7
  • 45
  • 121

1 Answers1

10
ClearAll[a, b];
a := 1 + 1
b = Sqrt

Is this acceptable?

foo = # /. Join @@ Cases[#, s_Symbol :> OwnValues[s], ∞, Heads->True] &;

foo @ Hold[a + b[c]]

Hold[(1 + 1) + Sqrt[c]]

Update

As OP has noticed I've missed the fact that ReadProtected symbols won't show its OwnValues.

We could do something like s_Symbol /; FreeQ[ Attributes[s], ReadProtected] but why should we skip that symbol if we can just evaluate it to get its OwnValue?

Here's alternative approach:

ClearAll[a, b];
SetAttributes[{b, d}, ReadProtected]
a := 1 + 1
b = Sqrt
d := 1 + 2

foo = # /. Join @@ Cases[#, s_Symbol :> If[ FreeQ[Attributes[s], ReadProtected], OwnValues[s], {HoldPattern[s] :> Evaluate@s} ], [Infinity], Heads -> True] &;

foo@Hold[a + b[c] + I + d]

Hold[(1 + 1) + Sqrt[c] + I + 3]

As you can see d is inserted but not as 1+2, that's the price of ReadProtected. We could Unprotect but it wouldn't work for Locked symbols. So at the end it's up to OP how to handle those cases.

Kuba
  • 136,707
  • 13
  • 279
  • 740
  • Ninja'd me there, +1 – LLlAMnYP Jun 09 '16 at 12:41
  • This is quite nice. – QuantumDot Jun 09 '16 at 13:02
  • And, it's easy to prevent heads from getting replaced by simply changing Heads->False. – QuantumDot Jun 09 '16 at 13:32
  • 1
    I found something funny: if you try foo @ Hold[a + I b[x]], you'll get General::readp messages and it doesn't work. This seems to be related to I being held is different from when it is processed by the kernel... – QuantumDot Jun 09 '16 at 17:06
  • Nice. Some bonus use-cases would be: (1) Don't substitute symbols when they are being passed to HoldAll / HoldAllComplete / HoldFirst / etc functions (2) If performing #1, include pure functions with the same attributes (can't get such attributes with Attributes, though, so method may be different), and (3) Don't substitute symbols when they are being passed to Module / Block / With (probably a freebie if performing the other use-cases). Maybe I'll come up with something. – Sean Jan 27 '21 at 05:40