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$
    – user293787
    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$
    – user293787
    Commented Sep 16, 2022 at 17:40
  • $\begingroup$ @user293787, thx! I'll add your simpler example to the question. $\endgroup$
    – Lacia
    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$
    – Michael E2
    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$
    – Lukas Lang
    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$
    – Michael E2
    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$
    – Lukas Lang
    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$
    – Lukas Lang
    Commented Sep 17, 2022 at 23:56
  • $\begingroup$ @LukasLang Yes, that seems to be the case. $\endgroup$
    – Michael E2
    Commented Sep 18, 2022 at 1:33
  • $\begingroup$ Thx! In my actual usage the rule is a long list. $\endgroup$
    – Lacia
    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$
    – Michael E2
    Commented Sep 18, 2022 at 16:40

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.