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 手册 在第 1 章中对此进行了说明。

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

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

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

Escript 适用于简短的“脚本式”程序。一种更通用的方法是启动 Erlang VM,不带 shell,并将更多开关传递给 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 Interface 和链接驱动程序而导致内存损坏问题的绝望求助...

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

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 项可以使用 bifs 转换为二进制表示形式,反之亦然

	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 程序的十分之一。这包括几乎所有“微基准测试”

  • 大部分时间花在与其他系统通信、从故障中恢复和做出复杂决策的大型系统运行速度至少与等效的 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_info system_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?

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

	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(或者,也许是通过 Debian 或 BSD 等打包系统间接安装)安装整个 Erlang 系统是最简单的选择。

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 参数的函数。