push_element/2 - efficient sliding window

Ulf Wiger (AL/EAB) ulf.wiger@REDACTED
Fri Aug 12 11:05:48 CEST 2005


Vlad Dumitrescu wrote:
>
> Ulf Wiger wrote:
> > tag_search([{Tag | _} = T|_], Tag) ->
> >     {value, T};
> > ...
> > 
> > That is, describing the "tail" of a tuple in a manner
> > similar to that of lists. This form is actually used
> > in some places in the documentation! Most likely because
> > it's very convenient. It could also be used to improve
> > backwards compatibility, since new elements in a tuple
> > are usually added last to preserve order.
> 
> Dou you think it should also work as a constructor?
> 	Tuple2 = { Elem | Tuple1}
> 
> Elegance would require it, but it would be quite inefficient 
> to use tuples in this way (where in fact lists are needed) 
> because one needs to copy the tuple contents, whereas adding 
> to the head of a list is constant time.

I'd be perfectly happy to have it as syntactic sugar for

tag_search([T|_], Tag) when element(1, T) == Tag ->
   {value, T};


In this particular example, it's only a marginal improvement,
but in other cases, it could be a significant boon:


(Just grabbing some code from stdlib -- erl_lint.erl:


pattern({record_field,Line,_,_}=M, _Vt, _Old, _Bvt, St0) ->
    case expand_package(M, St0) of
        {error, St1} ->
            {[],[],add_error(Line, illegal_expr, St1)};
        {_, St1} ->
            {[],[],St1}
    end;


Imagine wanting to add an "annotations" element to tuples in
the parse tree (or perhaps column numbers). It's a silly idea,
since it would require a total rewrite of compiler and linter,
and a load of other modules as well.

What if we could rewrite the above code as:

pattern({record_field,Line|_}=M, _Vt, _Old, _Bvt, St0) ->
    case expand_package(M, St0) of
        {error, St1} ->
            {[],[],add_error(Line, illegal_expr, St1)};
        {_, St1} ->
            {[],[],St1}
    end;


But this function didn't modify the tuple, so it's an easy
case.

pattern({op,_Line,'++',{cons,Li,{char,_L2,_C},T},R}, Vt, Old, Bvt, St) ->
    pattern({op,Li,'++',T,R}, Vt, Old, Bvt, St);    %Char unimportant here

would at first become:

pattern({op,_Line,'++',{cons,Li,{char,_L2,_C|_},T|_},R|Rest}, Vt, Old, Bvt, St) ->
    pattern({op,Li,'++',T,R|Rest}, Vt, Old, Bvt, St);    %Char unimportant here


Then, yes, it would have to work as a constructor too, but 
with the meaning of:

pattern(P, Vt, Old, Bvt, St) when element(1,P) == op, 
                                  element(2,P) == '++',
                                  element(1,element(3,P))==cons,
                                  element(1,element(3,element(3,P)))==char ->
    Cons = element(3, P),
    Li = element(2, Cons),
    T = element(3, Cons),
    P1 = setelement(2, setelement(4, P, T), Li),
    pattern(P1, Vt, Old, Bvt, St);   %Char unimportant here


That is, not modifying the lenght of the tuple, but rather
as syntactic sugar for what would otherwise be a cascade of 
setelement/3 calls.

(which may also serve to illustrate why nobody in his right mind
writes code like this today.)

/Uffe



More information about the erlang-questions mailing list