Skip to main content

evaluation - Injecting a sequence of expressions into a held expression


Consider the following toy example:


Hold[{1, 2, x}] /. x -> Sequence[3, 4]

It will give


Hold[{1, 2, Sequence[3, 4]}]


because Sequence[] (like Unevaluated) is expanded only in the first level of heads with attribute HoldAll.


How can I obtain Hold[{1,2,3,4}]? What is the simplest way to do this?


Notes:




  • Use case: I am trying to generate a piece of code that will be passed to Compile. I need to inject a variable number of iterators (which I have as a list) into a Do expression:


    Hold[Do[code, iterators]] /. iterators -> Sequence[{i,5}, {j,5}]


  • I would prefers solutions that don't match on the expression enclosing x. I would not like to repeat this expression (a Do in this case) in my code.





  • It's perhaps worth pointing out that


    Hold[{1, 2, f[3, 4]}] //. f[x___] :> x

    returns


    Hold[{1, 2, Sequence[3, 4]}]

    so I can't easily implement a manual sequence-flattening step.







Answers


Based on Leonid's code we can write a flattenSequence[] function that will flatten out all Sequence expressions at any level:


flattenSequence[expr_] := 
expr //. f_[left___, Verbatim[Sequence][middle___], right___] :>
f[left, middle, right]

flattenSequence[Hold[{1, Sequence[2, 3]}]]


(* ==> Hold[{1, 2, 3}] *)

Based on Mr.Wizard's code we can write a general function for injecting subexpressions into other expressions while supporting Sequence:


ClearAll[inject1, inject]

SetAttributes[inject1, HoldFirst]
Quiet[
inject1[expr_, (Rule|RuleDelayed)[var_Symbol, values : Verbatim[Sequence][__]]] :=
Replace[Unevaluated[values], Sequence[var__] :> expr];
inject1[expr_, (Rule|RuleDelayed)[var_Symbol, value_]] :=

Replace[Unevaluated[value], var_ :> expr],

{RuleDelayed::rhs}
]

SetAttributes[inject, HoldAll]
inject[rules_, expr_] :=
Internal`InheritedBlock[
{Rule, RuleDelayed},
SetAttributes[{Rule, RuleDelayed}, HoldFirst];

ReleaseHold@Fold[inject1, HoldComplete[expr], rules]
]

Usage:


inject[{a -> Sequence[b, 3], b :> 1 + 1}, Hold[{a, b}]]

(* ==> Hold[{1 + 1, 3, 1 + 1}] *)

The replacements are done one after the other, so the second one can use the result of the first. Rule and RuleDelayed are both handled correctly.



Answer




{3, 4} /. {x__} :> Hold[{1, 2, x}]


Hold[{1, 2, 3, 4}]

Leonid Shifrin used this here long before I wrote this answer.




In light of Leonid's comment to halirutan it is worth pointing out that you can inject expressions from an arbitrary head including Hold. You can also use -> rather than :> like this:


expr = Hold[{1, 2, x}];


Hold[6/2, 2 + 2] /. _[x__] -> expr


Hold[{1, 2, 6/2, 2 + 2}]

Comments