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

9

1

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

Posted 2016-06-09T12:02:12.540

Reputation: 18 597

All other values UpValues DownValues SubValues etc.. should not be inserted into the held expression. – QuantumDot – 2016-06-09T12:05:22.540

1Why not just Hold[a+b+c]/.OwnValues[a]? – Leonid Shifrin – 2016-06-09T12:05:37.603

@LeonidShifrin This assumes you know that a is the symbol with OwnValues. How do I make this replacement for all symbols which have OwnValues without knowing beforehand which ones have them? – QuantumDot – 2016-06-09T12:12:59.890

@Mr.Wizard It does seem familiar, but I can't find a dupe right now. – Leonid Shifrin – 2016-06-09T13:02:18.517

Related: (40165), (46535)

– Mr.Wizard – 2016-06-09T13:02:50.170

@Kuba No, I was just having another Senior Moment. :-p – Mr.Wizard – 2016-06-15T15:24:29.157

Answers

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

Posted 2016-06-09T12:02:12.540

Reputation: 129 207

Ninja'd me there, +1 – LLlAMnYP – 2016-06-09T12:41:09.177

This is quite nice. – QuantumDot – 2016-06-09T13:02:40.600

And, it's easy to prevent heads from getting replaced by simply changing Heads->False. – QuantumDot – 2016-06-09T13:32:44.670

1I 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 – 2016-06-09T17:06:39.067

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 – 2021-01-27T05:40:32.987