Erlang入门(一)

    xiaoxiao2024-04-02  127

    读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使用%开始单行注释。

    文章转自庄周梦蝶  ,原文发布时间5.17

    相关资源:erlang 入门练习
    最新回复(0)