Skip to main content

hold - How do I restore HoldAll, HoldFirst, HoldRest attributes of Inactivated functions


Let f be defined as follows:


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

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


The 1+1 stays as is. But if I Inactivate f:


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

it has the paradoxical effect of reactivating the held 1+1, making it evaluate to 2. How do I prevent Inactive from killing the attributes of f?


note: I am looking for a solution that solves the problem of losing attributes, not one of deactivating 1+1 by doing f[1+1] // Inactivate



Answer




I'm not aware of any way to hold evaluation of arguments in expression of form Inactive[f][arguments], which is how inactivated expressions look like. What we can do, to prevent evaluation of arguments, is to use a symbol with appropriate Hold... attribute, instead of Inactive[f] expression. This symbol should have no DownValues that could evaluate when arguments are passed to it.



Below I present two approaches to this problem. First is based on custom (in)activate functions, creating "dummy" symbols on the fly, for each inactivated symbol with Hold... attribute. Second attaches special inactivation behavior to specific symbols, which causes them to evaluate to custom inactive heads.


Basic examples


When we have a function with a Hold... attribute.


ClearAll[f]
SetAttributes[f, HoldFirst]
f[x_, y_] := Hold[x, y]

f[1 + 1, 2 + 2]
(* Hold[1 + 1, 4] *)


Ordinary inactivation of this function causes evaluation of its arguments:


Inactivate[f[1 + 1, 2 + 2], f]
% // FullForm
% // Activate
(* f[2, 4] *)
(* Inactive[f][2, 4] *)
(* Hold[2, 4] *)

so expression after inactivation and activation is different than if it would evaluate without inactivation involved.


With first, presented below, approach one can do:



holdingInactivate[f[1 + 1, 2 + 2], f]
% // FullForm
% // holdingActivate
(* f[1 + 1, 4] *)
(* inactive`Global`f[Plus[1, 1], 4] *)
(* Hold[1 + 1, 4] *)

with second approach:


setHoldingInactivation[f];
Inactivate[f[1 + 1, 2 + 2], f]

% // FullForm
% // Activate
(* f[1 + 1, 4] *)
(* holdingInactiveHoldFirst[Plus[1, 1], 4, Hold[Inactive[f]]] *)
(* Hold[1 + 1, 4] *)




Let's start with some helper symbols used in both approaches.


ClearAll[symPatt, holdAttrs, hasHoldAttrQ, getHoldAttr]

symPatt = Except[HoldPattern@Symbol[___], _Symbol];
holdAttrs = HoldFirst | HoldRest | HoldAll | HoldAllComplete;
hasHoldAttrQ = Function[, MemberQ[Attributes[#], holdAttrs], HoldFirst];
getHoldAttr = Function[, FirstCase[Attributes[#], holdAttrs], HoldFirst];

Dummy "inactive" symbols


In this approach we define custom holdingInactivate, holdingActivate and ignoringHoldingInactive functions that should be used instead of built-in Inactivate, Activate and IgnoringInactive.


holdingInactivate inactivates expression and replaces each inactive symbol, that has Hold... attribute, with symbol specially defined in inactive` context. This special symbol has same Hold... attribute as replaced one, and is formatted as inactive replaced symbol.


holdingActivate activates expression and replaces symbols from inactive` context with original symbols.


ignoringHoldingInactive returns IgnoringInactive expression with certain symbols replaced by Alternatives of original symbol and its dummy "inactive" counterpart, so returned pattern will match both active and inactive versions of expressions.



We start with some helper functions.


ClearAll[
$inactiveContext, inactiveSymbolQ, toInactiveSymbol, fromInactiveSymbol,
postprocessInactiveBoxes, defineInactiveSymbol, $inactivateExclusions,
$inactivateExclusionsHeld, $basicInactivePatternRules,
inactivePatternReplace
]

$inactiveContext = "inactive`";


inactiveSymbolQ[s:symPatt] := StringMatchQ[Context[s], $inactiveContext <> "*"]
inactiveSymbolQ[expr_] = False;

toInactiveSymbol[inactSym:symPatt?inactiveSymbolQ] := inactSym
toInactiveSymbol[s:symPatt] :=
Symbol[$inactiveContext <> Context[s] <> SymbolName@Unevaluated[s]]

fromInactiveSymbol[inactSym:symPatt?inactiveSymbolQ] :=
Symbol@StringJoin[
StringDrop[Context[inactSym], StringLength[$inactiveContext]],

SymbolName@Unevaluated[inactSym]
]
fromInactiveSymbol[s:symPatt] := s

SetAttributes[postprocessInactiveBoxes, HoldAllComplete]
postprocessInactiveBoxes[_, hISym_][
RowBox[{TemplateBox[tbArg_, "InactiveHead", opts___], "[", args___, "]"}]
] :=
With[{tooltip = ToString[Unevaluated[hISym], InputForm]},
RowBox[{

InterpretationBox[
TemplateBox[tbArg, "InactiveHead", Tooltip -> tooltip, opts],
hISym
],
"[", args, "]"
}]
]
postprocessInactiveBoxes[expr_, _][boxes_] := InterpretationBox[boxes, expr]

defineInactiveSymbol[h:symPatt /; Not@inactiveSymbolQ[h]] :=

With[{holdAttr = getHoldAttr[h]},
With[{hISym = toInactiveSymbol[h]},
ClearAll[hISym];
SetAttributes[hISym, holdAttr];
hISym /: MakeBoxes[expr : hISym[args___], form_] :=
postprocessInactiveBoxes[expr, hISym]@
MakeBoxes[Inactive[h][args], form];
hISym
] /; Not@MissingQ[holdAttr]
]


$inactivateExclusions =
Alternatives @@ Replace[Developer`$InactivateExclusions, {
{sym_, "Symbol"} :> sym,
{sym_, "Expression"} :> Blank[sym]
}, 1];

$inactivateExclusionsHeld =
Alternatives @@ Cases[Developer`$InactivateExclusions,
{

sym_ /; MemberQ[Attributes[sym], HoldAll | HoldAllComplete],
"Symbol"
} :> Blank[sym]
];

$basicInactivePatternRules = {
excl:$inactivateExclusions :> excl,
h:symPatt /; Not@inactiveSymbolQ[h] :>
With[{inactSym = toInactiveSymbol[h]}, h | inactSym /; True]
};


inactivePatternReplace[expr_] :=
Quiet[
Unevaluated[expr] /. {
(h : Condition | PatternTest | Repeated)[patt_, rest___] :>
With[{replaced = inactivePatternReplace[patt]},
h[replaced, rest] /; True
],
Verbatim[Pattern][name_, patt_] :>
With[{replaced = inactivePatternReplace[patt]},

Pattern[name, replaced] /; True
],
Verbatim[Verbatim][verb_] :>
With[
{replaced =
Unevaluated[verb] /. $basicInactivePatternRules
},
Verbatim[replaced] /; True
],
(bl : Blank | BlankSequence | BlankNullSequence)[

h:symPatt /; Not@inactiveSymbolQ[h]
] :>
With[{inactSym = toInactiveSymbol[h]},
bl[h] | bl[inactSym] /; True
],
Sequence @@ $basicInactivePatternRules
},
RuleDelayed::rhs
]


SetAttributes[{
inactiveSymbolQ, toInactiveSymbol, fromInactiveSymbol,
defineInactiveSymbol, inactivePatternReplace
}, HoldFirst]

Now three "public" functions.


ClearAll[holdingInactivate, holdingActivate, ignoringHoldingInactive]

SetAttributes[holdingInactivate, HoldFirst]
holdingInactivate[expr_, patt_:_, opts:OptionsPattern[Inactivate]] :=

Inactivate[Hold[expr], patt, opts] //.
Inactive[h:Except[Except[_Symbol] | Except[patt]]][args___] :>
With[{hISym = defineInactiveSymbol[h]},
hISym[args] /; MatchQ[hISym, symPatt]
] //
ReleaseHold

holdingActivate[expr_, patt_:_, opts:OptionsPattern[Activate]] :=
Activate[expr, patt, opts] /. h:patt?inactiveSymbolQ :> fromInactiveSymbol[h]


ignoringHoldingInactive[expr_] :=
IgnoringInactive[expr] /. {
excl:$inactivateExclusionsHeld | $inactivateExclusions :> excl,
(h:symPatt /; hasHoldAttrQ[h] && Not@inactiveSymbolQ[h])[args___] :>
HoldPattern@h[args]
} // Evaluate // inactivePatternReplace

Usage example:


ClearAll[f, g, h]
SetAttributes[f, HoldFirst]

SetAttributes[g, HoldAll]

testExpr = f[1 + 1, g[1 + 1, 1 + 1], 2, f[1 + 1]][h[2], 2, f[1 + 1]]
inactiveTestExpr = holdingInactivate[Evaluate[%], f | g | h]
% // FullForm
% // holdingActivate

print screen of inactivation with dummy symbols


Pattern wrapped with ignoringHoldingInactive will match both active and inactive versions of same expression:


MatchQ[testExpr, ignoringHoldingInactive[testExpr]]

(* True *)
MatchQ[inactiveTestExpr, ignoringHoldingInactive[testExpr]]
(* True *)

It can be also used to manipulate inactive expression:


inactiveTestExpr /. {
ignoringHoldingInactive[f[arg : 1 + _]] :> Hold[arg],
ignoringHoldingInactive[gExpr_g] :> 5 + gExpr
}


print screen of replacement using ignoringHoldingInactive


Attaching special inactivation behavior to symbols


In this approach we attach special behavior to some of symbols that are supposed to be inactivated. Inactivation and activation is performed using built-in Inactivate and Activate.


We set special UpValues, for e.g. f symbol, causing Inactive[f][args] to evaluate to holdingInactive...[args, Hold@Inactive[f]], where holdingInactive... is head with same Hold... attribute as f. We keep f itself in last argument of holdingInactive..., this way expression can be appropriately formatted and, when activated, can automatically evaluate to original f[args].


Since special behavior, during both inactivation and activation, depends on evaluation, Inactive[f][args] expressions will not be replaced by holdingInactive... expression if it's inside some holding wrapper itself. This will not cause any problems with evaluation of f's arguments (since it's in holding wrapper they will not evaluate), but under the hood inactive expression can be slightly inconsistent and can contain both Inactive[f][args] and holdingInactive...[args, Hold@Inactive[f]], which might be inconvenient when manipulating inactive expression. When part of inactive expression is allowed to evaluate, then held, then activated, it may happen that we end up with expression containing holdingInactive... that will remain there until it's allowed to evaluate.


ClearAll[holdingInactive, setHoldingInactivation]

(* Define four holdingInactive... functions one for each Hold... atribute. *)
Scan[
With[{head = Symbol["holdingInactive"<>ToString[#]]},

ClearAll[head];
SetAttributes[head, #];
head[args___, Hold[h:Except@Inactive[_]]] := h[args];
head /: MakeBoxes[expr:head[args___, Hold[h:Inactive[_]]], form_] :=
InterpretationBox[#, expr]&@MakeBoxes[h[args], form];
holdingInactive[#] = head
]&,
holdAttrs
]


SetAttributes[setHoldingInactivation, Listable]
setHoldingInactivation[h_Symbol] :=
With[{holdAttr = getHoldAttr[h]},
With[{holdingInactiveFunc = holdingInactive[holdAttr]},
h /: Inactive[h] =
Function[,
holdingInactiveFunc[##, Hold@Inactive[h]],
HoldAllComplete
];
h

] /; Not@MissingQ[holdAttr]
]

Usage example:


ClearAll[f, g, h]
SetAttributes[f, HoldFirst]
SetAttributes[g, HoldAll]

setHoldingInactivation[{f, g}]
(* {f, g} *)


f[1 + 1, g[1 + 1, 1 + 1], 2, f[1 + 1]][h[2], 2, f[1 + 1]]
Inactivate[Evaluate[%], f | g | h]
% // FullForm
% // Activate

print screen of result with custom inactive heads


Comments

Popular posts from this blog

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...