Erlang教程

来源:互联网 发布:sql数据库置疑修复工具 编辑:程序博客网 时间:2024/05/01 03:54
Erlang详细资料和安装环境:
  http://www.erlang.org/doc.html
说明:仍然保留原著里面的术语,以免画蛇添足。
      本文的内容近似整理和翻译,对函数式语言有兴趣的朋友请到上文提到的Url下载更全面的材料。
      本文作者也是边学边写,对文中出现的错误,请大家指正。
      推荐大家多翻一翻,Erlang自带的Doc,里面有很多实用的例程和编程原则(效率方面的)。
      Email: do.chuan@gmail.com
  Erlang简明教程

    1. 介绍
    2. 几个简单例程
    3.  Basic Principle
    4.  并发编程
    5.  分布式
    6.  错误处理
    7.  程序的健壮性
    8.  附录(一些来自网络的小资料)

1. 介绍
    Erlang是一门函数式语言。
    Erlang是一个丹麦的数学家,他搞出来的一个概率分布Erlan分布,并且用这个分布开创一们学
科排队理论,电信上经常用这个分布来测算话务量,因此在电信界比较有名.当年Joe Amstrong把他
的语言命名为Erlang也是为了纪念这个为电信领域作出过卓越贡献的人.
    Erlang是Ericsson和Ellemtel Computer Scientce Laboratories为解决电信领域中的并发和
分布式问题,设计的语言;从理论应用于实践的角度讲,主要探索的是函数式语言能否应用到通信
领域的大型交换机上,从Erlang的实践效果来看,答案是肯定的。
    Erlang的优点:
    (1) Code Loading Primitives允许在系统运行过程中升级代码。
    (2) Erlang的轻量级进程可以支持极高的并发性,而且在高并发的情况下内存使用相当的少。
        Erlang的并发性并不会受到宿主操作系统并发性的限制。Erlang的原子操
 作是一个压栈级别的,而C语言是指令级别的。
    (3) 最开始Erlang的设计目标就是实现分布式环境,一个Erlang的虚拟机就是一个Erlang网络上
        的节点。一个Erlang节点可以在另一个-Erlang节点上创建自己的并发进程,而子进程所在的
        节点可能是运行其他的操作系统的服务器。不同节点的之间的可以进行极为高效而又精确的通
        信,就像它们运行-在同一个节点一样。
    (4) Erlang内部建设有多种错误检测原语。我们可以通过这些原语来架设高容错性的系统。例如,
        一个进程可以监视其他进程的状态和活动,即使那些被监-控的进程处于其他节点。在分布式状
        态下,我们可以把系统配置成具有Fail-over功能的分布式系统。当有其他节点出错的时候,系
        统会把他的运行场景自动快速-的切换备份节点上。
    (5) Erlang是一个“软”实时系统(Soft Real Time),它可以提供毫秒级别的响应。

2. 几个简单例程
   2.1 How to run?
       笔者用的是Win2000。
       首先安装otp_win32_R11B-2.exe。
       a. 建立Erlang程序(vi/Emacs/Notepad都可以的编辑器),文件名的格式是 *.erl。
          读者可以暂时先在下面的程序中,Copy一份来做运行试验。
       b. 进入终端,把路径定位到您的*.erl目录中
       c. 启动Erlang程序
          笔者的启动方式:"d:/Program Files/erl5.5.2/bin/erl.exe"
          读者可以根据自己的路径,启动程序。
       d. 编译程序
          c(math.erl).
          或者 c(math).
          注意:结尾的.是不能省略的
       e. 运行
          math:fib(10).
       f. Erlang的帮助
          help().
   2.2 简单例程
       2.2.1 阶乘
     -module(math).  %定义模块名字
     %也可以理解成为一个起到命名空间
     %的作用
            -export([fact/1]).  %起到了头文件(*.h)的作用
     %声明这个函数,供外部调用
            fact(0) -> 1;              
            fact(N) -> N * fact(N-1).
           
            这是一个算阶乘的程序。
            % -- 注释符号
            具体执行 fact(0)还是fact(N),Erlang是模式匹配的,当前的N匹配
        上哪个定义,就执行哪一个。
            /1 -- 说明这个函数接受一个参数。
            -> -- 定义函数的方式,请类比数学当中的映射。
      2.2.2 定义更多的函数
         
      -module(math).         
      -export([fact/1]).     
      -export([fib/1]).
      fact(0) -> 1;
             fact(N) -> N * fact(N-1).
             fib(1) -> 1;
             fib(2) -> 2;
             fib(X) -> fib(X-1) + fib(X-2).
 
           也可以这样:
      -module(math).         
      -export([fact/1, fib/1]).
           
               ...  ...
           如果你要定义成这个样子:
              -module(math).         
       -export([fact/1]).     
       fact(0) -> 1;
              fact(N) -> N * fact(N-1).
              fib(1) -> 1;
              fib(2) -> 2;
              fib(X) -> fib(X-1) + fib(X-2).
           那么fib(N)只能在math模块内部使用(fib是private的)。
           注:下面的程序为了简单,只是写实现。
       2.2.3 操作List
            
      reverse(L) -> reverse(L, []).   %1
      reverse([H|T], Acc) ->          %2
               reverse(T, [H|Acc]);      %3
      reverse([], Acc) ->             %4
               Acc.                          %5
            
      []是list的符号
             %1 使用了[],这起到了辅助量的作用。
             %2 [H|T] 这种形式代表取list的头和余下的部分
                      H绑定了list的头(Head)
                      T绑定了list的余下部分(Tail)
             %[] 空表
             %2 + %3 代表一个子句(clause),用';'来分隔子句
                   子句内部用','
                     标识结束用'.'
             你也许可以编个大段的程序,来体会何时用',' , ';' , '.'。
       2.2.4 匹配更多的函数
             -module(math3).
             -export([area/1]).
      area({square, Side}) ->
  Side * Side;
      area({rectangle, X, Y}) ->
  X * Y;
             area({circle, Radius}) ->
  3.14159 * Radius * Radius;
      area({triangle, A, B, C}) ->
  S = (A + B + C)/2,
  math:sqrt(S*(S-A)*(S-B)*(S-C)).
      这是一个计算基本图形面积的例程。
             S为一个临时量。
             可以通过math3:({triangle, 3, 4, 5})计算三角形
                 通过math3:({circle, 5})计算圆
 
       2.2.5 快速排序
             -module(sort).
      -export([sort/1]).
      sort([]) -> [];
       sort([Pivot|Rest]) ->
                {Smaller, Bigger} = split(Pivot, Rest),
                lists:append(sort(Smaller), [Pivot|sort(Bigger)]).
      split(Pivot, L) ->
                split(Pivot, L, [], []).
      split(Pivot, [], Smaller, Bigger) ->
                {Smaller, Bigger};
             split(Pivot, [H|T], Smaller, Bigger) when H < Pivot ->
  split(Pivot, T, [H|Smaller], Bigger);
             split(Pivot, [H|T], Smaller, Bigger) when H >= Pivot ->
  split(Pivot, T, Smaller, [H|Bigger]).
  
             这个排序大家很熟悉吧,在这里就不介绍这个算法的原理了。
             只是想就这个例程来发一些感慨。
             这个程序很简洁,实现即原理。
             入学从Lisp或其他函数式语言而不是C语言来教授计算机课程,
         个人感觉更能让学生专注在算法的理解上;如果,用C写这个程序,要考虑很
         多怎样交换的细节问题、变量定义等,尤其老师会说递归是个很耗资源
         的调用方式,很容易堆栈溢出,很多因素让学生分心了、对实现怀疑了。
             希望有能力改变一些东西的老师,去改变一些东西。
3. Basic Principle
 
  3.1 Tuples
      特点:确定数目的item。
      用于创建复杂的数据结构。
      符号是'{}'。
      请读者看一下2.2.4的例程,我们可以这样调用它。
 > Thing = {triangle, 6, 7, 8}.
 {triangle, 6, 7, 8}
 >math3:area(Thing).
 20.3332
      这里就把Thing绑定(bound)到了一个tuple上--{triangle, 6, 7, 8},请读者注意
   这里的用词:"绑定(bound)"。我们在Erlang中是没有变量的概念的,所有用到的量都是
   一次性赋值,即绑定。
      继续我们的Tuple...
      Thing是一个有4项的tuple。第一项是原子量triangle,剩下的三项是整型数6,7,8。
      其他例子:
      {a, 12, b}, {}, {1, 2, 3}, {a, b, c, d ,e}.
     
  
   3.2 List
       特点:存储不定数目的item。
       符号是'[]'
       例子:
       [], [a, b, 12], [22], [a, 'hello friend']

   3.3 补充Data Types
 3.2.1 Constant data types
            -Numbers: 123, -789, 3.14159, 7.8e12, -1.2e-45.
                      分为整数和浮点数。
            -Atoms: abc, 'An atom with spaces', monday,green,hello_world
 3.2.2 Compound data types(复合型)
            用于把不同类型的数据组织起来。
            -Tuples
            -Lists
    3.4 一个复杂的数据结构
        组合使用tuples 和 lists可以帮助我们创建复杂的数据结构。
    X = {book, preface, acknowledgments, contents,
   {chapters, [
      {chapter, 1, 'An Erlang Tutorial'},
      {chapter, 2, ...}
      ]
    }},
         用一个复杂的数据结构,把一本书表示了出来,美极了。
         :)
     3.5 Pattern matching
         
         处理匹配失败了怎么办?
           -module(temp).
    -export([convert/2]).
   
    convert({fahrenheit, Temp}, celsius) ->
  {celsius, 5 * (Temp - 32) / 9};
    convert({celsius, Temp}, fahrenheit) ->
  {farenheit, 32 + Temp * 9 / 5};
    convert({reaumur, Temp}, celsius) ->
  {celsius, 10 * Temp / 8};
    convert({celsius, Temp}, reaumur) ->
  {reaumur, 8 * Temp / 10};
    convert({X, _}, Y) ->  %匹配失败了
  {cannot,convert,X,to,Y}.        %处理匹配失败
        
  运行:
 > temp:convert({fahrenheit, 98.6}, celsius).
 {celsius,37.0000}
 > temp:convert({reaumur, 80}, celsius).
 {celsius,100.000}
 > temp:convert({reaumur, 80}, fahrenheit).
 {cannot,convert,reaumur,to,fahrenheit}
        程序中使用了一个下划线'_'来捕捉失败的情况,这个'_'的作用像是C中的default
     它表示:这里有一个值,但这个值具体是什么我不关心。
        注意这个东西一定要放到最好,这一点不像C中的default,Erlang有些地方不是很
     智能,这也许是基于电信中的效率考虑吧。设想热恋中,你鼓足勇气要说"I love you"
     之类的东西了,这时候,电信信息要升级、匹配什么的,总之是断了一下......这让你
     觉得有什么先兆,胡思乱想... ...“算了,不说了”....
        效率就是一切,把一切都交给程序员吧。
      3.6 '='
 > N = {12, banana}.
 {12,banana}
 > {A, B} = N.
 {12,banana}
 > A.
 12
 > B.
 banana
        看明白了吗?

4. 并发编程
 
 Erlang是并行语言。
        也就是说Erlang对并行的支持是语言级别的,不需要操作系统的支持。
        'spawn'启动一个过程(process),给一个process发消息,从一个process收消息。
        'spawn/3'启动一个process并返回一个标识(identifier),这个标识用于收、发
      消息。
        语法: Pid ! Msg
        Example: Pid ! {a, 12}
   Pid : identifier
            !  : 发消息标识
           {a, 12} : 一条消息
 所有参数在发消息之前都要计算(比较:Erlang中是懒惰求值的,但是在发消息的
      时候,必须提前计算)。
        Example: foo(12) ! math3:area({square, 5})
        计算foo(12) 和 math3:area({square, 5}),但是两边的计算规则是未定义的。
       〔
         这一段翻译的不好,原文:
  foo(12) ! math3:area({square, 5})
         means evaluate the function foo(12) (this must yield a valid process
         identifier) and evaluate math3:area({square, 5}) then send the result
         (i.e. 25) as a message to the process. The order of evaluation of the
          two sides of the send primitive is undefined.
         〕
         'receive'原语,用于收消息。
         语法:
  receive
      Message1 ->
   ... ;
      Message2 ->
   ... ;
   ...
  end
          含意:定义一组收消息的模式语意,用于在收到消息后,当消息匹配上一条
        MessageN,执行这个消息'->'后面的语句。
          如果收到的消息,没有匹配上receive...end中定义的模式,这个收消息的过
        程将挂起,等待处理下一条消息;未匹配的消息,将保存,等待后续处理。
   Example:
        {A, Msg}
  A --------------------------------->B
                         Msg
                A<----------------------------------B
     
                           An echo process
  -module(echo).
  -export([start/0, loop/0]).
  
  start() ->
   spawn(echo, loop, []).
  loop() ->
   receive
      {From, Message} ->
       From ! Message,
       loop()
   end.
   运行:
  Id = echo:start(),
  Id ! {self(), hello}.
          self()是个内建函数(BIF),返回该过程的identifier
原创粉丝点击