[Erlang 0045] Erlang 杂记 Ⅲ

  1. 云栖社区>
  2. 博客>
  3. 正文

[Erlang 0045] Erlang 杂记 Ⅲ

唐玄奘 2017-12-03 14:55:00 浏览1123
展开阅读全文
 学习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.
复制代码

网友评论

登录后评论
0/500
评论
唐玄奘
+ 关注