zoukankan      html  css  js  c++  java
  • Erlang入门(一)

    读erlang.org上面的Erlang Course四天教程
    1.数字类型,需要注意两点
    1)B#Val表示以B进制存储的数字Val,比如

    7> 2#101.
    5

    二进制存储的101就是10进制的5了
    2)$Char表示字符Char的ascii编码,比如$A表示65

    2.比较难以翻译的概念——atom,可以理解成常量,它可以包含任何字符,以小写字母开头,如果不是以小写字母开头或者是字母之外的符号,需要用单引号包括起来,比如abc,'AB'

    3.另一个概念——Tuple,有人翻译成元组,可以理解成定长数组,是Erlang的基础数据结构之一:

    8> {1,2,3,4,5}.
    {1,2,3,4,5}
    9> {a,b,c,1,2}.
    {a,b,c,1,2}
    10> size({1,2,3,a,b,c}).
    6

    内置函数size求长度,元组可以嵌套元组或者其他结构。下面所讲的列表也一样。

    4.另外一个基础数据结构就是各个语言都有的list(列表),在[]内以,隔开,可以动态改变大小,

        [123, xyz]
        [123, def, abc]
        [{person, 'Joe', 'Armstrong'},
            {person, 'Robert', 'Virding'},
            {person, 'Mike', 'Williams'}
        ]

    可以使用内置函数length求列表大小。以""包含的ascii字母代表一个列表,里面的元素就是这些字母的ascii值,比如"abc"表示列表[97,98,99]。

    5.通过这两个数据结构可以组合成各种复杂结构,与Lisp的cons、list演化出各种结构一样的奇妙,Erlang也可以当作是操作列表的语言。

    6.Erlang中变量有两个特点:
    1)变量必须以大写字母或者下划线开头,可以包含字母、下划线和@
    2)变量只能绑定一次,也就是所谓的Single Assignment。或者以一般的说法就是只能赋值一次,其实Erlang并没有赋值这样的概念,=号也是用于验证匹配。

    7.模式匹配——Pattern Matching,Erlang的模式匹配非常强大,看了buaawhl的《Erlang语法提要》的介绍,模式匹配的功能不仅仅在课程中介绍的数据结构的拆解,在程序的分派也扮演重要角色,或者说Erlang的控制的流转是通过模式匹配来实现的。具体功能参见链接,给出书中拆解列表的例子:

        [A,B|C] = [1,2,3,4,5,6,7]
            Succeeds - binds A = 1, B = 2,
            C = [3,4,5,6,7]
        
        [H|T] = [1,2,3,4]
            Succeeds - binds H = 1, T = [2,3,4]
        
        [H|T] = [abc]
            Succeeds - binds H = abc, T = []
        
        [H|T] = []
            Fails


    下面会给出更多模式匹配的例子,给出一个模块用来计算列表等

    8.Erlang中函数的定义必须在一个模块内(Module),并且模块和函数的名称都必须是atom,函数的参数可以是任何的Erlang类型或者数据结构,函数要被调用需要从模块中导出,函数调用的形式类似:
    moduleName:funcName(Arg1,Arg2,...).
    写我们的第一个Erlang程序,人见人爱的Hello World:

    -module(helloWorld).
    -export([run/1]).
    run(Name)->
        io:format("Hello World ~w~n",[Name]).

    存为helloWorld.erl,在Erlang Shell中执行:

    2> c(helloWorld).
    {ok,helloWorld}
    3> helloWorld:run(dennis).
    Hello World dennis
    ok

    打印出来了,现在解释下程序构造,

    -module(helloWorld).

    这一行声明了模块helloWorld,函数必须定义在模块内,并且模块名称必须与源文件名相同。

    -export([run/1]).

    而这一行声明导出的函数,run/1指的是有一个参数的run函数,因为Erlang允许定义同名的有不同参数的多个函数,通过指定/1来说明要导出的是哪个函数。
    接下来就是函数定义了:

    run(Name)->
        io:format("Hello World ~w~n",[Name]).

    大写开头的是变量Name,调用io模块的format方法输出,~w可以理解成占位符,将被实际Name取代,~n就是换行了。注意,函数定义完了要以句号.结束。然后执行c(helloWorld).编译源代码,执行:

    helloWorld:run(dennis);


    9.内置的常用函数:

        date()
        time()
        length([1,2,3,4,5])
        size({a,b,c})
        atom_to_list(an_atom)
        list_to_tuple([1,2,3,4])
        integer_to_list(2234)
        tuple_to_list({})
        hd([1,2,3,4])  %输出1,也就是列表的head,类似Lisp的car
        tl([1,2,3,4])  %输出[2,3,4],也就是列表的tail,类似List的cdr


    10.常见Shell命令:
    1)h(). 用来打印最近的20条历史命令
    2)b(). 查看所有绑定的变量
    3) f(). 取消(遗忘)所有绑定的变量。
    4) f(Val).  取消指定的绑定变量
    5) e(n).   执行第n条历史命令
    6) e(-1).  执行上一条shell命令

    11.又一个不知道怎么翻译的概念——Guard。翻译成约束?呵呵。用于限制变量的类型和范围,比如:

         number(X)    - X 是数字
        integer(X)    - X 是整数
        float(X)    - X 是浮点数
        atom(X)        - X 是一个atom
        tuple(X)    - X 是一个元组
        list(X)        - X 是一个列表
        
        length(X) == 3    - X 是一个长度为3的列表
        size(X) == 2    - X 是一个长度为2的元组
        
        X > Y + Z    - X >Y+Z
        X == Y        - X 与Y相等
        X =:= Y        - X 全等于Y
        (比如: 1 == 1.0 成功
                   1 =:= 1.0 失败)

    为了方便比较,Erlang规定如下的比较顺序:

    number < atom < reference < port < pid < tuple < list

    其中pid就是process的id。

    12.忘了介绍apply函数,这个函数对于熟悉javascript的人来说很亲切,javascript实现mixin就得靠它,它的调用方式如下:

    apply(Mod, Func, Args),三个参数分别是模块、函数以及参数列表,比如调用我们的第一个Erlang程序:
    apply(helloWorld,run,[dennis]).
    13.if和case语句,if语句的结构如下:
    if
    Guard1 ->
    Sequence1 ;
    Guard2 ->
    Sequence2 ;
    ...
    end
    而case语句的结构如下:
    case Expr of
    Pattern1 [when Guard1] -> Seq1;
    Pattern2 [when Guard2] -> Seq2;

    PatternN [when GuardN] -> SeqN
    end

    if和case语句都有一个问题,就是当没有模式匹配或者Grard都是false的时候会导致error,这个问题case可以增加一个类似java中default的:

    case Fn of

       _ ->
       true
    end

    通过_指代任意的Expr,返回true,而if可以这样:

    if
      
      true ->
       true
    end

    一样的道理。case语句另一个需要注意的问题就是变量范围,每个case分支中定义的变量都将默认导出case语句,也就是在case语句结束后可以被引用,因此一个规则就是每个case分支定义的变量应该一致,不然算是非法的,编译器会给出警告,比如:

    f(X) ->
    case g(X) of
    true -> A = h(X), B = A + 7;
    false -> B = 6
    end,
    h(A).


    如果执行true分支,变量A和变量B都被定义,而如果执行的false分支,只有变量B被定义,可在case语句执行后,h(A)调用了变量A,这是不安全的,因为变量A完全可能没有被定义,编译器将给出警告
    variable 'A' unsafe in 'case' (line 10)



    14.给出一些稍微复杂的模型匹配例子,比如用于计算数字列表的和、平均值、长度、查找某元素是否在列表中,我们把这个模块定义为list:

    -module(list).
    -export([average/1,sum/1,len/1,double/1,member/2]).
    average(X)->sum(X)/len(X).
    sum([H|T]) when number(H)->H+sum(T);
    sum([])->0.
    len([_|T])->1+len(T);
    len([])->0.
    double([H|T]) -> [2*H|double(T)];
    double([]) -> [].
    member(H, [H|_]) -> true;
    member(H, [_|T]) -> member(H, T);
    member(_, []) -> false.
                    

    细细体会,利用递归来实现,比较有趣,这其实与Lisp中利用尾递归来实现迭代是一样的道理,[H|T]的形式类似Lisp中的car、cdr操作。_用于指代任意的变量,当我们只关注此处有变量,但并不关心变量的值的时候使用。用分号;来说明是同一个函数定义,只是不同的定义分支,通过模式匹配来决定调用哪个函数定义分支。
    另一个例子,计算各种图形的面积,也是课程中给出的例子:


    -module(mathStuff).
    -export([factorial/1,area/1]).
    factorial(0)->1;
    factorial(N) when N>0->N*factorial(N-1).
    %计算正方形面积,参数元组的第一个匹配square    
    area({square, Side}) ->
        Side * Side;
    %计算圆的面积,匹配circle  
    area({circle, Radius}) ->
       % almost :-)
       3 * Radius * Radius;
    %计算三角形的面积,利用海伦公式,匹配triangle 
    area({triangle, A, B, C}) ->
       S = (A + B + C)/2,
    math:sqrt(S*(S-A)*(S-B)*(S-C));
    %其他
    area(Other) ->
       {invalid_object, Other}.

    执行一下看看:

    1> c(mathStuff).
    {ok,mathStuff}
    2> mathStuff:area({square,2}).
    4
    3> mathStuff:area({circle,2}).
    12
    4> mathStuff:area({triangle,2,3,4}).
    2.90474
    5> mathStuff:area({other,2,3,4}).
    {invalid_object,{other,2,3,4}}


    Erlang使用%开始单行注释。

    from:http://www.blogjava.net/killme2008/archive/2007/06/13/123860.html

  • 相关阅读:
    web框架-(七)Django补充---models进阶操作及modelform操作
    web框架-(六)Django补充---form表单验证
    web框架-(五)Ajax
    时间处理总结(一).net
    linq 多条件查询 where 拼接+分页
    Angularjs总结(八)$ cookie和$rootscope
    Angularjs总结(七) 路由及请求服务等
    Nhibernate总结(一)查询返回指定字段
    asp.net mvc5 设置Area下的为启动页
    Angularjs总结(五)指令运用及常用控件的赋值操作
  • 原文地址:https://www.cnblogs.com/xuan52rock/p/4597835.html
Copyright © 2011-2022 走看看