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 aDo
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 (aDo
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.
Sequence
it might be easier $\endgroup$Sequence
at all. $\endgroup$inject[{aa -> {3, xx}}, Hold@With[{xx = 8}, Hold[{1, 2, aa}]]]
$\endgroup$