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$
  • $\begingroup$ All other values UpValues DownValues SubValues etc.. should not be inserted into the held expression. $\endgroup$ – QuantumDot Jun 9 '16 at 12:05
  • 1
    $\begingroup$ Why not just Hold[a+b+c]/.OwnValues[a]? $\endgroup$ – Leonid Shifrin Jun 9 '16 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 '16 at 12:12
  • $\begingroup$ @Mr.Wizard It does seem familiar, but I can't find a dupe right now. $\endgroup$ – Leonid Shifrin Jun 9 '16 at 13:02
  • $\begingroup$ Related: (40165), (46535) $\endgroup$ – Mr.Wizard Jun 9 '16 at 13:02
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$
  • $\begingroup$ Ninja'd me there, +1 $\endgroup$ – LLlAMnYP Jun 9 '16 at 12:41
  • $\begingroup$ This is quite nice. $\endgroup$ – QuantumDot Jun 9 '16 at 13:02
  • $\begingroup$ And, it's easy to prevent heads from getting replaced by simply changing Heads->False. $\endgroup$ – QuantumDot Jun 9 '16 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 '16 at 17:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.