Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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.

share|improve this question
    
If you don't insist on Sequence it might be easier –  acl Feb 17 '12 at 18:43
    
If you can solve this in a direct and robust way I think it will call into question the need for Sequence at all. –  Mr.Wizard Feb 17 '12 at 19:22
    
I just read this. You have plenty of answers already. I just wanted to point out that those answers that rely on building the expression on the rhs of a rule, such as the firsts of MrWizard, respect scoping so you can get things renamed if you are injecting inside a scoping construct. This means that your inject might too. Try inject[{aa -> {3, xx}}, Hold@With[{xx = 8}, Hold[{1, 2, aa}]]] –  Rojo Jul 17 '12 at 13:49

9 Answers 9

up vote 35 down vote accepted
{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}]
share|improve this answer
    
very cool actually –  acl Feb 17 '12 at 20:30
    
@acl I lied though: it still uses Sequence, just in the form of BlankSequence. halirutan uses SlotSequence. So I think it is not possible without Sequence of one brand or another. –  Mr.Wizard Feb 17 '12 at 20:32
    
Yes, but that's beyond the point. One could also do all this by converting to strings and replacing, and to me the other answers seem almost as clumsy as that. You do it with a much lighter touch. –  acl Feb 17 '12 at 20:36

Not sure how robust this is, but you could do something like

flattenSequence[expr_, {x_, p__}] := Module[{f, t},
  f[t__] = expr /. x -> t;
  f[p]]

Then for the example above

flattenSequence[Hold[{1, 2, x}], {x, 3, 4}]
Hold[{1, 2, 3, 4}]
share|improve this answer

One way is to use Function and the possibility of SlotSequence. I define an additional function f to be sure nothing gets evaluated:

f[x_] := Print["Evaluated"];
Function[Hold[Do[f[1], ##]]][{i, 5}, {j, 5}]

(*
  Hold[Do[f[1], {i, 5}, {j, 5}]]
*)
share|improve this answer
6  
This will have a problem when i or j have values before evaluation. You can use Function[Null,Hold[Do[f[1],##]],HoldAll] to avoid it. –  Leonid Shifrin Feb 17 '12 at 19:59

How about this:

ClearAll[inject];
SetAttributes[inject, HoldRest];
inject[Hold[{args__}], new__] := Hold[{args, new}]

This will also accept Sequence[3,4] as a second argument. Sequences are spliced, while arguments themselves not evaluated.

EDIT

You can also use a composite rule, with some head s instead of Sequence (you can localize s if needed):

Hold[{1, 2, x}] /. x -> s[3, 4] /. 
  f_[left___, s[middle___], right___] :> f[left, middle, right]
share|improve this answer
    
This is exactly what I meant when I said "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." This will be ugly with the Do. –  Szabolcs Feb 17 '12 at 19:58
    
@Szabolcs Does my update address you needs? You could also wrap s around your Sequence, if you naturally get your results as a Sequence –  Leonid Shifrin Feb 17 '12 at 20:07
    
Yes, it does. This can be expanded into a function that will flatten out all Sequence elements in an arbitrary held expression (i.e. it can made into a self-contained and general tool that integrates well with other solutions) –  Szabolcs Feb 17 '12 at 21:08
    
I got mixed up with so many answers, with that syntax I don't see any scoping trouble. I'm deleting the comment –  Rojo Jul 17 '12 at 13:48
    
@Rojo Ok, I deleted mine as well. –  Leonid Shifrin Jul 17 '12 at 13:56

Neat solutions provided, but there are probably more straight forward ways to solve the original problem. One of these might help.

In:= s=0; Apply[Do[s+=i^i,{i,##}]&,Hold[1,12,3]]; s  

Out[75]= 10000823800  


In:= r=Hold[s=0;{1,12,3};s];  
Part[r,1,2,0]=(Do[s+=i^i,{i,##}]&); ReleaseHold[r]  

Out= 10000823800  
share|improve this answer

I remarked before that I didn't think this was possible without Sequence, SlotSequence, BlankSequence, etc. (Without using string processing or the like that is.) It seems I was wrong, unless there is an implicit Sequence in here:

Hold[1 + 1, 2 + 2, #] & @ Unevaluated[3 + 3, 4 + 4]
Hold[1 + 1, 2 + 2, 3 + 3, 4 + 4]
share|improve this answer
    
This works fine because you are injecting at first level inside Hold. See my following answer for clarifications. –  Federico Mar 22 '13 at 23:05

Perhaps something like this?

ClearAll[replaceFlatteningSequences];
replaceFlatteningSequences[lhs_, pat_ :> rhs_] /; MatchQ[lhs, pat] := 
 lhs /. lhs -> rhs
replaceFlatteningSequences[lhs_, pat_ :> Sequence[repSeq__]] := 
 Module[{tag},
  lhs /. {Slot -> tag, SlotSequence -> tag["Sequence"], 
        Function -> tag["Function"]} /. pat :> ## /. 
     all_ :> (all &[repSeq]) /. {tag["Function"] -> Function, 
     tag["Sequence"] -> SlotSequence} /. tag -> Slot
  ]

To be used

replaceFlatteningSequences[Hold@With[{x = 8}, ## aa &], 
 aa :> Sequence[x, 4]]

Hold[With[{x = 8}, ##1 x 4 &]]

share|improve this answer

In a previous answer Mr.Wizard suggested

Hold[1 + 1, 2 + 2, #] &@Unevaluated[3 + 3, 4 + 4]

However injecting deeper inside a Hold with this technique does not work:

Hold[{1 + 1, 2 + 2, #}] &@Unevaluated[3 + 3, 4 + 4]

returns Hold[{1 + 1, 2 + 2, Sequence[3 + 3, 4 + 4]}].

I would like to point out that a little variation does indeed work:

Hold[{1 + 1, 2 + 2, ##}] &[3 + 3, 4 + 4]

And if one does indeed want the arguments not to be evaluated, then it is possible to use

Function[Null, Hold[{1 + 1, 2 + 2, SlotSequence[1]}], {HoldAll}][3 + 3, 4 + 4]
share|improve this answer

How about Hold[{1, 2, x}] /. {a : ___, x, b : ___} :> {a, 3, 4, b}

share|improve this answer
    
Isn't this just a rephrasing of Mr.Wizard's answer? –  Oleksandr R. Apr 26 '13 at 13:21
    
Sorry, I haven't noticed that. How to delete this post? Sorry for making duplicate answer. –  Life Apr 27 '13 at 4:26
    
Not to worry--happens to us all! You should be able to delete your own post, but if not, flag it for moderator attention and ask for it to be deleted. –  Oleksandr R. Apr 29 '13 at 0:30

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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