10

Consider the following expression:

expr = Hold[
   {
    f[
     {1, {Print@1}}
     ],
    g[
     {{{Print@1}}}
     ]
    }
   ];

I'm looking for a way to apply a replacement rule to the contents of all expressions with head f. To give a more concrete example, let's say I want to replace Print by Echo, but only inside f.

Restrictions: I have no knowledge about the exact structure around and inside f - there could be more or less nesting going on. This means the replacement rule can't capture the wrapper with Hold attribute, nor can it capture the individual parts of f[…]. (And the evaluation of Print should be prevented of course)

Kuba
  • 136,707
  • 13
  • 279
  • 740
Lukas Lang
  • 33,963
  • 1
  • 51
  • 97

3 Answers3

11

Here's a way making use of Block to cause f to be inert:

Block[{f},
 SetAttributes[f, HoldAllComplete];
 expr /. f[args__] :>
   RuleCondition[f[args] /. Print -> Echo]
 ]

Hold[{f[{1, {Echo[1]}}], g[{{{Print[1]}}}]}]

Note of course that this only works if you have a pattern of the form _Symbol[...]

b3m2a1
  • 46,870
  • 3
  • 92
  • 239
8

[Edit: For most situations, @Kuba's answer is better]

I can think of one (ugly) way to do it:

Attributes[myHold] = {HoldAll};

expr /.
 f[args__] :> With[
   {res = myHold[args] /. Print -> Echo},
   f @@ res /; True
 ] /.
  HoldPattern[f_ @@ myHold[args__]] :> f[args]
(* Hold[{f[{1, {Echo[1]}}], g[{{{Print[1]}}}]}] *)

The idea is to wrap the contents of f inside a function with HoldAll attribute (not Hold, to be able to identify it uniquely later on). In a first step, the expression is returned with myHold[…] still in place. In a second round of replacements, myHold is stripped out again.

Lukas Lang
  • 33,963
  • 1
  • 51
  • 97
  • 3
    +1. If we use the vanishing macro defined in (43096), we can write vanishing[{h}, expr /. x_f :> RuleCondition[h[x] /. Print -> Echo]]. – WReach Jan 05 '19 at 18:16
8

Alternatively:

expr /. 
  foo_f :> RuleCondition[Hold[foo] /. Print -> Echo] /. 
  Hold[foo_f] :> foo 

We can safely perform the second replacement because we just wrapped every f[..] with Hold.

Kuba
  • 136,707
  • 13
  • 279
  • 740
  • +1 - In general, this is definitely better than my solution - the only potential issue is the fact that the pattern _f has to be written twice - so for complex patterns, this might lead to some code-duplication/performance cost – Lukas Lang Jan 07 '19 at 09:18
  • @LukasLang yes, consider this a quick solution to a specific problem. In general something like b3m2a1 shows is needed or a dummy vanishing head. – Kuba Jan 07 '19 at 09:21