4
$\begingroup$

I'm aware that Replace and ReplaceAll behave differently as discussed in this question, but still get confused with the following example when Flat is involved:

f//Attributes = {Flat, OneIdentity}; Replace[f[x, 2 y, z], f[x_, 2 y_] :> f[x, y], {0, Infinity}] (* f[x, 2 y, z] *) ReplaceAll[f[x, 2 y, z], f[x_, 2 y_] :> f[x, y]] (* f[x, y, z] *) 

The result of ReplaceAll is as expected, but why does Replace not try the pattern f[f[x, 2y], z]? Is there a simple workaround for Replace without modifying the rule?


Update 1

A simpler example by @user293787:

SetAttributes[f, Flat]; ReplaceAll[f[a,b],f[a]->0] (*f[0,b]*) Replace[f[a,b],f[a]->0,All] (*f[a,b]*) 

Update 2

It seems that in the sequence inside f, a non-Pattern head will stop the matching from left to right, e.g.

Replace[f[a,b,c],f[a_,b]->g[a,b],All] (*f[a, b, c]*) Replace[f[a,b,c],f[a_,c]:>g[a,c],All] (*g[f[a, b], c]*) 

(the update 2 is from part of my answer, in which the solution is totally wrong. I delete it to avoid confusions.)

$\endgroup$
4
  • 1
    $\begingroup$ That is an interesting observation. Related: f[a,b]/.f[a]->0 gives f[0,b] but Replace[f[a,b],f[a]->0,{0,Infinity}] gives f[a,b]. $\endgroup$ Commented Sep 16, 2022 at 17:29
  • 1
    $\begingroup$ Tangentially related: In this post from 9 years ago they say that SetAttributes[M,{Flat}];Replace[M[F1[a]],M[x_F1]|M[x_F2]->x] returns M[F1[a]], but if I run it now I get F1[a]. Apparently this was "fixed". Perhaps you found a similar problem. $\endgroup$ Commented Sep 16, 2022 at 17:40
  • $\begingroup$ @user293787, thx! I'll add your simpler example to the question. $\endgroup$ Commented Sep 16, 2022 at 17:51
  • $\begingroup$ @user293787 Yes, that was fixed. Or possibly "fixed", as you put it; I'm never certain about these changes when it comes to the pattern matcher. $\endgroup$ Commented Sep 18, 2022 at 16:11

2 Answers 2

2
$\begingroup$

For a way to explain the behavior, see @MichalE2's answer

The following might be a workaround, at least for the examples in the question. It is based on the observation that a catch-all rule like x_:>x stops ReplaceAll from matching any parts not on the top level, but doesn't stop "Flat-induced" matches:

f // Attributes = {Flat, OneIdentity}; ReplaceAll[f[x, 2 y, z], {f[x_, 2 y_] :> f[x, y], x_ :> x}] (* f[x, y, z] *) ReplaceAll[{f[x, 2 y, z]}, {f[x_, 2 y_] :> f[x, y], x_ :> x}] (* {f[x, 2 y, z]} *) 

Thus:

myReplace[expr_, rules_, level_ : {0}] := Map[ReplaceAll[Flatten@{rules, x_ :> x}], expr, level] myReplace[f[x, 2 y, z], f[x_, 2 y_] :> f[x, y]] (* f[x, y, z] *) myReplace[{f[x, 2 y, z]}, f[x_, 2 y_] :> f[x, y]] (* {f[x, 2 y, z]} *) myReplace[{f[x, 2 y, z]}, f[x_, 2 y_] :> f[x, y], 1] (* {f[x, y, z]} *) 
$\endgroup$
4
  • 2
    $\begingroup$ Nice. (+1) I've used this trick in other situations. Somehow if replacement is on my mind, I usually do myReplace like this: myReplace[expr_, rules_, level_ : {0}] := Replace[expr, e_ :> (e /. Flatten@{rules, x_ :> x}), level]. I can't think of any advantage of one over the other, though. $\endgroup$ Commented Sep 18, 2022 at 14:53
  • $\begingroup$ @MichaelE2 I guess a minor advantage is the fact that it is more explicit that the level specification is applied correctly. On the other hand, I do find it slightly harder to mentally parse. Other than that, they should indeed behave equivalently as far as I can tell $\endgroup$ Commented Sep 18, 2022 at 15:55
  • $\begingroup$ I didn't realize there was any difference in how they handled the level spec. (Or I've forgotten the difference.) $\endgroup$ Commented Sep 18, 2022 at 16:25
  • $\begingroup$ @MichaelE2 Sorry for the confusion - I meant to say that while the levelspec behavior is the same for Map and Replace (as far as I know), it's more obvious that you get the correct behavior if you do actually use Replace $\endgroup$ Commented Sep 18, 2022 at 17:11
2
$\begingroup$

why does Replace not try the pattern f[f[x, 2y], z]?

First, I don't think ReplaceAll does this either. It matches the argument sequence x, 2y by virtue of the Flat attribute and replaces it by f[x, y] according to the OP's rule. Alternatively, we could look at it as matching Part[f[x, 2y, z], ;; 2]. Either, I don't think pattern matcher unflattens a flat function. (This is undocumented AFAIK, so I could be mistaken.)

It can do something like the operation in the quote above when carrying out the replacement. Using TracePrint on f[w, x, 2 y, z] and f[v, w, x, 2 y, z], we see that x_ in f[x_,2 y_]:>f[x,y] matches w, x and replaces them with Part[f[w, x, 2y, z][[;; 2] or f[w, x], and similarly for v, w, xandf[v, w, x]`.

f[w,x,2 y,z] /. f[x_,2 y_]:>f[x,y] f[f[f[w,x],y],z] f[v,w,x,2 y,z] /. f[x_,2 y_]:>f[x,y] f[f[f[v,w,x],y],z] 

Next to the docs for Replace:

Replace by default applies rules only to complete expressions

Of course x, 2y is not a complete expression, and arguably Part[f[x, 2 y, z], ;; 2] is part of an expression, not a complete expression at any level of f[x, 2y, z].

Unfortunately, the site users who seemed to have authoritative knowledge of the pattern matcher also seem to have left the site. :(

Workaround?

I'd use this, but maybe I'm missing something about what the OP wants:

Replace[f[x, 2 y, z], f[x_, 2 y_, foo___] :> f[x, y, foo]] 
$\endgroup$
5
  • 1
    $\begingroup$ +1. While I think your explanation makes sense, I don't like what it is implying. In my mind, Replace is a more controlled version of ReplaceAll. However, this behavior means that it is impossible to faithfully apply DownValues of a symbol at the top-level only (besides trying to check the expression for its head beforehand or similar). Also, the documentation is not really clear on this (as you point out): For instance, from the documentation of Flat: "Flat is an attribute... This property is accounted for in pattern matching." - the latter doesn't really seem to be true here $\endgroup$ Commented Sep 17, 2022 at 23:56
  • $\begingroup$ @LukasLang Yes, that seems to be the case. $\endgroup$ Commented Sep 18, 2022 at 1:33
  • $\begingroup$ Thx! In my actual usage the rule is a long list. $\endgroup$ Commented Sep 18, 2022 at 9:46
  • $\begingroup$ I was unaware that anyone had authoritative knowledge of the pattern matcher. I'm not sure such a background can exist, though to be fair I'm not aware of any non-existence proof either. (Me, I just fix the bugs, I don't pretend to understand much of it. In particular I don't understand the inner doings of Flat which, as best I can tell, is the pattern matcher's equivalent to the human limbic system.) $\endgroup$ Commented Sep 18, 2022 at 16:15
  • $\begingroup$ @DanielLichtblau They seemed to know. And when they corrected me, often rather quickly, they seemed to be right. How they came by that knowledge, I don't know. Whether one wants to call it "authoritative," or "divine," or whatever, their knowledge exceeded mine and would still, I believe. (It was a wistful remark recalling good times, which also was meant to invite anyone with a different understanding to disagree and educate me.) $\endgroup$ Commented Sep 18, 2022 at 16:40

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.