Erlang if、case、guard和函数

这一生的挚爱 提交于 2020-03-01 13:28:44

Erlang 函数、if、case、guard

每节排版顺序:伪代码,说明文字,案例

函数

伪代码

%单一语句的的函数
function_name(Param1, Param2, ..., ParamN) ->
    Expression1, Expression2, ..., ExpressionN.

%多语句的函数,其中每条语句的参数个数应该是一样的
function_name(Param11, Param12, ..., Param1N) ->
    Expression11, Expression12, ..., Expression1N;
function_name(Param21, Param22, ..., Param2N) ->
    Expression21, Expression22, ..., ExpressionN2;
...
function_name(ParamN1, ParamN2, ..., ParamNN) ->
    ExpressionN1, ExpressionN2, ..., ExpressionNN;

函数的名称是一个基元。一个函数的头包括名字,随后是一对括号,在里面包含多个形式的参数或者没有参数。在Erlang中,函数参数的数量叫做元数。使用箭头(->)来分隔函数头和函数主体。

Erlang函数是由分号分隔开的一个或者多个语句组成的,最后用句点来结束。每一个语句都有一个头部用来指定期望的参数模式,还有一个函数体,它由一个或多个以逗号分隔开的表达式组成,这些语句逐一按序执行,函数的返回值是最后一个表达式的结果。

案例

%一个计算形状面积的Erlang函数

area({ square, Side}) ->
    Side* Side;
area({ circle, Radius}) ->
    math:pi() * Radius * Radius;
area({ triangle, A, B, C}) ->
    S = ( A  B  C)/ 2,
    math:sqrt(S*(S-A)*(S-B)*(S-C));
area( Other) ->
    {error, invalid_ object}.

if 结构

伪代码

if
    Condition1 ->
        Expression11, Expression12, ...;
    Condition2 ->
        Expression21, Expression22, ...;
    Condition3 ->
        Expression31, Expression32, ...;
    ...;
    ConditionN ->
        ExpressionN1, ExpressionN2, ...
end

Erlang是这样工作的:先对Condition1求值,如值为true,那么将执行Expression11, Expression12, ...;并跳出该结构。若Condition1不成功,那么就继续对Condition2求值,以此类推,直到有条件成功。在if结构中,必须要有一个结果为true,否则Erlang就会抛出一个异常。通常if语句的最后一个条件会是原子true,表示如果没有匹配的条件的话,应该做什么动作。

整个if表达式的结果是上面这一系列语句的返回值,它就是被执行的分支中最后执行语句的计算值。

案例

     %前面面代码省略 func_common 就不贴出来了
    IsEmail    = func_common:is_email(LoginId),
    IsMobile   = func_common:is_mobile(LoginId),
    IsNickname = func_common:is_nickname(LoginId),
    Conditions = if
        IsEmail ->
            [{email, LoginId}];
        IsMobile ->
            [{mobile, LoginId}];
        IsNickname ->
            [{nickname, LoginId}];
        true ->
            [{nickname, LoginId}]
    end,

    Member = boss_db:find(member, Conditions),
    %后面代码省略
%% if 语句
compare(A, B) ->
    if A > B ->
            io:format("~p > ~p~n", [A, B]);
        A < B ->
            io:format("~p < ~p~n", [A, B]);
        true ->
            io:format("~p = ~p~n", [A, B])
    end.

case 结构

伪代码

case conditional-expression of
    Pattern1 ->
        Expression1;
    Pattern2 ->
        Expression2;
    ...;
    PatternN ->
        ExpressionN
end

case结构依靠模式匹配来判断应该执行哪些语句,它跟通过模式匹配来选择执行函数很类似。不同的是,case不是把函数的真实参数和形式参数进行模式匹配,而是通过对一个表达式求值,然后对它的结果和一个由分号隔开的模式匹配列表进行匹配。

使用的关键字是case、of和end。对conditional-expression求值,然后和Pattern1,...,PatternN进行匹配,直到第一个模式匹配成功。->符号把语句的模式或者头部与由逗号分隔的表达式列表组成的主体分开。一旦模式匹配成功,选中的语句表达式就会一个个按次序执行,case结构的结果就是最后一个表达式的运行结果。

与函数定义一样,case表达式的结果必须和一个模式匹配,否则将会得到一个运行时错误。如果在最后的模式中有_或者未绑定变量,那么它将匹配任何Erlang项元,这就如我们在第2章中讨论的catch-all语句一样。catch-all语句不是必需的,事实上,不鼓励把它作为一种防错性编程的形式来使用。

案例

%% case 语句
compare3(A, B) ->
    case A > B of
        true ->
            io:format("~p > ~p~n", [A, B]);
        _ ->
    case A < B of
        true ->
            io:format("~p < ~p~n", [A, B]);
        _ ->
            io:format("~p = ~p~n", [A, B])
    end
end.

guard 结构

伪代码

function_name(Param1, Param2, ..., ParamN) when Condition1 ->
    Expression1;
function_name(Param1, Param2, ..., ParamN) when Condition2 ->
    Expression2;
...
function_name(Param1, Param2, ..., ParamN) when ConditionN ->
    ExpressionN.

保护元( guard)是一个额外的限制条件,它应用于函数的case或者receive语句中。保护元应该放在“->”之前来分隔语句的主体和头部。 保护元由when关键字和紧跟其后的一个保护元表达式组成。只有在模式匹配和保护元表达式求值结果为基元true的情况下,这个语句才会执行。

单独的保护元表达式可以使用如下的结构获得:

  • 约束变量·Erlang常量的数据值,包括数字、基元、元组和列表等
  • 类型测试语句,比如is_binary、is_atom、is_boolean和is_tuple等
  • 第2章中所列出的项元比较运算符==、=/=、<、>等
  • 第2章中所列出的使用算术运算符组成的算术表达式
  • 第2章中所列出的布尔表达式

保护元内置函数导致运行时错误的保护元子表达式被视为返回false。

<b>保护元可以是测试的复杂组合,但并不允许引用任何用户自定义的函数。 </b>

规定开发人员不能用语句实现自己的保护元函数的原因是要限制它们的操作,从而确保保护元语句不会产生边界效应。在找到一个成功的语句执行之前,将会执行所有的测试保护元语句,这意味着假设在一个保护元里调用一个io:format,如果它失败了,你还是会看到打印输出,即使这个语句没有被选中执行。

<b>注意:</b>在Erlang语言中有一些类型测试内置函数的老版本,它们的名称就是类型名称:atom/1和integer/1等。不推荐使用这些内置函数,因为它们已经过时,它们的存在只是为了向后兼容。新的保护元函数是is_atom/1和is_integer/1等。

Erlang允许保护元进行简单的逻辑组合,以不同的方式实现:

  • 用逗号(,)来分隔各个保护元语句,这是一种逻辑乘,因此只有在串行序列中所有表达式的值都是true的时候,它的结果才为true。
  • 用分号(;)来分隔各个保护元语句,这是一种逻辑加(或者实际上是逗号分隔的逻辑乘),如果有一个表达式的值为true,则它的结果就是true。

案例

my_add(X, Y) when not( ((X>Y) or not(is_atom(X))) and (is_atom(Y) or ( X==3.4)) ) ->
    X+Y.
max(X, Y) when X > Y
    -> X;
max(X, Y) ->
    Y.
factorial(N) when N>0 ->
    N * factorial(N-1);
factorial(0) ->
    1.
-module(compare).

-export([compare/2, compare2/2, compare3/2]).

%% guard 语句
compare2(A, B) when A > B ->
    io:format("~p > ~p~n", [A, B]);
compare2(A, B) when A < B ->
    io:format("~p < ~p~n", [A, B]);
compare2(A, B) ->
    io:format("~p = ~p~n", [A, B]).
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!