Re: Flat riddle
- To: mathgroup@smc.vnet.net
- Subject: [mg11480] Re: Flat riddle
- From: Robert Villegas <villegas@wolfram.com>
- Date: Fri, 13 Mar 1998 12:21:26 -0500
- Organization: Wolfram Research
> At http://www.wolfram.com/support/Kernel/Symbols/findsymbol.cgi > Wolfram Research Technical Support points out that you will run into > trouble if you have a function (f) with the Flat attribute, and the > rule ( f[p_]:=p ). > If (f) has these properties then ( f[1,2] ) will result in infinite > iteration. Ted, If you want f to ungroup its arguments that have head f, and also to act as the identity on a single argument, I recommend that you avoid attributes and use definitions instead. Here is a foundation that I think will establish the basic properties you want for f: ClearAll[f] f[elems___] /; MemberQ[Unevaluated[{elems}], _f] := Flatten[Unevaluated @ f[elems], Infinity, f] f[singleton_] := singleton f[] = 0 (* Degenerate case: replace 0 with whatever you like *) luxury of assuming a canonical form where arguments don't have head f. For instance, I think this one will implement the formula you wanted: f[p1_, p2_, q_] := {f[p1, p2], q} As a rule of thumb, if I want f to group its arguments, I prefer to do this myself with definitions that make the intention explicit, rather than leaving it up to the Flat attribute, which has a pre-set order for trying groupings. Here is a test: In[1]:= ClearAll[f] In[2]:= f[elems___] /; MemberQ[Unevaluated[{elems}], _f] := Flatten[Unevaluated @ f[elems], Infinity, f] In[3]:= f[singleton_] := singleton In[4]:= f[] = 0; In[5]:= f[p1_, p2_, q__] := {f[p1, p2], q} In[6]:= {f[], f[1], f[1, 2], f[1, 2, 3], f[f[1], f[2, 3]]} Out[6]= {0, 1, f[1, 2], {f[1, 2], 3}, {f[1, 2], 3}} Here is a longer test which shows that the arguments evaluate before the flattening occurs. In[7]:= f[ f[1, 2, 3], f[4, 5, 6], f[7, 8, 9] ] Out[7]= {f[{f[1, 2], 3}, {f[4, 5], 6}], {f[7, 8], 9}} It is normal for the arguments to evaluate before the function is called. If you should want the flattening to occur before argument evaluation, then modify the foundation as follows: ClearAll[f] Attributes[f] = HoldAll f[elems___] /; MemberQ[Unevaluated[{elems}], _f] := f @@ Flatten[Unevaluated[{elems}], Infinity, f] f[singleton_] := singleton f[] = 0 (* Degenerate case: replace 0 with whatever you like *) The behavior is different now for the longer example In[7] above: In[17]:= f[ f[1, 2, 3], f[4, 5, 6], f[7, 8, 9] ] Out[17]= {f[1, 2], 3, 4, 5, 6, 7, 8, 9} A note about debugging problems such as the infinite loop you saw: a useful trick to catch the recursion or iteration red-handed is to wrap the right-hand side of your rule in Hold. f[e_] := Hold[e] This will freeze evaluation after the first step, so you can see what matched e, and how your rule went awry. There is an undocumented hack that I want to warn you against because I have seen it proposed from time to time. Relying on it in your code is risky, since its behavior may change some day. The hack is to set the attributes after the making the function definitions. (* risky *) h[] = 0; (* risky *) h[x_] := x (* risky *) h[p1_, p2_, q_] := {h[p1, p2], q} (* risky *) Attributes[h] = Flat; In[14]:= ClearAll[h] In[15]:= h[] = 0; In[16]:= h[x_] := x; In[17]:= h[p1_, p2_, q_] := {h[p1, p2], q} In[18]:= Attributes[h] = Flat; In[19]:= {h[], h[1], h[1, 2], h[1, 2, 3], h[h[1], h[2, 3]]} Out[19]= {0, 1, h[1, 2], {h[1, 2], 3}, {h[1, 2], 3}} This question seems to come up in one form or another from time to time. I sent similar replies in May 1995 and September 1997, and could forward those postings if you are interested. So far, I haven't heard that the technique fails, but be wary, and test it on some nontrivial cases you need. I personally haven't needed these, so I haven't exercised them much. > One Mathematica expert has told me it's impossible. I couldn't believe > it, so I continued searching for a solution. I think I found one. > > In[1]:= > $Post=ReplaceAll[#,f[a_]->a]&; > Attributes[f]={Flat,OneIdentity}; One problem I have with this, and I gather it makes you uncomfortable, too, is that it clobbers $Post to implement simplification of a specific function. What if you have fifteen functions where you're having difficulty getting simplifications to work? Do you keep adding rules to $Post? A deeper problem with it is the ReplaceAll, which replaces all f[a_] everywhere in the expression, even if you purposely wrapped them in Hold, or used them in something like the body of Function where they are supposed to remain unevaluated: In[1]:= $Post = ReplaceAll[#, HoldPattern[f[a_]] :> a]&; In[2]:= Attributes[f] = {Flat, OneIdentity}; In[3]:= f[p_, q_] := {p, q} /; Length[p] == 2 In[4]:= Hold[ f[1], f[2, 3, 4] ] Out[4]= Hold[1, f[2, 3, 4]] It's even difficult to see what rule is attached to $Post now: In[5]:= $Post Out[5]= #1 /. HoldPattern[a_] :> a & It's better to set things up so the rewrites occur within the normal evaluation procedure. Robby Villegas