21

I have three expressions a[x, y], b[x, y], c[x, y] that act as placeholders for functions of two variables x,y. Consider the following substitution:

a[x, y]/(b[x, y] c[x, y]) /. f_[x1_, y1_] :> f[2 x1, 3 y1]

a[2 x, 3 y]/(64 b[x, y]^3 c[x, y]^3)

In the output we see that the numerator expression was substituted properly, but in the denominator the pattern f_ registered for the head Power instead of looking for my own expressions. Of course I can fix this by:

a[x, y]/(b[x, y] c[x, y]) /. a[x1_, y1_] :> a[2 x1, 3 y1] /.b[x1_, y1_] :> b[2 x1, 3 y1] /. c[x1_, y1_] :> c[2 x1, 3 y1]

a[2 x, 3 y]/(b[2 x, 3 y] c[2 x, 3 y])

which gives the desired output. But this amounts to writing three times as many substitution directives and is therefore inconvenient. To fix the first example, I tried using /. f_Symbol[x1_, y1_] :> f[2 x1, 3 y1] or /. f_[x1_, y1_]/;Head[f]===Symbol :> f[2 x1, 3 y1], but this does not correct it. Is there a way to write a proper substitution that works with headers and does not act on built in functions? Thanks for any suggestions.

EDIT:

Just noticed that Head[Power] actually returns Symbol, which is kind of weird. I would have expected it to return e.g. Function, or Directive, or something along the lines. (If one unprotects and clears the Power function, then I would again expect Head[Power] to return Symbol of course. But maybe that's just me...)

Kagaratsch
  • 11,955
  • 4
  • 25
  • 72
  • 2
    This doesn't answer your question, but if you want to match a defined set of functions without writing a rule for each, you could replace f_ by (f:(a|b|c)) – 2012rcampion Nov 24 '16 at 05:50
  • 1
    Product is not a head that ever appears in these expressions. This can be seen by using FullForm or FreeQ[a[x, y]/(b[x, y] c[x, y]), Product]. It is a little bit tricky to see what heads f_ will be matched with, as things depend on evaluation, but the heads are a, Power and Power. This can be seen from Reap[a[x, y]/(b[x, y] c[x, y]) /. f_[x1_, y1_] :> Sow[f]][[2, 1]] . – Jacob Akkerboom Nov 24 '16 at 08:56
  • 1
    @JacobAkkerboom Thank you for pointing that out! I updated the question to properly refer to Power head instead of Product. (The problem stays the same though.) – Kagaratsch Nov 30 '16 at 21:04

4 Answers4

23

The best method I am aware of to handle this kind of problem is to filter by context.(1)

SetAttributes[user, HoldFirst]
user[s_Symbol] := Context@Unevaluated@s =!= "System`";

a[x, y]/(b[x, y] c[x, y]) /. f_?user[x1_, y1_] :> f[2 x1, 3 y1]
a[2 x, 3 y]/(b[2 x, 3 y] c[2 x, 3 y])

One could include other contexts in the exclusion besides System, or use the inverse and test only for user symbols existing in the "Global`" context. Without additional examples my example is as specific as I can make it.


Regarding the unusual evaluation of the ? operator (PatternTest) please see:

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
13

You can impose conditions on the patterns to restrict their matching:

a[x, y]/(b[x, y] c[x, y]) /. 
 f_?(MemberQ[{a, b, c}, #] &)[x1_, y1_] :> f[2 x1, 3 y1]

or the alternative, equivalent:

a[x, y]/(b[x, y] c[x, y]) /. 
 f_[x1_, y1_] :> f[2 x1, 3 y1] /; MemberQ[{a, b, c}, f]

Both expressions return your desired result:

Mathematica graphics

MarcoB
  • 67,153
  • 18
  • 91
  • 189
12

Because built-in functions are Protected, the following also works.

a[x, y]/(b[x, y] c[x, y]) /. 
    f_[x1_, y1_] :> f[2 x1, 3 y1] /; ! MemberQ[Attributes[f], Protected]

(* a[2 x, 3 y]/(b[2 x, 3 y] c[2 x, 3 y]) *)
bbgodfrey
  • 61,439
  • 17
  • 89
  • 156
0

You could also use the feature that all built-in functions start with capital letters. if you maintain your user functions start with lowercase letters you can do this:

SetAttributes[user, HoldFirst] 
user[s_Symbol] := Capitalize[ToString[s]] =!= ToString[s]; 
a[x, y]/(b[x, y] c[x, y]) /. f_?user[x1_, y1_] :> f[2 x1, 3 y1]

a[2 x, 3 y]/(b[2 x, 3 y] c[2 x, 3 y])
Basheer Algohi
  • 19,917
  • 1
  • 31
  • 78