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.
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
Post a Comment