Erlang logo

如何...

覆盖

展开全部
收起全部

目录

5 我如何...

本节是为没有耐心的人准备的。大多数问题可以通过阅读(在线)Erlang 手册来解决,但有时我们只想得到一个快速的答案...

请记住,程序片段旨在说明一个想法,而不是作为可重用、健壮的模块!

5.1  ...比较数字?

比较数字的运算符是 >、>=、<、=<、== 和 =/= 。由于历史原因,其中一些与 C 有点不同。以下是一些示例

	Eshell V4.9.1  (abort with ^G)
	1> 13 > 2.
	true
	2> 18.2 >= 19.
	false
	3> 3 == 3.
	true
	4> 4 =/= 4.
	false
	5> 3 = 4.
	** exited: {{badmatch,4},[{erl_eval,expr,3}]} **
	

最后一个示例是一个(失败的)模式匹配,而不是比较。

5.2  ...表示文本字符串?

作为字符列表。你可以写

A = "hello world".

这与写完全相同

A = [104,101,108,108,111,32,119,111,114,108,100].
	

也与写相同

A = [$h,$e,$l,$l,$o,$ ,$w,$o,$r,$l,$d].

每个字符在 32 位机器上消耗 8 个字节的内存(一个 32 位整数和一个 32 位指针),在 64 位机器上消耗两倍的内存。访问第 n 个元素需要 O(n) 时间。访问第一个元素需要 O(1) 时间,添加一个字符也是如此。

位语法在 手册 中描述,它提供了一种替代但有些受限的方式来紧凑地表示字符串,例如

	1> A = <<"this string consumes one byte per character">>.
	<<116,104,105,115,32,115,116,114,...>>
	2> size(A).
	43
	3> <<_:8/binary,Q:3/binary,R/binary>> = A.
	<<116,104,105,115,32,115,116,114,...>>
	4> Q.
	<<105,110,103>>
	5> binary_to_list(Q).
	"ing"
	

这种字符串模式匹配操作的风格为某些字符串操作提供了 O(1) 的等效项,当使用列表表示时,这些操作是 O(N) 的。在列表表示中为 O(1) 的某些其他操作在使用位语法时变为 O(N)。

有一些通用的方法可以提高字符串性能。

5.3  ...将字符串转换为小写?

        1> string:to_lower("Five Boxing WIZARDS").
	"five boxing wizards"
	2> string:to_upper("jump QuickLY").
	"JUMP QUICKLY"
        

如果你有较旧版本的 Erlang(R11B-5 之前的版本),你可以在 httpd_util 模块中找到这两个函数。

5.4  ...将项的文本字符串表示形式转换为项?

        1> {ok,Tokens,_} = erl_scan:string("[{foo,bar},x,3].").
        {ok,[{'[',1}, {'{',1}, {atom,1,foo}, {',',1}, {atom,1,bar},...
        2> {ok,Term} = erl_parse:parse_term(Tokens).
        {ok,[{foo,bar},x,3]}
        

另请参阅 file:consult/1

5.5  ...使用 unicode/UTF-8?

Erlang 将字符串表示为整数列表,因此它能够直接处理 unicode。unicode 库模块有助于在 unicode 的各种表示形式之间进行转换。

5.6  ...像 C 中那样破坏性地更新数据?

不能这样做被认为是 Erlang 的一个特性。 Erlang 书籍 在第一章中对此进行了说明。

话虽如此,有一些常见的方法可以实现类似于破坏性更新的效果:将数据存储在进程中(使用消息来操作它),将数据存储在 ETS 中,或者使用为相对廉价的更新而设计的数据结构(dict 库模块是一种常见的解决方案)。

5.7  ...直接从 unix shell 运行 Erlang 程序?

从 Erlang/OTP R11B-4 开始,你可以使用 Escript 运行 Erlang 代码,而无需编译。手册页有一个完整的示例,展示了如何执行此操作。

Escript 适用于简短的“脚本式”程序。一种更通用的方法,也适用于较旧的 Erlang 版本,是不使用 shell 启动 Erlang VM,并将更多开关传递给 Erlang 虚拟机。这是 hello world 的另一个示例

	-module(hello).
	-export([hello_world/0]).

	hello_world() ->
		io:fwrite("hello, world\n").
	

将其保存为 hello.erl,编译它并直接从 unix(或 msdos)命令行运行它

	matthias >erl -compile hello
	matthias >erl -noshell -s hello hello_world -s init stop
	hello, world
	

5.8  ...与非 Erlang 程序通信?

Erlang 有几种机制可以与用其他语言编写的程序进行通信,每种机制都有不同的权衡。Erlang 文档包括一份 与其他语言接口的指南,其中描述了最常见的机制

  • 分布式 Erlang

  • 端口和链接式驱动程序

  • EI (Erlang 的 C 接口,取代了大多已弃用的 erl_interface)

  • Jinterface (Erlang 的 Java 接口)

  • IC (IDL 编译器,与 C 或 Java 一起使用)

  • 通用 TCP 或 UDP 协议,包括 ASN.1

  • NIF(用 C 实现的函数)

以上某些方法比其他方法更容易使用。松散耦合的方法,例如通过 TCP 套接字使用自定义协议,比更紧密耦合的选项(例如链接式驱动程序)更容易调试。Erlang-questions 邮件列表存档包含许多因错误使用 Erl 接口和链接式驱动程序而导致内存损坏问题的绝望求助请求...

有许多用户支持的方法和工具,包括

5.9  ...用 Erlang 编写 unix 管道程序?

unix 中的许多实用程序可以通过将一个程序的输出通过管道传输到另一个程序的输入来链接在一起。以下是一个示例,它对标准输入进行 rot-13 编码并将其发送到标准输出

	-module(rot13).
	-export([rot13/0]).

	rot13() ->
	  case io:get_chars('', 8192) of
	    eof -> init:stop();
	  Text ->
	    Rotated = [rot13(C) || C <- Text],
	    io:put_chars(Rotated),
	    rot13()
	end.

	rot13(C) when C >= $a, C =< $z -> $a + (C - $a + 13) rem 26;
	rot13(C) when C >= $A, C =< $Z -> $A + (C - $A + 13) rem 26;
	rot13(C) -> C.
        

编译完成后,你可以从命令行运行它

	matthias> cat ~/.bashrc | erl -noshell -s rot13 rot13 | wc
	

5.10  ...与 DBMS 通信?

与数据库通信有两种完全不同的方式。一种方式是让 Erlang 充当另一个系统的数据库。另一种方式是让 Erlang 程序访问数据库。前者是一个“研究”课题。后者可以通过 ODBC 轻松实现,ODBC 允许您访问几乎所有的商业 DBMS。《 OTP ODBC 手册》解释了如何实现这一点。

5.11  ...解码二进制 Erlang 术语

Erlang 术语可以使用 BIF 在二进制表示形式之间相互转换。

	1> term_to_binary({3, abc, "def"}).
	<<131,104,3,97,3,100,0,3,97,98,99,107,0,3,100,101,102>>
	2> binary_to_term(v(1)).
	{3,abc,"def"}
        

如果您想使用 C 程序解码它们,请查看 EI

5.12  ...解码 Erlang 崩溃转储

Erlang 崩溃转储提供了模拟器崩溃时系统的状态信息。《手册解释了》如何解释它们。

5.13  ...估计 Erlang 系统的性能?

Erlang 的原始开发者之一 Mike Williams 喜欢说:

“如果你在开始设计系统之前不进行实验,那么你的整个系统都将是一个实验!”

这种理念在 Erlang 项目中很普遍,部分原因是 Erlang 开发环境鼓励通过原型设计进行开发。这种原型设计也将允许做出合理的性能估计。

对于那些想利用 C 和 C++ 经验的人,这里有一些粗略的经验法则:

  • 主要涉及数值计算和数据处理的代码的运行速度将比等效的 C 程序慢约 10 倍。这包括几乎所有的“微基准测试”。

  • 大型系统,其大部分时间花在与其他系统通信、从故障中恢复和做出复杂决策上,其运行速度至少与等效的 C 程序一样快。

与其他任何语言或系统一样,经验丰富的开发人员会意识到哪些操作是昂贵的,哪些是廉价的。习惯了其他语言中相对较慢的进程间通信设施的 Erlang 新手往往会高估创建 Erlang 进程以及在它们之间传递消息的成本。

5.14  ...测量 Erlang 系统的性能?

timer 模块用于测量函数执行期间经过的挂钟时间。

	7> timer:tc(lists, reverse, ["hello world"]).
	{27,"dlrow olleh"}
	8> timer:tc(lists, reverse, ["hello world this is a longer string"]).
	{34,"gnirts regnol a si siht dlrow olleh"}
	

eperf 库提供了一种分析系统的方法。

5.15  ...测量 Erlang 系统中的内存消耗?

内存消耗在 Erlang 中是一个有点棘手的问题。通常,您无需担心它,因为垃圾收集器会为您处理内存管理。但是,当出现问题时,有几个信息来源。从最一般的开始:

某些操作系统使用诸如 top、ps 或 Linux /proc 文件系统之类的工具提供有关进程内存使用情况的详细信息。

	cat /proc/5898/status

	VmSize:     7660 kB
	VmLck:         0 kB
	VmRSS:      5408 kB
	VmData:     4204 kB
	VmStk:        20 kB
	VmExe:       576 kB
	VmLib:      2032 kB
	

这为您提供了整个 Erlang 系统正在使用的内存量的可靠上限。

erlang:system_info 报告有关某些全局分配结构的字节数的有趣信息。

        3> erlang:system_info(allocated_areas).
	[{static,390265},
	 {atom_space,65544,49097},
	 {binary,13866},
	 {atom_table,30885},
	 {module_table,944},
	 {export_table,16064},
	 {register_table,240},
	 {loaded_code,1456353},
	 {process_desc,16560,15732},
	 {table_desc,1120,1008},
	 {link_desc,6480,5688},
	 {atom_desc,107520,107064},
	 {export_desc,95200,95080},
	 {module_desc,4800,4520},
	 {preg_desc,640,608},
	 {mesg_desc,960,0},
	 {plist_desc,0,0},
	 {fixed_deletion_desc,0,0}]
	

有关单个进程的信息可以从 erlang:process_info/1erlang:process_info/2 获取。

	2> erlang:process_info(self(), memory).
	{memory,1244}
	

shell 的 i()pman 工具也提供了有用的概述信息。

不要期望 process_infosystem_info 的结果总和等于操作系统报告的总内存使用量。Erlang 运行时还会将内存用于其他事情。

当您怀疑存在内存问题时,一种典型的方法是:

1. 通过检查操作系统报告的内存使用量是否异常高,来确认是否真的存在内存问题。

2. 使用 pman 或 shell 的 i() 命令来确保系统上没有失控的 Erlang 进程。失控的进程通常具有巨大的消息队列。Erlang 进程意外变大的一个常见原因是无限循环的函数,它不是尾递归的。

3. 检查用于二进制文件的内存量(由 system_info 报告)。Erlang 中的普通数据被放置在进程堆上,该堆会被垃圾回收。另一方面,大型二进制文件是引用计数的。这有两个有趣的后果。首先,二进制文件不计入进程的内存使用量。其次,可以在二进制文件中分配大量内存,而不会导致进程的堆增长太多。如果堆没有增长,则很可能不会进行垃圾回收,这可能会导致二进制文件比预期停留的时间更长。策略性地调用 erlang:garbage_collect() 会有所帮助。

4. 如果以上所有方法都未能找到问题,请使用 -instr 开关启动 Erlang 运行时系统。

5.16  ...估计 Erlang 项目的生产力?

一个粗略的经验法则是,每个开发人员产生的代码行数与 C 项目中的大致相同。一个涉及分布和容错的相当复杂的问题在 Erlang 中编写的代码长度将比在 C 中短大约五倍。

传统的减慢项目速度的方法,例如在项目进行到一半时增加大量顾问、在编写任何代码之前花费一年时间编写详细的设计规范、严格遵循瀑布模型、将开发分散到多个国家/地区以及举行团队会议来决定午餐时使用的餐巾纸的颜色,这些方法对于 Erlang 和其他语言同样有效。

5.17  ...在一个服务器中使用多个 CPU(或内核)?

Erlang 在所有主要平台上都支持 SMP,并且默认情况下启用它。

5.18  ...通过防火墙运行分布式 Erlang?

最简单的方法是通过设置(未记录的)内核变量“inet_dist_listen_min”和“inet_dist_listen_max”,预先限制分布式 Erlang 用于通信的 TCP 端口。示例:

	application:set_env(kernel, inet_dist_listen_min, 9100).
	application:set_env(kernel, inet_dist_listen_max, 9105).
	

这强制 Erlang 仅使用端口 9100--9105 进行分布式 Erlang 流量。在上面的示例中,您还需要配置防火墙以传递端口 9100--9105 以及端口 4369(用于 erlang 端口映射器)。

还有其他方法,例如通过 SSH 隧道传输信息或编写自己的分发处理程序。

5.19  ...将我编写的 Erlang 程序分发给我的朋友/同事/用户?

Erlang 程序只能在 Erlang VM 上运行,因此每个要运行 Erlang 程序的机器都需要安装 Erlang 运行时的副本。

在许多情况下,从 erlang.org 安装整个 Erlang 系统(或者,也许通过 Debian 或 BSD 等打包系统间接安装)是最简单的选择。

来自 2007 年的历史脚注是 SAE:独立 Erlang。SAE 允许将 Erlang 程序仅作为两个文件分发,总计约 500k。SAE 已不再使用,并且不再维护。

5.20  ...写入标准错误 (stderr)?

在 R13B 及更高版本中,使用原子 standard_error

            io:put_chars(standard_error, "this text goes to stderr\n").
    

这也适用于 io:format/3 以及所有其他采用 IoDevice 参数的函数。