9
$\begingroup$

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] *)
$\endgroup$
6
  • $\begingroup$ All other values UpValues DownValues SubValues etc.. should not be inserted into the held expression. $\endgroup$
    – QuantumDot
    Jun 9, 2016 at 12:05
  • 1
    $\begingroup$ Why not just Hold[a+b+c]/.OwnValues[a]? $\endgroup$ Jun 9, 2016 at 12:05
  • $\begingroup$ @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? $\endgroup$
    – QuantumDot
    Jun 9, 2016 at 12:12
  • $\begingroup$ @Mr.Wizard It does seem familiar, but I can't find a dupe right now. $\endgroup$ Jun 9, 2016 at 13:02
  • $\begingroup$ Related: (40165), (46535) $\endgroup$
    – Mr.Wizard
    Jun 9, 2016 at 13:02

1 Answer 1

10
$\begingroup$
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.

$\endgroup$
5
  • $\begingroup$ Ninja'd me there, +1 $\endgroup$
    – LLlAMnYP
    Jun 9, 2016 at 12:41
  • $\begingroup$ This is quite nice. $\endgroup$
    – QuantumDot
    Jun 9, 2016 at 13:02
  • $\begingroup$ And, it's easy to prevent heads from getting replaced by simply changing Heads->False. $\endgroup$
    – QuantumDot
    Jun 9, 2016 at 13:32
  • 1
    $\begingroup$ 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... $\endgroup$
    – QuantumDot
    Jun 9, 2016 at 17:06
  • $\begingroup$ 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. $\endgroup$
    – Sean
    Jan 27, 2021 at 5:40

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.