Eralng学习

1.配置D:\Program Files\erl5.9.1\bin的时候,将bin目录下面的工具引入到环境变量里面,然后可以利用




打开命令行控制窗口,先进到test.erl的位置,然后执行erl工具,详细见截图:




2.记得执行的时候要末尾要加上一个局点".",代表执行。
如果你想退出Erlang终端窗口,那么请输入q()并加上句点




3.基元由小写字母开始或者是由单引号界定。当基元用小写字母开始的时候,字母、数字、"at"符号(@).和下划线(_)都是有效的字符,如果一个基元通过单引号
封装起来,则可以使用任意字符。   (注:大写字母不能当做基元来操作)
基元(atom)来表示文字常量。Erlang中的基元和其他语言中的枚举作用是一样的。




6> '1  
6> 2  
6> 3  
6> 4'.  
'1\n2\n3\n4'
上面的列子说明 单引号里面的字符串可以换行来执行




4.rlang中有各种各样的内置函数,通常在Erlang社区中称为BIF。
它们可以应用在程序和终端中。其中内置函数is_boolean用来测试一个Erlang值是否是布尔类型。
is_boolean(9+6).




5.布尔类型的逻辑运算符
((1>0) and (2>1)).  








6.元组是用来保存一组数据元素的复合数据类型,其中数据元素要求是Erlang数据类型,但并不一定要求是相同的类型。
元组使用封闭的花括号{...}来表示,而其中的元素由逗号隔开。
Erlang提供了一些内置函数用来设置和检索元组元素的内容,并得到元组的大小。




当一个元组的第一个元素是一个基元时,称它为标记(tag)。
这一Erlang惯例用来表示不同类型的数据,同时通常在使用它的程序中有着特殊的意义。
例如,在元组{person, 'Joe', 'Armstrong'}中,
基元person是标记,这有可能也就表明第二个元素是这个人的名,然后第三个元素是这个人的姓。
















1> tuple_size({abc, {def, 123}, ghi}).  
3  
2> element(2,{abc, {def, 123}, ghi}).  
{def,123}                            
3> setelement(2,{abc, {def, 123}, ghi},def).  
{abc,def,ghi}  
4> {1,2}<{1,3}.  
true  
5> {2,3}<{2,3}.  
false  
6> {1,2}=={2,3}.  
false




从第二条可以看出,下标是从1开始,而不是0
(找一份Erlang的api)








7.列表和元组都是用来存储元素集合的,在这两种情况中,它们的元素都可以是不同的。
列表用封闭的括号[...]来定义,而它们的元素由逗号隔开。




[january, february, march]  
[123,def, abc]  
[a,[b,[c,d,e],f],g]  
[]  
[{person,'Joe','Armstrong'},{person,'Robert','Virding'},  
{person, 'Mike', 'Williams'}]  
[72,101,108,108,111,32,87,111,114,108,100]  
[$H,$e,$l,$l,$o,$,$W,$o,$r,$l,$d]  
"Hello World"




字符和字符串:   
字符(a,b,c类似形式)  字符串("abc","bbbn"类型形式)
字符由整数来表示,字符串(由字符组成)则由整数列表来表示。字符的整数值可以通过在字母前加上$符号来得到:




1> $A.  
65  
2> $A + 32.  
97  
3> $a.  
97
Erlang中没有字符串数据类型。在Erlang中,字符串是一个由ASCII值组成的整数列表,并使用双引号("")来表示。
因此,字符串"Hello World"实际上就是列表[72,101,108,108,111,32,87,111,114,108,100] 。
如果你使用ASCII整数符号$Character来表示整数,那么你就会得到这样的列表[$H,$e,$l,$l,$o,$ ,$W,$o,$r,$l,$d]。
空字符串""则等价于空列表[]:




基元和字符串的区别




基元和字符串的区别在哪里?首先它们处理的方式不同,唯一可用于基元的操作是比较操作,而你可以使用很多不同的方式处理字符串。
例如,可以把字符串"Hello World"分解成["Hello","World"]的列表,而基元'Hello World'就不能这样处理。
双引号表示字符串。单引号表示基元








列表的组成和处理


正如我们前面所说,列表和元组的处理方式是非常不同的。元组的处理只能是提取的具体元素,而列表只要不为空,就可以把一个列表分成头部和尾部。
列表的头部是指列表的第一个元素,它的尾部是一个包含所有剩余元素的一个列表,而这个列表可以继续这样分解下去,如图2-1所示。

列表的头可以是任何东西,但是列表的尾通常还是一个列表。


正如列表可以如此这样分割下去一样,我们也可以用一个列表和一个元素组成一个新的列表。新的列表可以这样构造 —— [头部|尾部],这是一个典型的构造器(constructor,简写为cons)。


关于列表的很多操作都在lists库模块中定义,下面我们可以看到其中的一些例子。
这些函数不是内置函数,它们通过在函数前加上模块的名字来调用,并用冒号隔开,例如 lists:split。


列表函数和操作:
    1> lists:max([1,2,3]).  
    3  
    2> lists:reverse([1,2,3]).  
    [3,2,1]  
    3> lists:sort([2,1,3]).  
    [1,2,3]  
    4> lists:split(2,[3,4,10,7,9]).  
    {[3,4],[10,7,9]}  
    5> lists:sum([3,4,10,7,9]).  
    33  
    6> lists:zip([1,2,3],[5,6,7]).  
    [{1,5},{2,6},{3,7}]  
    7> lists:delete(2,[1,2,3,2,4,2]).  
    [1,3,2,4,2]  
    8> lists:last([1,2,3]).  
    3  
    9> lists:member(5,[1,24]).  
    false  
    10> lists:member(24,[1,24]).  
    true  
    11> lists:nth(2,[3,4,10,7,9]).  
    4  
    12> lists:length([1,2,3]).  
    ** exception error: undefined function lists:length/1  
    13> length([1,2,3]).  
    3




如果你想在一个列表前添加一个元素,那么有两种方法:


直接使用构造器,例如[1|[2,3,4]]。


使用++运算符,例如[1] + + [2,3,4]。
这两种方法产生同样的结果,但++运算符效率更低,并可能导致程序运行时速度大幅度减慢。
因此当你想添加一个元素到一个列表头部的时候,应该尽量使用高效率的构造器方法([...|...])。


当++运算符加入到Erlang语言之后,程序员就从使用append函数转变为滥用++运算符。
其实++运算符和append函数都是代价很高的操作,因为表达式的左边的列表都要遍历一次。
它们不仅仅是耗时的操作,而且常常是多余的,比如在Erlang中所有I/O函数(包括套接字操作)都接受未展开的字符串,比如["Hello ",["Concurrent "]|"World"]。
 
列表:
List = [Element|List] or []
[[3,2]|1]
[1|[2,3]]==[1,2,3]   true     [1,2|3]==[1,2,3]   false
[1|[2|[3]]   应该少了一个]号确实少了个 正确的应该是 [1|[2|[3]]]
[1|[2|[3|[]]]]
[1,2|[3|[]]]


[3]==[3|[]].   true
[3]==[[]|[3]].   false
[3|[]]==[3].   true
[[]|[3]]==[3]. false










 8.项元比较.
 
 布尔类型:
 ==等于   /==不等于
 =:= 精确等于   =/=精确不等于
 >= 大于等于  > 大于    <=  <
 
   number < atom < reference < fun < port < pid < tuple < list < binary
   列表按字典顺序排列,就像是字典里面的词一样。第一个元素先比较,小的那个表明这个列表也较小:如果它们是相同的,则第二个元素将继续进行比较,如此下去。当其中一个列表先比较完了,那么它就是较小的列表。因此:


    5> [boo,hoo]<[adder,zebra,bee].  
    false  
    6> [boo,hoo]<[boo,hoo,adder,zebra,bee].  
    true


而另一方面,元组比较时,先会比较结构中的元素数目,然后再一个个比较各个元素的值:


    7> {boo,hoo}<{adder,zebra,bee}.  
    true  
    8> {boo,hoo}<{boo,hoo,adder,zebra,bee}.  
    true
 


9.变量


变量用来存储简单和复合数据类型的值。在Erlang中,变量必须以大写字母开头(注2),后面的字符可以是大小写字母、整数和下划线。
它们不能包含任何其他的“特殊”字符。下面是变量的一些例子:
A_long_variable_name Flag Name2 DbgFlag


Erlang中的变量不同于大多数的传统编程语言。
在一个变量的生命周期内,包括在Erlang终端处理过程中,只容许给变量赋值一次,你就不能改变它了,这称为单次赋值。
因此,当你需要对一个变量的值进行计算和操作的时候,可以把结果存储在另外一个新的变量当中。


Erlang中变量的另一个有用的特性是我们不需要声明它们,只需要使用它们即可。


rlang的一个优点是,它不需要明确地分配和释放内存。对于C程序员来说,这意味着不会再有不眠之夜来查找指针错误或者内存泄漏。
用来存储复杂数据结构的内存在需要的时候会由系统自动分配,而当不再引用它的时候,就会由垃圾收集器自动回收释放掉。


Erlang内存管理:
现有的Erlang虚拟机的实现使用了通用继承性垃圾收集器。
垃圾收集针对每个并发进程各自独立进行:当一个处理器没有更多的内存用于存储的时候,就会自动触发垃圾收集器。






10.匹配模式: (重点测试下这种模式  测试 xs=[[]|XS])
模式匹配[A,B,C,D] = [1,2,3]则会失败。虽然它们两个都是列表类型,但是左边的列表有4个元素,而右边一个列表只有3个元素。
一个常见的误解是D可以设置为空列表,然后模式匹配应该成功。在这个例子中这是不可能的,因为C和D之间由逗号而不是构造器操作符分隔开。
[A,B,C|D]=[1,2,3]的模式匹配就会成功,变量A、B和C分别绑定到整数1、2和3,变量D则绑定到尾部,在这里就是空列表。


   {A, _, [B|_], {B}} = {abc, 23, [22, 23], {22}}
会成功地提取元组的第一个元素,即基元abc,并把它绑定到变量A上。同时也将成功提取第三个元素元组的第一个元素并把它绑定到变量B上。


    14> Var = {person, "Francesco", "Cesarini"}.  
    {person, "Francesco", "Cesarini"}  
    15> {person, Name, Surname} = Var.  
    {person, "Francesco", "Cesarini"}


在第一个语句中我们把一个类型为person的元组绑定到变量Var上了,然后在第二个语句中我们分别提取了名字和姓氏。
这将会成功地把字符串“Francesco”绑定到变量Name上并把字符串“Cesarini”绑定到变量Surname上。




    11> {Element, Element, _} = {1,1,2}.  
    {1,1,2}


就如使用变量一样,在一个模式中我们也可以使用通配符,_,。这将匹配任何东西,并且不产生任何绑定。


我们刚才看到了变量可以用下划线开始,下划线是一个特殊的标记,它告诉编译器,这些变量是无关紧要的,它们只是作为程序中不需要的变量的占位符。
“无关紧要的”变量的功能跟普通变量一样,可以检查、使用和比较它们的值。唯一不同的是,普通变量的值如果从未使用过,编译器将会发出警告,而使用“无关紧要的”变量则不会。使用“无关紧要的”变量是一种很好的编程实践,这告诉阅读代码的人应该忽略掉这个值。
为了提高可读性和维护性,程序员经常以“无关紧要的”变量的形式引入值和类型。
下划线本身也是个“无关紧要的”变量,但不能访问其内容:因为它的值被忽略了而且从未绑定。




11.函数
Erlang程序包含相互调用的函数。函数组合在一起并在模块中定义。函数的名称是一个基元。
一个函数的头包括名字,随后是一对括号,在里面包含多个形式的参数或者没有参数。
在Erlang中,函数参数的数量叫做元数。使用箭头(->)来分隔函数头和函数主体。




在这两种操作系统下,你可以在Erlang终端中使用cd(Directory)进入到各个目录。
一旦进入该目录,你就可以在Erlang的终端中使用c(Module)并省略名称中的.erl后缀来进行编译。
如果代码中没有错误,编译就会成功。


    factorial(0) -> 1;  
    factorial(N) ->
      N * factorial(N-1).
 正如我们前面提到过的,模式匹配发生在函数的头部,如何匹配成功,就会绑定变量N的一个实例。对每个语句来讲变量都是本地的,它们不需要分配或释放,Erlang运行时系统会自动处理这些。(方法定义的时候,前面先用;来表示,最后一个方法用.来表示)
 
 12.模块:
 函数组合在一起构成了模块。一个程序往往是分散在几个模块当中,每个模块包括按逻辑组合在一起的函数。
 模块文件以.erl后缀来结尾,文件名称和模块名称必须是相同的。
 
 若要执行从一个模块中导出的函数,你必须编译代码,这会在与模块相同的目录下面生成一个module.beam文件:
 
 模块可以这样直接命名–module(Name),因此在例2-2中,demo模块将存储在一个名为demo.erl的文件中。
 
     -module(demo).  
    -export([double/1]).  
     
    % This is a comment.  
    % Everything on a line after % is ignored.  
     
    double(Value) ->
      times(Value, 2).  
    times(X, Y) ->
      X*Y.
      
      export指令以Function/Arity的格式(方法名/参数个数),包含了导出函数的一个列表。这些函数是全局性的,这意味着可以从模块的外部调用它们。
      在Erlang中,注释以百分号(%)开始直到该行结束。请务必在代码中多多使用它们!
      
      
      在Windows环境下,一种打开werl终端的方法是右击一个.beam文件,然后从弹出的窗口菜单中选择Open With option的“werl”选项。从现在起,双击任何源文件相同目录下的.beam文件,就会打开一个Erlang终端了。


在这两种操作系统下,你可以在Erlang终端中使用cd(Directory)进入到各个目录。一旦进入该目录,你就可以在Erlang的终端中使用c(Module)并省略名称中的.erl后缀来进行编译。如果代码中没有错误,编译就会成功。


    1> cd("/home/francesco/examples").  
    /home/francesco/examples  
    ok  
    2> c(demo).  
    {ok,demo}  
    3> demo:double(10).  
    20  
    4> demo:times(1,2).  
    ** exception error: undefined function demo:times/2
    
    13.从复合数据类型中提取值


    Person = {person, "Mike", "Williams", [1,2,3,4]}.  
    {person, Name, Surname, Phone} = Person.  
    Name.
    
    
    小写字母开头表示基元,大写字母开头表示变量
    
    14.case
    防御性编程


假设程序把一个整数映射到一个表示一周中某天的基元。使用catch-all语句的防错性编程如下所示。


    convert(Day) ->
      case Day of  
        monday    -> 1;  
        tuesday   -> 2;  
        wednesday -> 3;  
        thursday  -> 4;  
        friday    -> 5;  
        saturday  -> 6;  
        sunday    -> 7;  
        Other     -> {error, unknown_day}  
      end.
      
      
      
          listlen(Y) ->
      case Y of  
        []     -> 0;  
        [_|Xs] -> 1 + listlen(Xs)  
      end.


      
          if                                    case X rem 2 of  
      X rem 2 == 1 -> odd;                  1 -> odd;  
      X rem 2 == 0 -> even                  0 -> even  
    end                                   end
    
    
    15.保护元


保护元(guard)是一个额外的限制条件,它应用于函数的case或者receive语句中(我们会在第4章中具体讲述receive表达式)。
保护元应该放在“->”之前来分隔语句的主体和头部。


保护元由when关键字和紧跟其后的一个保护元表达式组成。只有在模式匹配和保护元表达式求值结果为基元true的情况下,这个语句才会执行。

max(X,Y) when X>Y- >  X;
max(X,Y)  - >  Y.                          

接下来重写第2章中阶乘的例子:


    factorial(0) -> 1;  
    factorial(N) ->
      N * factorial(N-1).


这一次使用保护元:


    factorial(N) when N > 0 ->
      N * factorial(N - 1);  
    factorial(0) -> 1.
      
      
          my_add(X,Y) when not(((X>Y) or not(is_atom(X))
    ) and (is_atom(Y) or (X==3.4))) ->
      X+Y.
      
      新的保护元函数是is_atom/1和is_integer/1等。
      
      元编程:
      一个函数在运行时才确定将调用哪些函数的特性叫做元编程,也就是程序创建程序并运行。
      
      
      16.内置函数:
      要从标准输入读取一行,请使用io:get_line/1,它需要提示符字符串(或基元)作为它的输入:


    1> io:get_line("gissa line>").  
    gissa line>lkdsjfljasdkjflkajsdf.  
    "lkdsjfljasdkjflkajsdf.\n"


也可以这样读取指定数量的字符:


    2> io:get_chars("tell me> ",2).  
    tell me> er  
    "er"
    
    
    17.
    
keysearch(Key, N, TupleList) -> {value, Tuple} | false
这个函数和keyfind差不多,就是返回值的结构不一样
也是在TupleList中找一个Tuple,这个Tuple的第N个元素和Key一样。
例子:
List1 = [{name,"zhangjing"},{name,"zhangsan"}]
lists:keysearch("zhangjing",2,List1).
结果:
{value,{name,"zhangjing"}}




lists:keysearch(1,1,[{1,2},{2,4}]).  


第一个元素的值和key值一样。


18.匹配模式,函数匹配模式

Page40   模块是Erlang中代码的基本单元,我们编写的所有函数都存于模块之中。模块文件通常以.erl为扩展名的文件中。
要运行一个模块,首先需要编译它,编译成功之后的模块文件其扩展名为.beam
模块名:方法名().  来调用模块里面的方法


小写字母是基元,大写字母是变量

函数cost/1   :符号Name/N表示一个带有N个参数名为Name的函数,N称为函数的运算目。

函数cost/1必须从模块之中导出,如果你想从模块的外部调用它,这是必须的。即用-compile(export_all)来导出模块之中的所有函数

[x]就是[x|[]]的缩写

19.fun
P47。fun就是匿名函数,先定义一个Z,然后将fun赋值给一个变量Z   (基元是小写字母开头的,就当做是一个常量,变量大写字母开头的,相当于一个变量)
1>Z=fun(X)-> 2*X end.
2>Z(2)
4

fun匹配多个函数  p48

20.  逗号(,)用来分隔函数调用、数据构造器以及模式中的参数
     分号(;)用来分隔子句,在这几种情况相下都会用到子句:分段的函数定义、case语句、if语句、try....catch语句以及receive表达式。
     无论何时,我们只看到一组后面跟有表达式的模式,都会使用分号进行分隔。
    
     div   整数除法
     rem   整数取余
    
    
    
21.返回fun的fun的值的时候,
Double=fun(x)->(2*x)end.
Double(5).
10

%定义两层fun
Mult= fun(Time)->(fun(X)->(Time*X)) end.     
调用它的时候,  Trip=Mult(5).   Time的值为5
                Trip(3).        X的值为3
                
                
(1.)Mult=fun(time,A,B)->A*B  end.
调用 Mult(time,2,3)   返回6
(2.)Mult(A,B)->  A*B  end.
调用  Mult(3,4)        返回12


求和:

sum(H|T)-> H+sum(T);
sum([])->0.

将此文件储存为mylists.erl

c(mylists.erl).   %%先将此模块编译
然后再调用
L=[1,2,3]
mylists.sum(L).
6


map(_,[])  ->[];
map(F,[H|T]) ->[F(H)|map(F,T)].   %[F[H]|F[T]|[]]   将依次的列表元素当做参数传进方法里面去   (F变量代表的是一个方法名)

L=[1,2,3,4]
mylists.map(fun(x)->2*x end,L).         %% fun(1)+fun(2)+fun(3)+fun(4)
解析:
[2,4,6,8]

注:不能将自己的模块定义成lists ,因为有系统的模块名为这个名字


模块  -import(lists,[map/2,sum/1]).
 如果有导入声明的时候,不需要写lists:map(...),可以直接使用map(....), 如果没有导入的函数,记得要加上模块名才能使用
 
23.
 列表解析: (列表解析很有作用)
 L=[1,2,3,4]
 [2*X||X<-L].  %[F(X)||X<-L]代表"由F(X)组成的列表,其中X是取值于列表L"
 
 使用列表解析实现过滤功能:
 [X||{a,X}<-{a,1},{1,mn},{a,'m'}]
 [1,'m']
 
 使map方法更简洁:
map(F,L)->[F(X)||X<-L].     %<-不是方法体,而是说X的值是从L里面取的
 
列表解析很强大!!
list.seq(1,N)返回一个由1到N整数组成的列表,所以A<-lists:seq(1,N)意味着A的取值范围是1到N的所有整数。
 
 
 
 max(X,Y)  ->
    case X>Y of
         true->X;
         false->Y;
        end.

max(X,Y)  ->
     if X>Y    
     true - >X;
     false ->Y;
     end.
    
     运用保护元:
max(X,Y)  when X>Y -> X;
max(X,Y)   ->Y.
    
     什么情况下使用end结尾??
    
     断言序列 ==保护元  Page58
    
    
    
     24.记录
     Page60.
    
     -record(Name,{key1=Default1,
                   key2=Default2,
                   .....
      }).
      
      
      定义record
      -record(todo,{status=remider,who=joe,text}
      
      })
 
   创建和更新记录
   X=#todo{}.
   X1=#todo{status=urgent,text='Fix errata in book'}.
   X2=X1#todo{status=done}.
   
   #用于创建类型为todo的新记录,key都是原子。
   X2=X1#todo{status=done}.  这个表示创建了一个X1(X1必须为todo类型的)的副本,并将value的值改为了done,原记录本身并未改变
   
   记录的匹配模式:
   #todo{who=W,text=Txt} =X2.
   W.
   joe
   Txt.
   Fix errata in book
   %%匹配操作符的左边,我们使用了自由变量W和Txt来定义一个记录模式
   
   
  如果只是想提取某个记录之中的对应的值,X2#todo.text来取值
   
   
   
   
   
   -module(shop).
   -export(cost).
   
   cost(oranges)   ->5;
   cost(apple)     ->8;
   cost(milk)      ->16.
   
   -module(shop1).
   -export([total/1]).
   total([{What,N}|T])  -> shop:cost(What)*N+total(T);
   total([])            -> 0.
   
   
   
   尝试在Erlang终端输入xd ,进入到相应的目录
   在这两种操作系统下,你可以在Erlang终端中使用cd(Directory)进入到各个目录。一旦进入该目录,你就可以在Erlang的终端中使用c(Module)并省略名称中的.erl后缀来进行编译。如果代码中没有错误,编译就会成功。
   
       -module(demo).  
    -export([double/1]).  
     
    % This is a comment.  
    % Everything on a line after % is ignored.  
     
    double(Value) ->
      times(Value, 2).  
    times(X, Y) ->
      X*Y.
   
   
    1> cd("/home/francesco/examples").  
    /home/francesco/examples  
    ok  
    2> c(demo).  
    {ok,demo}  
    3> demo:double(10).  
    20  
    4> demo:times(1,2).  
    ** exception error: undefined function demo:times/2
    
    
    25.顺序型编程进阶
    二进制数据:在Erlang中可以使用一种二进制数据结构来存储大量的原始数据。相对于列表或者元组,二进制类型更加节省内存,而且运行时系统也对此进行了优化。
    在书写和打印时,二进制数据以一个整数或者字符序列的形式了来出现,两端分别用两个小于号和两个大于号括起来,例如:
    <<5,10,20>>.
    <<5,10,20>>
    <<"hello">>.
    <<"hello">>
   
   列表构造:[1,2,[3,4,5],6|[8]]   (记得测试下)
   
   
   比特语法:
   M=<<X:3,Y:6,Z:7>>.
   
   atom_to_list([hello]).
   "hello"
   
   
   [2*X||X<-[1,2,3]]
   
   
   fileName  .hrl包含的文件。
   
   -include("lerne/file.hrl").
   
   
   短路布尔表达式:
     Expr1  orelse  Expr2    %%Expr1为true的时候,不会对Expr2求值,   只有当Expr1为false的时候,才会对Expr2求值。
     Expr1  andalso  Expr2   %%Expr1为true的时候,Expr2必须被求值,   只有当Expr1为false的时候,才不会对Expr2求值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值