MathGroup Archive 1998

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Flat riddle



> 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



  • Prev by Date: Re: Plotting points in 3D
  • Next by Date: Re: MultipleListPlot with Log Linear Scale
  • Prev by thread: Re: Flat riddle
  • Next by thread: Re: Flat riddle