[Erlang 0045] Erlang 雜記 Ⅲ


    學習Erlang有點滴收獲就會記錄到Evernote,今天又整理出來了一部分,分享一下.
    下面的內容有的來自項目實踐,有的來自Stackoverflow,erlangqa;erlangqa上的幾個問題都是litaocheng給出的答案,厲害!
 

簡單的開始

從簡單的開始,在Erlang Shell里面寫demo的時候要注意一下運算順序:
6> [1,2,3,2] --[2] --[2].
[1,2,3,2]
7> [1,2,3,2]--[2] --[2].
[1,2,3,2]
8> [1,2,3,2]--[2] .
[1,3,2]
9>
--符號的運算順序是從右到左
 
Q: 有沒有列表每項相加的函數?
比如這個[{0,2},{1,2},{1,2}]  相加后返回 [{2,6}]
A: lists:foldl(fun(X, Sum) -> {A,B}=Sum,{C,D}=X, {A+C,B+D} end, {0,0}, [{0,2},{1,2},{1,2}] ).
 
 
從Beam中提取源代碼
{ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(Beam,[abstract_code]).
io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).
 
float-> list 格式化輸出
Eshell V5.9  (abort with ^G)
1> float_to_list(2.3).
"2.29999999999999982236e+00"
2> io_lib:format("~.1f",[2.3]).
["2.3"]
3> io_lib:format("~.2f",[2.3]).
["2.30"]
4> io_lib:format("~.3f",[2.3]).
["2.300"]
5>
 
mochiweb項目的mochinum模塊提供的解決方案:
%% @spec digits(number()) -> string()
%% @doc Returns a string that accurately represents the given integer or float
%% using a conservative amount of digits. Great for generating
%% human-readable output, or compact ASCII serializations for floats.
digits(N) when is_integer(N) ->
integer_to_list(N);
digits(0.0) ->
"0.0";
digits(Float) ->
{Frac1, Exp1} = frexp_int(Float),
[Place0 | Digits0] = digits1(Float, Exp1, Frac1),
{Place, Digits} = transform_digits(Place0, Digits0),
R = insert_decimal(Place, Digits),
case Float < 0 of
true ->
[$- | R];
_ ->
R
end.
1> mochinum:digits(1.1).
"1.1"
2> mochinum:digits(1.2345).
"1.2345"
 

Erlang Shell History

Erlang Shell輸入歷史的問題,能夠調用剛剛輸入過的命令能夠提高調試的效率,除了向上向下箭頭之外,還有更多選擇
[1] 組合使用h().  e(N). v(N).命令
 
h()        -- history
v(N)       -- use the value of query <N>
e(N)       -- repeat the expression in query <N>
 
     在Shell中執行h()可以看到之前的輸入的歷史記錄,使用e(N)可以執行對應編號的語句,看個例子:
Eshell V5.9  (abort with ^G)
1> erlang:now().
{1331,436175,566666}
2> io:format("hello world!").
hello world!ok
3> F=12.
12
4> integer_to_list(F).
"12"
5> h().
1: now()
-> {1331,436175,566666}
2: io:format("hello world!")
-> ok
3: F = 12
-> 12
4: integer_to_list(F)
-> "12"
ok
6> e(4).
"12"
7>
[2] 使用rlwrap維護輸入歷史
 上面的方法在關閉Erlang Shell再啟動之后就顯得無能為力了,能不能在Erlang Shell之外維護輸入歷史呢?rlwrap就是解決這個問題的,
 rlwrap is a readline wrapper, a small utility that uses the GNU readline library to allow the editing of keyboard input for any other command. 
It maintains a separate input history for each command, and can TAB-expand words using all previously seen words and/or a user-specified file.
rlwrap安裝及使用方法,請移步  [Erlang 0044] Erlang Shell History      
其它需要維護輸入歷史的場景也可以使用rlwrap
 
 
Erlang Shell因為誤操作退出問題
 
      生產環境執行命令風險是很高的,需要心細膽大,腦子要比手快,需要有一些良好的習慣來規避風險,比如我寫SQL語句從來都是倒着寫,先寫where語句限定查詢條件;對於Erlang Shell 執行語句,也需要同樣的細心和沉穩,但是這種事情要求所有人都養成同樣的習慣是很難的.我至今未在生產環境出過誤操作,但高度緊張的狀態真的讓人疲憊;如果有技術上的方法能提供一些保障措施,還是大有裨益的;
      erlangqa上就有這樣一個問題:不小心輸入q(). 導致線上系統停止!有什么辦法防止誤操作么?
      litaocheng已經給出答案了,就是Restricted Shell:
 
shell有一種模式叫做restricited mode,運行在一個受限的環境中,你可以設置回調函數決定某個函數是否可以運行。模塊如下:
%% Powered by litaocheng
-module(restricted_mod).
-compile([export_all]).
local_allowed(q, _ArgList, State) ->
io:format("** q() is disable\n"),
{false, State};
local_allowed(Func, ArgList, State) ->
io:format("** local fun:~p args:~p state:~p", [Func, ArgList, State]),
{true,State}.
non_local_allowed(FuncSpec, ArgList, State) ->
io:format("** local fun:~p args:~p state:~p", [FuncSpec, ArgList, State]),
{true,State}.
啟動restricited shell:erl -stdlib restricted_shell restricted_mod 
> erlang:memory().
** local fun:{erlang,memory} args:[] state:{[],[]}
> q().
** q() is disable
參考: http://www.erlang.org/doc/man/shell.html
 
 Erlang Shell 快捷鍵

First of all, if you type some text and then go 
^A (Ctrl+A), you should see your cursor moving to the beginning of the line. ^E (Ctrl+E) gets you to the end. You can use arrow keys to go forward, backwards, show previous or next lines so you can repeat code.
If you type something like li and then press "tab", the shell will have completed the terms for you to lists:. Press tab again, and the shell will suggest you many functions to use after. This is Erlang completing the module lists and then suggesting functions from it. You may find the notation weird, but don't worry, you'll get familiar with it soon enough.

Erlang 中文

  服務器端做玩家輸入內容校驗時會遇到是否包含中文字符,以及中文字符個數問題,按照之前的經驗都是正則表達式來實現的對吧,在erlang中解決方案類似:
 
是否包含中文
不是很精確的做法是:   
re:run("hello 中國ren", "[\x{4e00}-\x{9fff}]+", [unicode]).
16#4e00-16#9fff是中日韓字符的unicode范圍。參考
http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%97%A5%E9%9F%A9%E7%BB%9F%E4%B8%80%E8%A1%A8%E6%84%8F%E6%96%87%E5%AD%97
                                                                                                                    Powered by litaocheng
中文字符個數  
%%Powered by litaocheng
L = unicode:characters_to_list(list_to_binary("你好nihao")),
{CharN, ZhN} =
lists:foldl(
fun(E, {N1, N2}) ->
if
E =< 255 ->
{N1 + 1, N2};
E >= 16#4e00 andalso E =< 16#9fff ->
{N1, N2 + 1};
true ->
{N1, N2}
end
end,
{0, 0}, L),
io:format("英文:~b 中文:~b", [CharN, ZhN]).
run(Src) ->
    {ok, Fd} = file:open(Src, [raw, binary]),
    do_match(Fd).

do_match(Fd) ->
    Zh = do_match(Fd, 1, []),
    file:write_file("zh.txt", lists:reverse(Zh)).

do_match(Fd, LineNo, Acc) ->
    case file:read_line(Fd) of
        eof ->
            Acc;
        {ok, Line} ->
            case re:run(Line, "[\x{4e00}-\x{9fff}]+", [unicode,global]) of
                nomatch ->
                    %io:format("dont have zh_CN\n"),
                    do_match(Fd, LineNo + 1, Acc);
                {match, MatchL} ->
                    L =
                    [begin
                        B = binary:part(Line, Pos, Len),
                        ["L", erlang:integer_to_list(LineNo), " ", B, "\n"]
                    end || [{Pos, Len}] <- MatchL],
                    %io:format("bin:~w\n", [L]),
                    do_match(Fd, LineNo + 1, L ++ Acc)
            end;
        {error, _Reason}->
            io:format("read line error:~w", [_Reason]),
            Acc
    end.

 

Erlang Shell中輸出顯示中文
Powered by litaocheng
Erlang對utf8的支持不是非常好。不過也夠用了。
在Erlang shell中:
> L=[229,136,157,231,186,167], io:format("~ts", [list_to_binary(L)]).
初級ok
如果是寫文件,L直接就是iodata,不用轉化,寫到文件中就是utf8編碼。
個人感覺,Erlang應該讓這個函數返回utf8 binary或者unicode list會比較好。
 
尾遞歸&異常
 

Note: It is important to know that the protected part of an exception can't be tail recursive. The VM must always keep a reference there in case there's an exception popping up.

Because the try ... catch construct without the of part has nothing but a protected part, calling a recursive function from there might be dangerous for programs supposed to run for a long time (which is Erlang's niche). After enough iterations, you'll go out of memory or your program will get slower without really knowing why. By putting your recursive calls between the of and catch, you are not in a protected part and you will benefit from Last Call Optimisation.

Some people use  try ... of ... catch rather than  try ... catch by default to avoid unexpected errors of that kind, except for obviously non-recursive code with a result they don't care about. You're most likely able to make your own decision on what to do!
 
我們調用c(File)的時候,發生了什么?  2013-03-13 14:59:49 更新
 
看代碼吧,編譯加載清理老版本什么的,都做了
lib\stdlib-1.18.3\src\shell_default.erl

c(File) -> c:c(File).
c(File, Opt) -> c:c(File, Opt).



erl5.9.3.1\lib\stdlib-1.18.3\src\c.erl

%% c(FileName)
%%  Compile a file/module.

-spec c(File) -> {'ok', Module} | 'error' when
      File :: file:name(),
      Module :: module().

c(File) -> c(File, []).

-spec c(File, Options) -> {'ok', Module} | 'error' when
      File :: file:name(),
      Options :: [compile:option()],
      Module :: module().

c(File, Opts0) when is_list(Opts0) ->
    Opts = [report_errors,report_warnings|Opts0],
    case compile:file(File, Opts) of
        {ok,Mod} ->                %Listing file.
            machine_load(Mod, File, Opts);
        {ok,Mod,_Ws} ->                %Warnings maybe turned on.
            machine_load(Mod, File, Opts);
        Other ->                %Errors go here
        Other
    end;
c(File, Opt) -> 
    c(File, [Opt]).

%%% Obtain the 'outdir' option from the argument. Return "." if no
%%% such option was given.
-spec outdir([compile:option()]) -> file:filename().

outdir([]) ->
    ".";
outdir([Opt|Rest]) ->
    case Opt of
    {outdir, D} ->
        D;
    _ ->
        outdir(Rest)
    end.

%%% We have compiled File with options Opts. Find out where the
%%% output file went to, and load it.
machine_load(Mod, File, Opts) ->
    Dir = outdir(Opts),
    File2 = filename:join(Dir, filename:basename(File, ".erl")),
    case compile:output_generated(Opts) of
    true ->
        Base = packages:last(Mod),
        case filename:basename(File, ".erl") of
        Base ->
            code:purge(Mod),
            check_load(code:load_abs(File2,Mod), Mod);
        _OtherMod ->
            format("** Module name '~p' does not match file name '~p' **~n",
               [Mod,File]),
            {error, badfile}
        end;
    false ->
        format("** Warning: No object file created - nothing loaded **~n", []),
        ok
    end.

 

 
周末愉快!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM