站在巨人的肩上
Standing on Shoulders of Giants
从两个星期前接触erlang开始到今天,刚好两个星期的时间。一开始接触的时候很不习惯erlang的函数型编程方式。以前玩的都是OPP型的语言,所以刚开始还真是不习惯他的简单的风格,和没有循环的尾递归。
买了三本书,看了两本,《ERLANG程序设计》和《Erlang/OTP并发编程实战》。不断的在werl下,实验书中的代码,不得不说这真是个有趣的过程。前面的基础内容很简单,没有什么函数编程基础的也可以一目了然,但是在写代码的时候还是会犯下很多OOP编程习惯带来的小错误,不错erlang强大的容错机制,让我得以很轻松的进行排错。erlang的可移植性是非常强大的,无论是从win到linux,还是从linux到win,完全不用修改任何你所编写的代码内容就可以直接在shell中运行。
但是,在基础语法中,我从来没接触过比特位语法,这个很奇怪的东西。
说实话,到现在我还是不太理解比特位语法的概念,即使我已经看了好多遍,只是知道通过比特位语法我可以随心所欲地构造指定尺寸和布局的二进制串;反之,它也可以用于匹配和抽取位串中指定的区段(例如从文件或套接字中读取二进制数据)。但是,在我目前所写的erlang或者是看过的erlang代码中都没有发现有使用比特位语法的,又或者是我还未理解比特位语法的精髓。如果哪位同学,有了解比特位语法的,或者有看过说明比特位语法的文章或者博客,请给小弟留个言。我急需
谢谢了。
erlang中的变量不变和等式不等很是吸引我。也正是这一点让我真正看到了函数式编程的魅力。
在erlang的递归中,我最记得的一个例子就是:
sum(N)->sum(N-1)+N.
do_sum(N,Total)->do_sun(N-1,Total+N).
在sum中,sum(N-1)调用完成后,在返回之前还有一些未尽事宜:即加N。另一边,在do_sum中,
do_sum(N,Total)调用完成后便万事大吉了——这次递归调用的返回值就是整个函数的返回值。凡是和服这个特征的调用就是尾调用,或称“最终调用”。尾调用与递归与否(调用方和被调用方为同一个函数)无关——尾递归只是尾调用的一哥特例,但同时也是最为重要的一种。位于sunm函数体中的尾调用就是+。
当我使用到OTP库的时候,才真正的发觉erlang这个语言的强大和稳定。历经了20多年各种强大风浪考验的实用性语言果然不是盖的。目录文件的布局十分简单。doc ebin include priv src。用监督者实现容错,本质上说,在运行时,应用就是一棵由监督者和工作进程共同构成的进程树,树根就是根监督者。
学到现在我自己记录了一些笔记,希望这些东西能对erlang新手有用:
1.原子的长度上限是255个字符,在单个系统中原子的总数也有一个上限:目前是1048576。
2.原子一经创建,即便不再使用也永远不会被清除,除非系统重启。
3.ok用于那些返回值没有任何实际意义、仅通过副作用发挥作用的函数。
4.undefined用作表示未知量的占位符。
5.Erlang的一个标准约定使用原子作为第一个元素来标记元组数据的类别。
6.空表[]也被称为nil,这个名字源自lisp编程语言,它更像是个原子,特别是它的值也只占一个字长的内存。
7.有件事用元组无法简单高效地完成,用列表却可以,那就是以现有的列表为基础创建一个新的、更长的列表,并使原列表称为新表的一部分。|(管道符)的作用便在此。新元素应从|左侧添加。
8.用++构造新列表时,先找到左侧列表的末尾,再从后往前逐步完成新列表的构造。也就是左侧列表的长度决定了++运算符的耗时。有鉴于此,新内容(通常较短)应该尽量从左侧加入列表,即便最终得到的列表是逆序的也无妨。
9.引用常被用作各种要求保证唯一性的一次性标签或cookie。可由make_ref()生成。
10.列表一般是由空表nil和所谓的列表单元共同构成的。这些单元各自携带一个元素挨个儿挂接到现有列表的顶部,从而在内存中形成一个单链表。每个单元仅占用两个字长的内存空间:[H|T]。
11.lists模块是多列表处理函数。
12.模式中不可出现运算符。只有一个例外:用于拼接字符串的++运算符。当且仅当左侧参数是字符串常量时,++可以再模式中出现。
13.位于try和catch之间的是正文或保护区。如果省略类别,则默认为throw。在of和catch的部分不是受保护的。注意,有了after,catch部分就不必要了,当然可以加上of部分。erlang:get_stacktrace()来查看当前进程最近抛出的异常的栈轨迹。
14.在命名上,Erlang变量和原子的命名规则都适用于宏,但习惯上常量名为大写,其余大部分宏位小写。在代码中使用宏(按其定义进行展开)时,必须加一个问号作为前缀。-define(foo,false).是定义宏,-undef(foo).是移除宏定义。
15.大部分类UNIX操作系统启动后的初始进程都叫init。
16.用到无穷循环的函数就只能采用尾递归。我们称这类函数在运行时空间消耗恒定,也就是说,即便函数永不返回,其内存占用量也不会随时间的流逝而增加。
17.在Erlang中,模块是函数的载体,进程借函数调用而诞生。进程彼此之间通过消息进行通信。
18.模块-模块是代码的容器。模块既可以将函数私有化,也可以将之导出供外部使用,模块通过这种方式得以控制函数的可访问性。每个目标文件(.beam文件)仅含一个模块。若模块名为test,则模块所在的源码文件名必须为test.erl,编译后的目标文件名也必须为test.beam。
19.函数-函数是真正干活的角色,Erlang模块中的所有代码都必须从属于某个函数。Erlang程序就是由一系列函数顺序拼接而成的。每个函数必须从属于某个模块。
20.进程-进程是Erlang并发的基本单元。它们互相之间通过消息进行通信。进程也是Erlang程序执行状态的基本容器:它可用于容纳会随时间变化的数据。每个进程都可以创建(派生)新的进程,并指定新进程应执行的函数调用。新进程将执行该调用并在调用结束时退出。被派生出来执行简单的io:format/2调用的进程会很短命,而被派生出来调用time:sleep(infinity)这类调用的进程则永远也不会退出,除非外力强行将之终止。
21.消息-消息是进程交互的媒介。消息可以是任意的Erlang数据。进程间的消息传递是异步的,消息接收方总会拿到消息的一份单独的副本。消息送达后就被存放在接收方的信箱内,接收方进程可以通过receive表达式来获取消息。
22.行为模式的接口是一组特定的函数和相关的调用规范。gen_server行为模式的接口包含六个函数:init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3。
23.可以将超时设置为infinity来禁用超时。
24.doc用于存放文档。如果文档是用EDoc生成的,请将overview.edoc文件放在此处,其余的文件将会自动生成
ebin用于存放编译后的代码(.beam文件)。含有应用元数据的.app文件也应存放在此处
include用于存放公共头文件。所有作为公共API的一部分的.hrl文件都应该放在这个目录中。仅用于你自己的代码之中且不打算公开的私有.hrl文件则应该与其余的源码文件一起放在src目录下
priv用于存放各种需要随应用一起发布的其他内容。包括但不限于模板文件、共享对象文件和DLL等。定位应用priv目录的方法很简单:调用code:priv_dir(),便会以字符串形式得到priv目录的完整路径
src用于存放应用的源码。不仅包括Erlang的.erl文件和内部.hrl文件,也包括ASN.1、YECC、MIB等其他源文件(如果不打算随应用一起发布源码,可以省去该目录或将目录留空)
25.每个主动应用都配有一个application行为模式的实现模块。该模块用于实现系统启动逻辑_app。
26.本质上说,在运行时,应用就是一棵由监督者和工作进程共同构成的进程树,树根就是根监督者。_sup
27.子进程规范由6个元素组成:{ID,Start,Restart,Shutdown,Type,Modules}。
第一个元素ID,是一个用于在系统内部表示各规范的项式。
第二项Start,是一个用于启动进程的三元组{Module,Function,Arguments}。
第三个元素Restart,用于指明子进程发生故障时是否需要重启。该选项可取值为表示永不重启进程的temporary,仅在进程意外终止时重启进程的transient,无论出于任何原因导致进程终止都应重启进程的permanent。
第四个元素Shutdown,用于指明如何终止进程。此处取值为一个整数2000,表示终止进程时应采用软关闭策略,给进程一段自我了断的时间(以毫秒为单位),如果进程未能在指定时间内自行退出,将被无条件终止。该选项还可取值为brutal_kill,表示在关闭监督进程时立即终止子进程;以及infinity,主要用于子进程本身也同为监督者的情况,表示应给予子进程充分的时间自行退出。
第五个值Type,用于表示进程是监督者(supervisor)还是工作者(worker)。在整个监督树中,除了实现了supervisor行为模式的监督者进程以外,剩下的都是工作进程。
第六个选项列出了该进程所依赖的模块。这部分信息仅用于在代码热升级时告知系统该以何种顺序升级各个模块。一般来说,只需列出子进程的主模块。
阅读(1167) | 评论(0) | 转发(0) |