[erlang-questions] Coming Back (maybe improving lists:reverse/1)
Ivan Carmenates Garcia
co7eb@REDACTED
Sat Oct 10 00:29:13 CEST 2015
Well, using this function I write instead of 'lists:flatten/1' your
algorithm takes 50XX instead of 62XX milliseconds when using
'lists:flatten/1'.
But if instead of adding the "," in your iolist generator algorithm we do it
here somehow it takes about 42XX milliseconds, I did but I still missing one
comma. I get something like this
"users.*,name,roles.id,roles.levelusers.name AS alias, comma missing between
role.level and user.name. I think we have no control of that in
'concat_io_list/2' function.
concat_io_list([], Acc) ->
Acc;
concat_io_list([[L | _] = List | Rest], Acc) when is_list(L)->
concat_io_list(Rest, Acc ++ join_simple_list(List, []));
concat_io_list([List | Rest], Acc) ->
concat_io_list(Rest, Acc ++ List).
join_simple_list([], Acc) ->
Acc;
join_simple_list([[X | _] = Str | Xs], Acc) when is_number(X) ->
join_simple_list(Xs, Acc ++ Str);
join_simple_list([Str | Xs], Acc) ->
join_simple_list(Xs, Acc ++ join_simple_list(Str, [])).
Well thanks for all,
Best regards,
Ivan (son of Gilberio).
-----Original Message-----
From: Richard A. O'Keefe [mailto:ok@REDACTED]
Sent: Thursday, October 8, 2015 10:17 PM
To: Ivan Carmenates Garcia
Cc: Erlang Questions Mailing List
Subject: Re: [erlang-questions] Coming Back (maybe improving
lists:reverse/1)
On 8/10/2015, at 1:20 pm, Ivan Carmenates Garcia <
<mailto:co7eb@REDACTED> co7eb@REDACTED> wrote:
> For example this is one of the algorithms, I optimize it as well as I
could:
Start by optimising the documentation.
>
> Considering here that the order of the fields is very important!.
>
> %% -------------------------------------------------------------------
> %% @private
> %% @doc
> %% Parses the specified list of full fields into a string containing
> %% all full fields separated my comma.
> %% example:
> %% <pre>
> %% parse_full_fields([{users, '*'}, name, {roles, [id, level]},
> %% {users, name, alias}], fun get_postgres_operator/2) ->
> %% {"users.'*',name,roles.id,roles.level,users.name AS alias",
> %% [users, roles]}.
> %% </pre>
> %% Returns `{[], []}' if called with `[]'.
> %% @throws {error, invalid_return_fields_spec, InvalidForm :: any()}
> %% @end %%
> -------------------------------------------------------------------
Parsing turns a string into structure.
Turning structure into a string is the OPPOSITE of parsing.
You are not parsing but UNparsing.
> -spec parse_return_full_fields(FullFieldsSpecs, Separator, OperatorFinder)
-> Str when
> FullFieldsSpecs :: proplists:proplist(),
> Separator :: string(),
> OperatorFinder :: fun(),
> Str :: string().
This is seriously confusing.
The thing about a proplist, as we've recently discussed in this list, is
that it contains
- atoms, where 'x' is equivalent to {'x',true}
- pairs {Key, Value}
- junk, which is completely ignored.
While I don't fully understand your example, it's clear
that name is NOT equivalent to {name,true} and
that {users, name, alias} is NOT ignored as junk.
So whatever you have, it certainly isn't a proplist.
Finally, your -spec describes a function with three arguments, but the
example in your comment has only two arguments.
It looks like you are mapping
'atom' -> "atom"
{'table', 'field'} -> "table.field"
{'table', ['f1',...,'fn']} -> "table.f1" ... "table.fn"
{'table', 'field', 'alias'} -> "table.field AS alias"
It would seem more logical to have
field() = atom() | {atom(),atom()}. % {name,alias}
fields() = field() | list(field()).
slot() = atom() | {atom(),fields()}. % {table,fields} so that it's
obvious how to put an alias inside a list of fields.
-module(goo).
-export([
tables/1,
unparse_return_full_fields/1,
unparse_slots/1
]).
unparse_return_full_fields(Slots) ->
{lists:flatten(unparse_slots(Slots)), tables(Slots)}.
tables([{Table,_}|Slots]) ->
Tables = tables(Slots),
case lists:member(Table, Tables)
of true -> Tables
; false -> [Table|Tables]
end;
tables([Name|Slots])
when is_atom(Name) ->
tables(Slots);
tables([]) ->
[].
unparse_slots([Slot|Slots]) ->
[ unparse_slot(Slot)
| unparse_remaining_slots(Slots) ].
unparse_remaining_slots([]) ->
"";
unparse_remaining_slots(Slots) ->
["," | unparse_slots(Slots)].
unparse_slot({Table,Fields}) ->
unparse_fields(Fields, atom_to_list(Table));
unparse_slot(Name)
when is_atom(Name) ->
atom_to_list(Name).
unparse_fields([Field|Fields], Table) ->
[ unparse_fields(Field, Table)
| unparse_remaining_fields(Fields, Table) ];
unparse_fields({Name,Alias}, Table) ->
[Table, ".", atom_to_list(Name), " AS ", atom_to_list(Alias)];
unparse_fields(Name, Table)
when is_atom(Name) ->
[Table, ".", atom_to_list(Name)].
unparse_remaining_fields([], _) ->
"";
unparse_remaining_fields(Fields, Table) ->
["," | unparse_fields(Fields, Table)].
With that definition, we get
1> c(goo).
2> goo:unparse_slots([{users, '*'}, name, {roles, [id, level]},
{users, {name, alias}}]). %% Note difference here.[["users",".","*"],
",","name",",",
[["roles",".","id"],",",["roles",".","level"]],
",",
["users",".","name"," AS ","alias"]]
This is a tree of strings, not a string. But for many purposes, it's just
as good. (It's called an iolist.) Flattening it gives
"users.*,name,roles.id,roles.level,users.name AS alias"
3> goo:unparse_return_full_fields([{users, '*'}, name, {roles, [id, level]},
{users, {name, alias}}]).
{"users.*,name,roles.id,roles.level,users.name AS alias", [roles,users]}
You will note that the functions above don't have any use for reversing a
list. And I must repeat that for many purposes, such as sending text to
another OS process, there isn't any *need* to flatten the tree of strings.
Even in your code,
> parse_return_full_fields(FullFieldsSpecs, Separator, OperatorFinder) ->
> {ParsedFields, TableNames} =
> parse_full_fields2(FullFieldsSpecs, [], [], OperatorFinder),
> {string:join(lists:reverse(ParsedFields), Separator), TableNames}.
there isn't actually any need to do this:
my_reverse_join([X|Xs], Sep) ->
my_reverse_join(Xs, X, Sep);
my_reverse_join([], _) ->
"".
my_reverse_join([], Acc, _) ->
Acc;
my_reverse_join([Y|Ys], Acc, Sep) ->
my_reverse_join(Ys, Y++Sep++Acc, Sep).
9> goo:my_reverse_join(["harry","deacon","thom","avery"]).
"avery,thom,deacon,harry"
We see two ideas here which are likely to be applicable to your code in
context.
(1) It may be possible to *eliminate* a call to lists:reverse/1
by fusing it with the function the result is being passed to.
(2) It may be possible to *eliminate* appends (++) by constructing
an iolist (a tree of strings) instead of a string. For example,
io:put_chars/2 wants a unicode:chardata().
<http://www.erlang.org/doc/man/unicode.html#type-chardata>
http://www.erlang.org/doc/man/unicode.html#type-chardata
<http://www.erlang.org/doc/man/io.html#put_chars-2>
http://www.erlang.org/doc/man/io.html#put_chars-2
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20151009/d47cafaa/attachment.htm>
More information about the erlang-questions
mailing list