21
$\begingroup$

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...)

$\endgroup$
  • 2
    $\begingroup$ 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)) $\endgroup$ – 2012rcampion Nov 24 '16 at 5:50
  • 1
    $\begingroup$ 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]] . $\endgroup$ – Jacob Akkerboom Nov 24 '16 at 8:56
  • 1
    $\begingroup$ @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.) $\endgroup$ – Kagaratsch Nov 30 '16 at 21:04
23
$\begingroup$

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:

$\endgroup$
  • 1
    $\begingroup$ Worth to stress out that f_?user[x1_, y1_] works like (f_?user)[x1_, y1_]. Or maybe it is just me who was surprised when have faced that for the first time :) $\endgroup$ – Kuba Dec 1 '16 at 8:46
  • $\begingroup$ @Kuba I needed a second look as well; though I'm no expert in any case. $\endgroup$ – Edmund Dec 1 '16 at 10:47
  • $\begingroup$ It occurs to me that this will not work for functions from included in Wolfram System Standard Extra Packages that have not yet been rolled into the System` context. $\endgroup$ – Edmund Dec 1 '16 at 10:54
  • 1
    $\begingroup$ @Edmund Yes, you can modify it with MemberQ but there is no clear distinction between Standard Extra Package and a custom one. $\endgroup$ – Kuba Dec 1 '16 at 10:58
  • $\begingroup$ @Kuba Good point; link added. $\endgroup$ – Mr.Wizard Dec 1 '16 at 20:24
13
$\begingroup$

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

$\endgroup$
12
$\begingroup$

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]) *)
$\endgroup$
  • 3
    $\begingroup$ Every symbol can be protected. $\endgroup$ – Kuba Dec 1 '16 at 8:44
  • $\begingroup$ Unless it is already Locked :) $\endgroup$ – Kuba Dec 1 '16 at 11:11
  • $\begingroup$ @Kuba What you say is true. Good comment. $\endgroup$ – bbgodfrey Dec 1 '16 at 13:23
0
$\begingroup$

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])
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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