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$
3
  • 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$ Commented Nov 24, 2016 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$ Commented Nov 24, 2016 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
    Commented Nov 30, 2016 at 21:04

4 Answers 4

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$
5
  • 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
    Commented Dec 1, 2016 at 8:46
  • $\begingroup$ @Kuba I needed a second look as well; though I'm no expert in any case. $\endgroup$
    – Edmund
    Commented Dec 1, 2016 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
    Commented Dec 1, 2016 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
    Commented Dec 1, 2016 at 10:58
  • $\begingroup$ @Kuba Good point; link added. $\endgroup$
    – Mr.Wizard
    Commented Dec 1, 2016 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
  • 3
    $\begingroup$ Every symbol can be protected. $\endgroup$
    – Kuba
    Commented Dec 1, 2016 at 8:44
  • $\begingroup$ Unless it is already Locked :) $\endgroup$
    – Kuba
    Commented Dec 1, 2016 at 11:11
  • $\begingroup$ @Kuba What you say is true. Good comment. $\endgroup$
    – bbgodfrey
    Commented Dec 1, 2016 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 and acknowledge you have read our privacy policy.

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