8

I am failing to write a Mathematica transformation rule that replaces e.g. f[a + 3, b + 3, c + 3] with f[a, b, c] + 3 for an arbitrary number of arguments. However, f[a + 3, b + 3, c + 2] should remain untouched after the transformation.

olafur
  • 81
  • 3
  • 3
    f[a + 3, b + 3, c + 3] /. f[pat : (_ + n_) ..] :> f @@ ({pat} - n) + n. Would you be willing to show what you've tried so that we can get a handle on where you're at, Mathematica-knowledge-wise? – march Jul 26 '16 at 17:57
  • 2
    In addition, can you expand on your problem? It's not specified enough, and so it's quite possible that people will spend time figuring out how to answer your question, and then you'll show a different example on which it fails. Or maybe it'll be a case where you want to specify what number (e.g. 3) gets pulled out. Please include more information in your post, and then I will consider posting an answer. – march Jul 26 '16 at 18:01
  • 2
    @march Your proposed replacement doesn't seem to work for me. Could you check it? – MarcoB Jul 26 '16 at 18:03
  • @MarcoB. I copied and pasted from my comment, and it worked for me! Are you trying it with some other input? – march Jul 26 '16 at 18:04
  • @march That's weird. Here's what I see: image. I'm on MMA 10.4.0 / Win7-64. – MarcoB Jul 26 '16 at 18:07
  • @MarcoB. Yep that's weird. I have 10.0.1 on Mac OSX 10.10.5. Here's what I get. Does adding lots of parentheses in various places fix the problem? Maybe this? f[a + 3, b + 3, c + 3] /. f[pat : ((_ + n_) ..)] :> n + f @@ ({pat} - n) – march Jul 26 '16 at 18:10
  • @march That's rather bizarre. Gotta go to lunch now, but I'll play with it a bit more later. – MarcoB Jul 26 '16 at 18:13
  • I cannot see why march's code should not work, yet on my system also (10.1.0 under Windows 7 x64) it does not. – Mr.Wizard Jul 26 '16 at 18:24
  • Thanks a lot for your replies. I experience the same problem with march's code. However, it works if I modify to to a + 3, b + 3, c + 3] /. f[pat : (n_Integer + _) ..] :> f @@ ({pat} - n) + n. – olafur Jul 26 '16 at 18:32
  • 2
    @Mr.Wizard and rest, isn't it a reordering somewhere what causes that? _+n_ stays the same while a+3 is 3+a so repeated element is the first one, not the second like in pattern. This works well MatchQ[ f[a + 3, b + 3, c + 3], f[Verbatim[Plus][n_, _] ..] ] but I don't know how OS affects that. – Kuba Jul 26 '16 at 18:35
  • @Kuba I thought the Orderless attribute of Plus would handle that but I guess not. I really should know this stuff better. :-/ I confirm your MatchQ is true. – Mr.Wizard Jul 26 '16 at 18:37
  • 2
    @march Here is your replacement rule compatible with Windows :P f[p : Verbatim[Plus][___, n_, ___] ..] :> n + f @@ ({p} - n). Please consider posting an answer. – Kuba Jul 26 '16 at 18:40
  • @Kuba I think I am forgetting my own answers: (94432) – Mr.Wizard Jul 26 '16 at 18:41
  • @Kuba Realize that by making this order-dependent your replacement will not work on f[1 + x, 2 + x, 3 + x] for example. – Mr.Wizard Jul 26 '16 at 18:49
  • @Mr.Wizard Now I don't understand why this does not match ... f[Verbatim[Plus][___, n_, ___] ..] – Kuba Jul 26 '16 at 18:57
  • @Kuba Me neither. I feel like I have been nerd sniped. :^) – Mr.Wizard Jul 26 '16 at 19:02
  • 1
    @Kuba Mr.Wizard: Perhaps I will avoid posting an answer until this is somewhat cleared up. However, does a function deconstruction version work or does it suffer from the same problem? f[pat : (_ + n_) ..] := f @@ ({pat} - n) + n; f[a + 3, b + 3, c + 3]. You could probably do something like SetAttributes[f, HoldFirst] to make it work... – march Jul 26 '16 at 19:04
  • @Mr.Wizard. I tried my code in V8 on a Windows machine, and it still worked :/ – march Jul 26 '16 at 20:28
  • @march The code from your original comment also works as written in version 7.0.1 under Windows. Something has changed and we need to know what and why. Do you care to post a question about this? I expect it is going to end up popular and I think you deserve the credit for bringing it to our attention, even if unintentionally. – Mr.Wizard Jul 26 '16 at 21:50
  • 1
    @Mr.Wizard. Will do. Feel free to edit it once it's up, since I'm not precisely sure how to ask the question. We'll see how it goes. – march Jul 26 '16 at 21:51
  • @Mr.Wizard. Also, since I can't check which things don't work (they all work for me on all of my machines and in all of the versions I have access to), I can only use the examples listed in this thread. That will have to do! – march Jul 26 '16 at 21:53
  • @Mr.Wizard. I have posted the question here. Feel free to edit (including the title, since I'm not sure that it's the Orderless attribute that is the problem). – march Jul 26 '16 at 22:14

3 Answers3

5

Since the problem is specified without context, here is a very specific solution:

expr = f[a + 3, b + 3, c + 3, d + 3, e + 3];

Thread[expr, Plus] /. f[n_ ..] :> n
3 + f[a, b, c, d, e]

Leaving aside the curious question of pattern matching raised in the comments here is a baroque approach that should at least be applicable in a number of cases.

rep[a : f[__Plus]] :=
  With[
    {out = Plus @@ f @@@ Factor[Plus @@ Times @@@ (a /. n_?NumericQ :> Hold[n])]},
    (out /. {Hold[n_] :> n, f[x_] :> x}) /; Length[out] == 2
  ]

Test:

f[a + 3, b + 3, c + 3, 3 + d] // rep
f[1 + x, 2 + x, 3 + x]        // rep
f[1 + x, 2 + x, x + y]        // rep
3 + f[a, b, c, d]

x + f[1, 2, 3]

x + f[y, 1, 2]

There must be a better way but I am too distracted by the patten matching issue to refine this now.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Thanks! It works great for the question I posed. I added a second example to clarify what I had initially omitted to explain. – olafur Jul 26 '16 at 18:28
4

Maybe this will fit your needs

ReplaceAll[
 { 
  f[a + 3, b + 3, c + 3], 
  f[x + 3, x + 3, x + 1], 
  f[x + 2, y + 3, x + 1]
 },
 f[p__] :> With[{c = Intersection[p]}, c + f @@ ({p} - c)]
    (*thanks to J.M.*)
 ]
]
{
   3 + f[a, b, c], 
   x + f[3, 3, 1], 
   f[2 + x, 3 + y, 1 + x] 
}

Earlier I overdid it with f[p__] :> With[{c = Plus @@ Intersection @@ (List @@@ {p})}, f @@ (# - c & /@ {p}) + c

Kuba
  • 136,707
  • 13
  • 279
  • 740
0

With a typed pattern.

ClearAll[Evaluate[Context[] <> "*"]];
k = 1;
ReplaceAll[
 {f[a + 1, b + 1, k + c], g[x - π, y - π, -π + z], h[u + 1, v + 2, w + 3]},
 f_[p : ((_Symbol + n_?NumericQ) ..)] :> n + f[Sequence @@ Cases[{p}, _Symbol, 2]]
 ]

(* {1 + f[a, b, c], -π + g[x, y, z], h[1 + u, 2 + v, 3 + w]} *)

Hope this helps.

Edmund
  • 42,267
  • 3
  • 51
  • 143