[erlang-questions] Update with complicated data structure
YuanZhiqian
on-your-mark@REDACTED
Tue Nov 24 07:51:03 CET 2015
Hi Richard,
Thanks for your elaborative comments. Some of the advices are helpful and I appreciate a lot for that; but some of them are not quite on the track ...
For example, regarding the array size issue, you see, it's just a sample where my point is focused on the assignment expression, so I use an array instead of a hash table which I actually have used in my project, for that would make the code messed up with less important parts. I am certainly not a C language tard = =
And please allow me to clarify my purpose of this question, this is not an erlang v.s. c&c++ debate, I am just facing an actual problem in real scenario, and need some practical advice.
So it is not that I insist in making erlang behave the same as imperial language, but that it is a real requirement in the project that I'm trying to solve.
As for the naming issue ... ok you are right, but does it really matter? At least not as important in my case ...
And you are asking me why I am using size_t instead of int, I don't know if I am right, but size_t seems an intended type for array indices, since it is unassigned, and codes from google use size_t as well. Try to compile using int, but configure your error level to the most strict mode, turn on "warning as error" option, then you won't get the code compiled.
So the point is, thanks again for your help, but unfortunately I am getting more confused with too much irrelevant comments.
Best regardsZhiqian
> Subject: Re: [erlang-questions] Update with complicated data structure
> From: ok@REDACTED
> Date: Tue, 24 Nov 2015 17:34:05 +1300
> CC: erlang-questions@REDACTED
> To: on-your-mark@REDACTED
>
>
> On 23/11/2015, at 11:06 pm, YuanZhiqian <on-your-mark@REDACTED> wrote:
>
> > Hi Guys,
> >
> > Sorry to bother you again... I am totally a green hand in Erlang :(
> >
> > Here is the problem I am now struggling with: in C&C++, we are always able to update a field in an object simply using obj.field1 = Value; But in Erlang, since its once-bound-never-change policy, people would have to find a way around to achieve the same goal,
>
> Be VERY VERY CLEAR what "THE SAME GOAL" really means.
>
> It does *not* mean "fighting the language on the beaches,
> on the landing grounds, in the fields and the streets,
> (and) in the streets" and "never surrender"ing in your
> determination to *FORCE* the low level imperative style
> on your functional language.
>
> It means achieving the same APPLICATION-LEVEL GOAL (not
> code-level goal) by some possibly very different means.
>
> > and things are getting worse when the data structure is a little more complicated, which is my case:
> >
> > I have a list of records, whose definition is as follows
> >
> > -record(company_info, {
> > company_id,
> > budget,
> > consumption,
> > compaign_ids
> > }).
> >
> > What I want to do is to add a value to the consumption of the record whose company_id is given, and returning error message if there's no such record found.
>
> We stop right there.
> First, you are presuming a structure in which you don't
> KNOW whether a company-id is present or not.
> Why?
> Second, you are using a list, which is a very nice data
> structure, but the catalogue of things lists are good at
> does not include "searching". You might find gb_trees:
> more apt to your needs.
>
> case gb_trees:lookup(Company_Id, Companies)
> of none -> raise whatever exception you want
> ; {value,Company_Info=#company_info{budget=Old}} ->
> Updated =
> Company_Info#company_info{budget = Old + Delta},
> gb_trees:update(Company_Id, Updated, Companies)
> end
>
> You should probably break this into two small functions:
>
> % (K, gbtree(K,V), (V -> V)) -> gbtree(K,V).
>
> update(Key, Tree, Fun) ->
> gb_trees:update(Key, Fun(gb_trees:get(Key, Tree)), Tree).
>
> increment_consumption(Info, Delta) ->
> Info#company_info(Info#company_info.consumption + Delta).
>
> ... update(Company_Id, Companies, fun (Info) ->
> increment_consumption(Info, Delta) end)
> ...
>
>
> > Written in C, that would be:
> >
> > ///////////////////////////////////In C&C++///////////
> > CompanyInfo co_list[1000];
> > /* initialized somewhere else ... */
> >
> > for (size_t i = 0; i < 1000; ++i) {
> > if (co_list[i].comany_id == co_id) {
> > co_list[i].consumption += price;
> > break;
> > }
> > }
> >
> > if (i == 1000)
> > err_msg = "bla...";
> > //////////////////////////////////////////////////////
>
> Written in C that would be DISASTROUS if you ever had
> 1001 companies... (Where did this practice of using
> size_t for indices come from? That's what 'int' is for!)
>
> (a) You need to break your Erlang code into smaller,
> individually meaningful pieces.
>
> (b) You need to consider AND DOCUMENT why you want to
> use a data structure that takes linear time and
> turns over linear space for an update, when a data
> structure offering logarithmic time and space turnover
> is available.
>
> (c) List comprehensions are generally easier to read than
> calls to lists:map/2.
>
> (d) Avoid thinking in terms of Booleans;
> also, use pattern matching more.
> case [Info || Info = #company_info{company_id = Co_Id} <- Companies]
> of [Found] -> ...
> ; [] -> no match
> ; [_,_|_] -> multiple matches
> end
> Thinking in terms of lists:any made it impossible for you
> to notice duplicate entries; writing
> [Found] = [C || C = #company_info{company_id = Co_Id} <- Companies]
> makes it impossible NOT to notice.
>
> (e) Since the record is a 'company_info' record, what is the
> point of calling the key field 'company_id' rather than
> just 'id'? You didn't call 'budget' 'company_budget'.
>
>
>
> >
> > But in Erlang, I couldn't find a straightforward way to do this, my code is like this:
> >
> > ////////////////////////////In Erlang//////////////////////////
> > %Co_list is the list of company_info records
> >
> > cal_win_notice({Co_id, Ca, Adgrp, Price, Cur},
> > #state{company_list = Co_list, campaign_list= Ca_list} = State) ->
>
> (f) Hvy abbrvtn mks ths hrd 2 ndrstnd.
> (g) Why do you have a mixed tuple that is not a record
> and is not separate arguments?
>
> > case lists:any(fun(#company_info{company_id = A}) -> A == Co_id end,
> > Co_list) of
> > true ->
> > New_co_list = lists:map(fun(R) ->
> > case R#company_info.company_id of
> > Co_id ->
> > R#company_info{consumption = R#company_info.consumption + Price};
> > _ ->
> > R
> > end
> > end,
> > Co_list),
> > {ok, State#state{company_list = New_co_list}};
> > false ->
> > {not_found, State}
> > end.
>
> No doubt you have a good reason for returning {not_found,State}
> instead of crashing (raising an exception), but it's probably
> worth documenting it. I say this because this is hard to
> compose: when you call your function you will have to handle
> two possible outcomes. Notice how my code got simpler when
> I switched from gb_trees:lookup/2 to gb_trees:get/2.
>
> > Well, as shown above, the codes in bold fonts are doing the same logic which in C can be done in just one expression.
>
> It wasn't one expression in C.
> It was 6 statements, including at least one capacity bug.
> You have to look at the *whole* thing, not just one tiny
> bit of it.
>
> > This will certainly make the code unnecessarily verbose when there are more similar operations to come. What should I do? I think this is due to Erlang's feature, but in case I am wrong and making a simple thing complicated ...
>
> Yes you are,
> by insisting that it be done the way you would do it in C.
>
> Make the code out of small composable pieces that are
> separately meaningful.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20151124/35d660a5/attachment.htm>
More information about the erlang-questions
mailing list