[Erlang] What are the pitfalls for future generations? (1) erlang Mining
1. if an error occurs in guard, no error is reported. Only false is returned!
case 1=:1 of true when not erlang:length(t) =:= 1 orelse true -> ok; _ -> errorend.
Result is: error
An error occurs when length is obtained for t (atom) in the protection formula, which should have been crash. However, in the protection formula, the default error occurs and the protection formula calculation ends and false is returned, this is one of the reasons why the protection class does not accept complex functions and can only use erlang's bif.
2. If the catch type is not specified during try catch, the default value is throw!
try_catch(Value) -> try case Value of error -> erlang:error({error,plz_catch_me}); throw -> erlang:throw({throw,oh_get_you}); exit -> erlang:exit({exit,sorry_by_mistake}) end catch T -> T end.
Result:
So it is best to: clear: Catch throw: T-> {throw, T}; error: T-> {error, T}; exit: T-> {exit, T} end.
3. Be careful when using erlang: length/1 in the protection mode! (To traverse the list, the length of time is not fixed)
% The time consumed and the List length are square: Do not do thisfoo (List) when lenght (List)> 0-> do_something; foo (_)-> done. % use the matching mode to determine the arbitrary length of better ([One])-> do_something_one (); better ([One, Two])-> do_something_two (); better ([one, Two | _])-> do_something_big (); better ([])-> do_something_empty () end.
Tip: To determine whether List is a non-empty List, use case List of [_ | _]-> do_something (); _-> done end.
4. ++ is just an alias for lists: append/2: If you want to use it, be sure to confirm the ShortList ++ LongList! (Can be counted as the length of the negative senseShort length... I think about it every time I use it)
%% DO NOT DOnaive_reverse([H|T]) -> naive_reverse(T)++[H];naive_reverse([]) -> [].
Which is the most inefficient way there is to reverse a list. since the ++ operator copies its left operand, the result will be copied again and again... leading to quadratic complexity.
This is the least efficient way to reverse a list, "++" WillCopy left.
But on the other hand: the following usage is fine:
%% OKnaive_but_ok_reverse([H|T], Acc) -> naive_but_ok_reverse(T, [H]++Acc);naive_but_ok_reverse([], Acc) -> Acc.
This is not a very bad attempt. Each list element is copied only once, and the increasing Acc is on the right side of ++.
Of course, the best practice is as follows:
%% Best Dovanilla_reverse([H|T], Acc) -> vanilla_reverse(T, [H|Acc]);vanilla_reverse([], Acc) -> Acc.
This is a little more efficient than above. You don't have to create a list element, just copy it directly (or: if the compiler does not rewrite [H] ++ Acc to [H | Acc], it is more efficient ).
5. There is a big difference between receive and case, although the statement is similar:
case_test(Value) -> case Value of 1 -> ok; 2 -> error end.receive_test(Value)when Value>2 -> PID = spawn(fun () -> receive {msg,1} -> ok; {msg,2} -> error end end), [begin PID ! {msg,ValueT} end ||ValueT<-lists:seq(3,Value)], PID.
Result:
We can see from the above:
5.1 if the case does not match, an error occurs;
5.1 recieve will be blocked when there is no matching message. As long as there is no matching message in the mailbox, it will be suspended during the waiting period. = when there is a new message, it will be awakened, the receive will first check the oldest message (in the queue header) and try to match it as in the case expression. If it cannot be found, the next message will continue. If it matches with the current message successfully, the message is removed from the mailbox and the body of the sub-statement is executed, if no suitable message is found, it will wait until timeout (If yes, after Times ).
6. When erl uses-noshell-noinput to start a node, it cannot be seen and cannot be entered. How can this problem be debugged? Use-remsh Parameters
> Erl-name foo@127.0.0.1-setcookie 123456-noshell-noinput> erl-name bob@127.0.0.1-setcookie 123456-remsh foo@127.0.0.1 % note that the starting node is foo, not bob! Foo@127.0.0.1> nodes (). foo@127.0.0.1> ['Bob @ 127.0.0.1 '] foo@127.0.0.1> node (). foo@127.0.0.1> 'foo @ 127.0.0.1'
The pitfalls here are:
6.1 call q () on a remote node (),Both nodes exit.! It's terrible. It's best to rewrite the q () function in user_default.erl to prevent him from executing init: stop ().
6.2 replace erl with werl in the window;
6.3 erl supports custom parameters. For example, if you write erl-rmsh test, no error will be reported. If you accidentally write an error, it will take a long time to query ........
Tip:Two nodes A and B have been started. Use A to control B. You can use Ctrl + G r NodeA j 2 in A. For details, see Learn some erlang remote shell.
7. If there is an ArgList that is passed in from unknown places like this: "[test1, test2, test3]", how can we use it for dynamic execution?
Scenario: the Method used to call a Method: Method uses erlang: apply (Module, Method, ArgList) to call the result. The ArgList does not conform to the following format:
% String = "[test1, test2, 4]." note the end of the last section! String_to_args (String)-> {OK, Scan1, _} = erl_scan: string (String), {OK, P} = erl_parse: parse_exprs (Scan1), {value, Value, []} = erl_eval: exprs (P, []), Value.
All the parameters in the above suitable List are bound: if there is a variable such as Test1, I have not tried it and have not encountered such a requirement.
Refer
8. The erl startup parameters can be defined by yourself, as shown in figure
>erl -test erlang1 -test hello -test1 test1>init:get_argument(test).{ok,[["erlang1"],["hello"]]>init:get_arguments().[{root,["C:\\Program Files\\erl6.0"]}, {progname,["erl"]}, {home,["C:\\Users\\**"]}, {test,["erlang1"]}, {test,["hello"]}]
8.1 do not define the parameter as a string, for example, "hello world"
8.2 If This Is used to start the application, do not count on using this custom parameter. Use config to define it.
Applications shocould not normally be configured with command line flags, but shocould use the application environment instead. Refer to keep ing an Application in the Design Principles chapter for details
9. Remember that you are in distributed when using RPC, always pay attention to the process you are in!
For example, if you put rpc: call in the loop and get different efficiency feedback from the outside, the results in the following example are equivalent, but the first method will issue a lot of calls, the second method has only one call.
%%Example - Bad[rpc:call(node, ets, lookup, [table, K]) || K <- ListOfKeys].%%Example - Goodrpc:call(node, ?MODULE, get_data, [ListOfKeys, []]).get_data([], Out) -> lists:reverse(Out);get_data([K|ListOfKeys], Out) -> get_data(ListOfKeys, [ets:lookup(table,K)|Out]).
Similarly, you can change it by yourself: [gen_server: call (Pid, {func, Fun}) | Fun <-FunList].
In short, do not send messages once.
10. Do not create a terms (or you cannot control the size of terms ).
Specifically, if you want to traverse all the elements in ets, the results obtained from ets: tab2list/1 may be very large. What can we do!
% All elements are also generated at a time: do not do this for bad_traverse_to_print ()-> [begin print (Person) end | Person <-ets: tab2list (person)], OK. % from the first to the last one: data needs to be moved from the erts to the process. When ets is very large, good_traverse_to_print ()-> good_traverse_to_print2 (ets: first (person )). outputs ('$ end_of_table')-> OK; good_traverse_to_print2 (Key)-> [Person] = ets: lookup (person, Key), print (Person), good_traverse_to_print2 (ets: next (person, Key) ). % Paging: Best practice ets: select match MatchSpec: ets internally compiles matchspec into opcode and then copies the required data to the process during eval, greatly reducing the data volume best_traverse_to_print ()-> case ets: match (person, '$ 1', 10) of {PersonList,' $ end_of_table '}-> [begin print (Person) end | [Person] <-PersonList]; {PersonList, key}-> [begin print (Person) end | [Person] <-PersonList], best_traverse_to_print2 (Key) end, OK. best_traverse_to_print2 (Key)-> case ets: Match (Key) of {PersonList, '$ end_of_table'}-> [begin print (Person) end | [Person] <-PersonList]; {PersonList, key2}-> [begin print (Person) end | [Person] <-PersonList], best_traverse_to_print2 (Key2) end. print (Person)-> io: format ("Name ~ P Phone :~ P ~ N ", [Person # person. name, Person # person. phone]), OK.
There are seemingly contradictions between 10th and 9th. One is that if one processing can be completed, it should not be done multiple times. The other is that if it is too large, it should be processed multiple times! Note: If a message body is too large, it should be repeated.