非常教程

Erlang 20参考手册

指南:参考手册 | Guide: Reference manual

8.表达 | 8. Expressions

在本节中,列出所有有效的Erlang表达式。在编写Erlang程序时,也允许使用宏和记录表达式。但是,这些表达式在编译过程中被扩展,从这个意义上来说不是真正的Erlang表达式。宏和记录表达式分为几个部分:

  • Preprocessor
  • Records

8.1表达评价

除非另有明确说明,否则在表达式本身进行评估之前对所有的子表达式进行评估。例如,考虑以下表达式:

Expr1 + Expr2

Expr1Expr2也是表达式,在执行添加之前首先评估 - 以任何顺序。

许多操作符只能应用于某种类型的参数。例如,算术运算符只能应用于数字。错误类型的参数会导致badarg运行时错误。

8.2项

最简单的表达形式是一个术语,它是一个整数,浮点数,原子,字符串,列表,映射或元组。返回值是该术语本身。

8.3变量

变量是一个表达式。如果一个变量绑定到一个值,返回值就是这个值。未绑定变量只能在模式中使用。

变量以大写字母或下划线(_)开头。变量可以包含字母数字字符,下划线和@

例子:

X
Name1
PhoneNumber
Phone_number
_
_Height

变量绑定到值使用pattern matching。Erlang使用单一赋值,也就是说,变量只能绑定一次。

所述匿名变量由下划线表示(_),并且可以在需要的变量,但其值可以忽略使用。

例子:

[H|_] = [1,2,3]

例如,以下划线(_)开头的_Height变量是正常变量,而不是匿名变量。但是编译器会忽略它们,因为它们不会为未使用的变量生成任何警告。

例子:

以下代码:

member(_, []) ->
    [].

可以被重写为更具可读性:

member(Elem, []) ->
    [].

这会导致对未使用变量的警告Elem,如果代码是使用warn_unused_vars设置的标志进行编译的。相反,代码可以重写为:

member(_Elem, []) ->
    [].

请注意,由于以下划线开头的变量不是匿名的,因此匹配:

{_,_} = {1,2}

但是这失败了:

{_N,_N} = {1,2}

变量的作用域是它的函数子句。在的一个分支结合的变量ifcasereceive表达必须在所有的分支被绑定到具有表达之外的值。否则,他们在表达之外被视为“不安全”。

对于try表达式而言,变量范围是有限的,因此表达式中绑定的变量总是“unsafe”。

8.4模式

模式与术语具有相同的结构,但可以包含未绑定的变量。

例子:

Name1
[H|T]
{error,Reason}

模式被允许在条款头,casereceive表达式和匹配表达式。

匹配运算符=in模式

如果Pattern1Pattern2是有效模式,以下也是有效模式:

Pattern1 = Pattern2

当与一个术语相匹配时,两者都是Pattern1Pattern2与术语相匹配。这个特性背后的想法是避免重建术语。

例子:

f({connect,From,To,Number,Options}, To) ->
    Signal = {connect,From,To,Number,Options},
    ...;
f(Signal, To) ->
    ignore.

可以改为

f({connect,_,To,_,_} = Signal, To) ->
    ...;
f(Signal, To) ->
    ignore.

模式中的字符串前缀

当匹配字符串时,以下是一个有效的模式:

f("prefix" ++ Str) -> ...

这是等价的语法糖,但难以阅读:

f([$p,$r,$e,$f,$i,$x | Str]) -> ...

模式表达

如果一个算术表达式同时满足以下两个条件,它可以在模式中使用:

  • 它只使用数字运算符或按位运算符。
  • 它的价值可以计算为一个常数时,遵守。

例子:

case {Value, Result} of
    {?THRESHOLD+1, ok} -> ...

8.5匹配

以下匹配Expr1模式针对Expr2

Expr1 = Expr2

如果匹配成功,则模式中的任何未绑定变量都会被绑定,并Expr2返回值。

如果匹配失败,badmatch则会发生运行时错误。

例子:

1> {A, B} = {answer, 42}.
{answer,42}
2> A.
answer
3> {C, D} = [1, 2].
** exception error: no match of right-hand side value [1,2]

8.6函数调用

ExprF(Expr1,...,ExprN)
ExprM:ExprF(Expr1,...,ExprN)

在函数调用的第一种形式中ExprM:ExprF(Expr1,...,ExprN),每一个ExprMExprF必须是一个原子或一个表达式,其值为一个原子。据说这个函数是使用完全限定的函数名称来调用的。这通常被称为远程外部函数调用

例子:

lists:keysearch(Name, 1, List)

在函数调用的第二种形式中ExprF(Expr1,...,ExprN)ExprF必须是一个原子或评估一个乐趣。

如果ExprF是原子,则称该函数是通过使用隐式限定的函数名称来调用的。如果该函数ExprF是本地定义的,则调用该函数。或者,如果ExprFM模块明确导入,M:ExprF(Expr1,...,ExprN)则会被调用。如果ExprF既不是本地声明的也不是明确导入的,ExprF必须是自动导入的BIF的名称。

例子:

handle(Msg, State)
spawn(m, init, [])

例如这里ExprF是一个有趣的:

1> Fun1 = fun(X) -> X+1 end,
Fun1(3).
4
2> fun lists:append/2([1,2], [3,4]).
[1,2,3,4]
3> 

请注意,调用本地函数时,使用隐式函数名称或完全限定函数名称是有区别的。后者总是指最新版本的模块。见Compilation and Code LoadingFunction Evaluation

本地函数名与自动导入的BIF冲突

如果本地函数的名称与自动导入的BIF名称相同,则语义是隐式限定的函数调用将定向到本地定义的函数,而不是BIF。为了避免混淆,有一个编译器指令可用-compile({no_auto_import,[F/A]}),这使BIF不被自动导入。在某些情况下,这样的编译指令是强制性的。

警告

在OTP R14A(ERTS版本5.8)之前,对具有与自动导入的BIF同名的函数的隐式限定函数调用总是导致调用BIF。在较新版本的编译器中,调用本地函数。这是为了避免将来添加到这组自动导入的BIF不会默默地改变旧代码的行为。

但是,为避免在使用OTP版本R14A或更高版本编译时旧的(R14前)代码改变了其行为,应用以下限制:如果覆盖在R14A之前的OTP版本中自动导入的BIF的名称(ERTS版本5.8)并在代码中对该函数进行隐式限定的调用时,您需要使用编译器指令显式地删除自动导入,或者用完全限定的函数调用替换该调用。否则,你会得到一个编译错误。看下面的例子:

-export([length/1,f/1]).

-compile({no_auto_import,[length/1]}). % erlang:length/1 no longer autoimported

length([]) ->
    0;
length([H|T]) ->
    1 + length(T). %% Calls the local function length/1

f(X) when erlang:length(X) > 3 -> %% Calls erlang:length/1,
                                  %% which is allowed in guards
    long.

同样的逻辑适用于从其他模块显式导入的函数,对于本地定义的函数。不允许同时从其他模块导入功能并在模块中声明该功能:

-export([f/1]).

-compile({no_auto_import,[length/1]}). % erlang:length/1 no longer autoimported

-import(mod,[length/1]).

f(X) when erlang:length(X) > 33 -> %% Calls erlang:length/1,
                                   %% which is allowed in guards

    erlang:length(X);              %% Explicit call to erlang:length in body

f(X) ->
    length(X).                     %% mod:length/1 is called

对于在Erlang/OTP R14A及其后版本中添加的自动导入的BIF,始终允许使用本地函数覆盖名称或显式导入。但是,如果-compile({no_auto_import,[F/A])未使用该指令,则只要使用隐式限定函数名称在模块中调用该函数,编译器就会发出警告。

8.7 If

if
    GuardSeq1 ->
        Body1;
    ...;
    GuardSeqN ->
        BodyN
end

if顺序扫描表达式的分支直到找到一个GuardSeq评估为真的保护序列。然后Body评估相应的(由','分隔的表达式序列)。

返回值Bodyif表达式的返回值。

如果没有防护序列评估为真,if_clause则会发生运行时错误。如有必要,true可以在最后一个分支中使用警卫表达式,因为警卫序列始终为真。

例子:

is_greater_than(X, Y) ->
    if
        X>Y ->
            true;
        true -> % works as an 'else' branch
            false
    end

8.8例

case Expr of
    Pattern1 [when GuardSeq1] ->
        Body1;
    ...;
    PatternN [when GuardSeqN] ->
        BodyN
end

Expr评估表达式并将模式Pattern顺序与结果进行匹配。如果匹配成功并且可选的保护序列GuardSeq为真,则对相应Body的进行评估。

返回值Bodycase表达式的返回值。

如果没有与真实保护序列匹配的模式,case_clause则会发生运行时错误。

例子:

is_valid_signal(Signal) ->
    case Signal of
        {signal, _What, _From, _To} ->
            true;
        {signal, _What, _To} ->
            true;
        _Else ->
            false
    end.

8.9发送

Expr1 ! Expr2

Expr2将消息的值发送到指定的进程Expr1。该值Expr2也是表达式的返回值。

Expr1必须评估为pid,注册名称(原子)或元组{Name,Node}Name是一个原子,Node是一个节点名称,也是一个原子。

  • 如果Expr1评估为名称,但该名称未注册,badarg则会发生运行时错误。
  • 即使pid识别不存在的进程,向pid发送消息也不会失败。
  • 分布式消息发送,即如果Expr1评估为元组{Name,Node}(或位于另一个节点的pid),也不会失败。

8.10接收

receive
    Pattern1 [when GuardSeq1] ->
        Body1;
    ...;
    PatternN [when GuardSeqN] ->
        BodyN
end

接收使用发送操作符(!)发送到进程的消息。图案Pattern顺序地对在第一消息中的时间顺序在邮箱匹配,则第二,等等。如果匹配成功并且可选的保护序列GuardSeq为真,则对相应Body的进行评估。匹配的消息被消耗,即从邮箱中删除,而邮箱中的任何其他消息保持不变。

Body返回值是receive表达式的返回值。

receive从未失败。执行被暂停,可能是无限期的,直到一个消息到达与一个模式匹配并且具有真正的保护序列。

例子:

wait_for_onhook() ->
    receive
        onhook ->
            disconnect(),
            idle();
        {connect, B} ->
            B ! {busy, self()},
            wait_for_onhook()
    end.

receive表达式可以用超时增加:

receive
    Pattern1 [when GuardSeq1] ->
        Body1;
    ...;
    PatternN [when GuardSeqN] ->
        BodyN
after
    ExprT ->
        BodyT
end

ExprT是评估为一个整数。允许的最高值是16#FFFFFFFF,即该值必须适合32位。receive..after其工作原理完全相同receive,只是如果在ExprT毫秒内没有匹配的消息到达,则会BodyT被评估。的返回值BodyT便成为的返回值receive..after表达。

例子:

wait_for_onhook() ->
    receive
        onhook ->
            disconnect(),
            idle();
        {connect, B} ->
            B ! {busy, self()},
            wait_for_onhook()
    after
        60000 ->
            disconnect(),
            error()
    end.

使用receive..after没有分支的表达式:

receive
after
    ExprT ->
        BodyT
end

这种构造不会消耗任何消息,只会在数ExprT毫秒内暂停执行。这可以用来实现简单的定时器。

例子:

timer() ->
    spawn(m, timer, [self()]).

timer(Pid) ->
    receive
    after
        5000 ->
            Pid ! timeout
    end.

超时值ExprT有两种特殊情况:

infinity过程将无限期地等待匹配的消息; 这与不使用超时相同。这对于在运行时计算的超时值很有用。0如果邮箱中没有匹配的消息,则立即发生超时。

8.11术语比较

Expr1 op Expr2

op

描述

==

等于

/=

不等于

= <

小于或等于

<

少于

=

大于或等于

比...更棒

=:=

完全等于

=/=

完全不等于

参数可以是不同的数据类型。定义了以下顺序:

number < atom < reference < fun < port < pid < tuple < map < nil < list < bit string

列表按元素逐个比较。元组按大小排序,具有相同大小的两个元组按元素进行比较。

地图按大小排序,两个大小相同的地图按照升序顺序按键进行比较,然后按键顺序按值进行比较。在地图中,按键顺序整数类型被认为小于浮动类型。

将整数与浮点数进行比较时,精度较低的项将转换为另一项的类型,除非运算符是=:=或之一=/=。浮点数比整数更精确,直到浮点数的所有有效数字都在小数点左边。当浮动值大于/小于+/- 9007199254740992.0时会发生这种情况。转换策略根据浮点大小而改变,否则大浮点数和整数的比较将失去其传递性。

术语比较运算符返回表达式的布尔值,truefalse

例子:

1> 1==1.0.
true
2> 1=:=1.0.
false
3> 1 > a.
false
4> #{c => 3} > #{a => 1, b => 2}.
false
4> #{a => 1, b => 2} == #{a => 1.0, b => 2.0}.
true

8.12算术表达式

op Expr
Expr1 op Expr2

操作者

描述

参数类型

+

Unary +

-

Unary -

+

-

*

/

浮点分割

BNOT

一位一位不是

整数

DIV

整数划分

整数

业务

X / Y的整数余数

整数

按位与

整数

生活

按位或

整数

BXOR

算术按位XOR

整数

BSL

算术左移

整数

BSR

Bitshift的权利

整数

例子:

1> +1.
1
2> -1.
-1
3> 1+1.
2
4> 4/2.
2.0
5> 5 div 2.
2
6> 5 rem 2.
1
7> 2#10 band 2#01.
0
8> 2#10 bor 2#01.
3
9> a + 10.
** exception error: an error occurred when evaluating an arithmetic expression
     in operator  +/2
        called as a + 10
10> 1 bsl (1 bsl 64).
** exception error: a system limit has been reached
     in operator  bsl/2
        called as 1 bsl 18446744073709551616

8.13布尔表达式

op Expr
Expr1 op Expr2

操作者

描述

not

一元逻辑NOT

and

逻辑AND

or

逻辑或

oxr

逻辑XOR

例子:

1> not true.
false
2> true and false.
false
3> true xor false.
true
4> true or garbage.
** exception error: bad argument
     in operator  or/2
        called as true or garbage

8.14短路表达式

Expr1 orelse Expr2
Expr1 andalso Expr2

Expr2仅在必要时评估。即,Expr2仅在以下情况下评估:

  • Expr1falseorelse表达式中评估
  • Expr1评估为true一个andalso表达式。

返回Expr1(即,truefalse)的值或Expr2(如果Expr2被评估)的值。

例1:

case A >= -1.0 andalso math:sqrt(A+1) > B of

即使A小于-1.0,它也可以工作,因为在那种情况下,math:sqrt/1永远不会被评估。

例2:

OnlyOne = is_atom(L) orelse
         (is_list(L) andalso length(L) == 1),

从Erlang/OTP R13A开始,Expr2不再需要评估为布尔值。因此,andalsoorelse现在是尾递归。例如,以下函数在Erlang/OTP R13A及更高版本中是尾递归的:

all(Pred, [Hd|Tail]) ->
    Pred(Hd) andalso all(Pred, Tail);
all(_, []) ->
    true.

8.15列表操作

Expr1 ++ Expr2
Expr1 -- Expr2

列表连接运算符++将第二个参数追加到第一个参数并返回结果列表。

列表减法运算符--产生一个列表,该列表是第一个参数的副本。该过程如下所示:对于第二个参数中的每个元素,该元素的第一个出现(如果有)将被删除。

例子:

1> [1,2,3]++[4,5].
[1,2,3,4,5]
2> [1,2,3,2,1,2]--[2,1,2].
[3,1,2]

警告

复杂度与A -- B成正比length(A)*length(B)。也就是说,它变得如果两个很慢AB很长的列表。

8.16地图表达式

创建地图

通过让一个表达式K与另一个表达式关联来构建一个新的地图V

#{ K => V }

通过列出每个关联,新地图可以包含多个建筑关联:

#{ K1 => V1, .., Kn => Vn }

空映射是通过不相互关联任何术语来构造的:

#{}

地图中的所有键和值都是术语。首先评估任何表达式,然后分别将结果项用作

键和值由=>箭头分隔,并且关联用逗号分隔,

例子:

M0 = #{},                 % empty map
M1 = #{a => <<"hello">>}, % single association with literals
M2 = #{1 => 2, b => b},   % multiple associations with literals
M3 = #{k => {A,B}},       % single association with variables
M4 = #{{"w", 1} => f()}.  % compound key associated with an evaluated expression

在这里,AB任何表情和M0通过M4被生成的地图条款。

如果声明了两个匹配键,则后者键优先。

例子:

1> #{1 => a, 1 => b}.
#{1 => b }
2> #{1.0 => a, 1 => b}.
#{1 => b, 1.0 => a}

没有定义构造键(和它们的相关值)的表达式的顺序。构造中的键值对的语法顺序是不相关的,除了最近提到的两个匹配键的情况。

更新地图

更新地图与构建地图的语法相似。

定义要更新的映射的表达式放在定义要更新的键及其相应值的表达式前面:

M#{ K => V }

M是一个类型映射的术语,K并且V是任何表达式。

如果密钥K与地图中的任何现有密钥都不匹配,则会从密钥K到值创建新的关联V

如果密钥K与map中的现有密钥相匹配M,则其关联值将由新值替换V。在这两种情况下,评估的地图表达式都会返回一个新地图。

如果M不是类型映射,badmap则抛出类型的异常。

要仅更新现有值,将使用以下语法:

M#{ K := V } 

M是一个类型映射术语,V是一个表达式,K是一个表达式,用于评估现有的键M

如果键K不匹配映射中的任何现有键M,则会badarg在运行时触发类型的异常。如果K映射中存在匹配键M,则其关联值将被新值替换V,并且评估后的映射表达式会返回一个新映射。

如果M不是类型映射,badmap则抛出类型的异常。

例子:

M0 = #{},
M1 = M0#{a => 0},
M2 = M1#{a => 1, b => 2},
M3 = M2#{"function" => fun() -> f() end},
M4 = M3#{a := 2, b := 3}.  % 'a' and 'b' was added in `M1` and `M2`.

M0是任何地图。接下来M1 .. M4是地图。

更多示例:

1> M = #{1 => a}.
#{1 => a }
2> M#{1.0 => b}.
#{1 => a, 1.0 => b}.
3> M#{1 := b}.
#{1 => b}
4> M#{1.0 := b}.
** exception error: bad argument

与构建中一样,键和值表达式的评估顺序未定义。除了两个键匹配的情况外,更新中键值对的语法顺序是无关紧要的。在这种情况下,使用后一个值。

图样图

匹配映射中的键值关联如下:

#{ K := V } = M

M是任何地图。关键K必须是具有绑定变量或文字的表达式。V可以是带有绑定或非绑定变量的任何模式。

如果变量V未绑定,它将绑定到与该键相关的值,该值K必须存在于映射中M。如果变量V被绑定,它必须匹配与Kin 关联的值M

例子:

1> M = #{"tuple" => {1,2}}.
#{"tuple" => {1,2}}
2> #{"tuple" := {1,B}} = M.
#{"tuple" => {1,2}}
3> B.
2.

这将变量绑定B到整数2

同样,可以匹配地图中的多个值:

#{ K1 := V1, .., Kn := Vn } = M

这里的键K1 .. Kn是带有文字或绑定变量的任何表达式。如果所有键都存在于map中M,则所有变量V1 .. Vn都与它们各自键的相关值相匹配。

如果匹配条件不符合,则匹配失败,或者:

  • 一个badmatch例外。这是如果它在匹配运算符的上下文中使用的,如示例中所示。
  • 或者导致下一个子句在函数头和case表达式中被测试。

匹配地图只允许:=作为关联的分隔符。

在匹配中声明键的顺序与此无关。

在匹配和匹配每个与键关联的模式时,允许重复键:

#{ K := V1, K := V2 } = M

将表达式与空映射文字匹配,匹配其类型,但不绑定变量:

#{} = Expr

如果表达式Expr的类型为map,则该表达式匹配,否则失败并出现异常badmatch

匹配语法

函数头中允许将文字作为键进行匹配:

%% only start if not_started
handle_call(start, From, #{ state := not_started } = S) ->
...
    {reply, ok, S#{ state := start }};

%% only change if started
handle_call(change, From, #{ state := start } = S) ->
...
    {reply, ok, S#{ state := changed }};

警卫地图

只要所有子表达式都是有效的警戒表达式,地图就可以在警卫中使用。

两名后卫BIF处理地图:

  • is_map/1erlang模块
  • map_size/1erlang模块

8.17位语法表达式

<<>>
<<E1,...,En>>

每个元素Ei指定位串的一个。每个元素Ei都是一个值,后跟一个可选的大小表达式和一个可选的类型说明符列表

Ei = Value |
     Value:Size |
     Value/TypeSpecifierList |
     Value:Size/TypeSpecifierList

用于位串构造,Value是一个表达式,用于计算整数,浮点数或位串。如果表达式不是单个文字或变量,它将被括在圆括号中。

在位字符串匹配中使用,Value必须是变量、整数、浮点数或字符串。

注意,例如,使用一个字符串作为in <<"abc">>是句法糖for <<$a,$b,$c>>

在位串结构中使用,Size是一个计算为整数的表达式。

在位字符串匹配中使用,Size必须是整数,或绑定到整数的变量。

Size以单位指定段的大小(见下文)。默认值取决于类型(见下文):

  • integer现在是8点。
  • float是64。
  • binarybitstring它是整个二进制或位字符串。

在匹配中,此默认值仅对最后一个元素有效。匹配中的所有其他位字符串或二进制元素都必须有大小规范。

对于utf8utf16utf32类型,Size不得给予。段的大小由类型和值本身隐含确定。

TypeSpecifierList是类型说明符的列表,以任何顺序用连字符( - )分隔。缺省值用于任何省略的类型说明符。

Type** = ** | | | | | | | | 默认是。是一个简写,并且是一个简写。有关类型的更多信息,请参阅下文。** = ** | 只对匹配和类型是重要的。默认是。** = ** | | Native-endian意味着在加载时endianness被解析为big-endian或little-endian,这取决于运行Erlang机器的CPU的本机特性。字节序仅事宜时,该类型可以是,,,或integer float binary bytes bitstring bits utf8 utf16 utf32integerbytesbinarybitsbitstringutfSignednesssigned unsignedintegerunsignedEndiannessbig little nativeintegerutf16utf32float。默认是bigUnit** = ** unit:IntegerLiteral允许的范围是1..256。默认为1,for integerfloatand bitstringfor 8 for binary。对于类型,和utf8,必须没有单位说明符。utf16utf32

Size乘以单位的值给出了位数。一段类型binary的大小必须能够被8整除。

在构建二进制文件时,如果N整数段的大小太小而不能包含给定的整数,则整数的最高有效位将被静默丢弃,并且只有N最低有效位被放入二进制文件中。

utf8utf16utf32种类用于指定编码/解码Unicode转换格式分别应用UTF-8,UTF-16,和UTF-32。

构建utf类型的段时,Value必须是范围为0..16#D7FF或16#E000 .... 16#10FFFF的整数。badarg如果Value超出允许的范围,构造将失败并发生异常。生成的二进制段的大小取决于类型Value,或两者:

  • 对于utf8Value编码为1-4个字节。
  • 因为utf16Value以2或4字节编码。
  • 对于utf32Value始终以4字节编码。

在构建时,可以给出一个文字字符串,后跟一个UTF类型,例如:<<"abc"/utf8>>这是用于语法的糖<<$a/utf8,$b/utf8,$c/utf8>>

成功匹配某个utf类型的段,会得到范围为0..16#D7FF或16#E000..16#10FFFF的整数。如果返回值超出这些范围,则匹配失败。

utf8如果匹配位置的二进制包含有效的UTF-8序列,则二进制文件中的一段类型匹配1-4个字节。(请参阅RFC-3629或Unicode标准。)

一段类型utf16可以匹配二进制文件中的2或4个字节。如果匹配位置的二进制文件不包含Unicode代码点的合法UTF-16编码,则匹配失败。(请参阅RFC-2781或Unicode标准。)

段的类型utf32可以匹配二进制中的4个字节,方法与integer段匹配32位相同。如果生成的整数超出上述合法范围,则匹配失败。

例子:

1> Bin1 = <<1,17,42>>.
<<1,17,42>>
2> Bin2 = <<"abc">>.
<<97,98,99>>
3> Bin3 = <<1,17,42:16>>.
<<1,17,0,42>>
4> <<A,B,C:16>> = <<1,17,42:16>>.
<<1,17,0,42>>
5> C.
42
6> <<D:16,E,F>> = <<1,17,42:16>>.
<<1,17,0,42>>
7> D.
273
8> F.
42
9> <<G,H/binary>> = <<1,17,42:16>>.
<<1,17,0,42>>
10> H.
<<17,0,42>>
11> <<G,H/bitstring>> = <<1,17,42:12>>.
<<1,17,1,10:4>>
12> H.
<<17,1,10:4>>
13> <<1024/utf8>>.
<<208,128>>

注意,位字符串模式不能嵌套。

还要注意“ B=<<1>>”被解释为“ B =<<1>>”,这是一个语法错误。正确的方法是在'='之后写一个空格:“ B= <<1>>

Programming Examples提供了更多示例。

8.18种有趣的表达方式

fun
    [Name](Pattern11,...,Pattern1N) [when GuardSeq1] ->
              Body1;
    ...;
    [Name](PatternK1,...,PatternKN) [when GuardSeqK] ->
              BodyK
end

有趣的表达从关键字开始,以关键字fun结束end。在它们之间是一个函数声明,类似于regular function declaration,除了函数名是可选的并且是一个变量(如果有的话)。

有趣头部的变量影响fun函数子句中围绕有趣表达式的函数名称和阴影变量。有趣的身体绑定的变量是本地的有趣的body。

表达式的返回值是得到的乐趣。

例子:

1> Fun1 = fun (X) -> X+1 end.
#Fun<erl_eval.6.39074546>
2> Fun1(2).
3
3> Fun2 = fun (X) when X>=5 -> gt; (X) -> lt end.
#Fun<erl_eval.6.39074546>
4> Fun2(7).
gt
5> Fun3 = fun Fact(1) -> 1; Fact(X) when X > 1 -> X * Fact(X - 1) end.
#Fun<erl_eval.6.39074546>
6> Fun3(4).
24

以下有趣的表达也是允许的:

fun Name/Arity
fun Module:Name/Arity

Name/ArityName是一个原子,Arity是一个整数。Name/Arity必须指定一个现有的本地功能。表达式是语法糖:

fun (Arg1,...,ArgN) -> Name(Arg1,...,ArgN) end

Module:Name/Arity,,ModuleName是原子,Arity是一个整数。从Erlang/ OTP R15开始,ModuleName,并且Arity也可以是变量。以这种方式定义的乐趣是指在最新版本的模块中Name带有arity 的功能。以这种方式定义的乐趣不依赖于定义它的模块的代码。ArityModule

提供了更多示例Programming Examples

8.19捕捉和抛出

catch Expr

返回Expr评估期间发生异常的值。在这种情况下,该例外被捕获。

对于类的异常error,即运行时错误,{'EXIT',{Reason,Stack}}将返回。

对于类的例外exit,就是所谓的代码exit(Term){'EXIT',Term}返回。

对于类的异常throw,即被调用的代码throw(Term),将Term被返回。

Reason取决于发生的错误类型,并且Stack是最近函数调用的堆栈,请参阅Exit Reasons

例子:

1> catch 1+2.
3
2> catch 1+a.
{'EXIT',{badarith,[...]}}

注意,catch低优先级和catch子表达式通常需要包含在块表达式或括号中:

3> A = catch 1+2.
** 1: syntax error before: 'catch' **
4> A = (catch 1+2).
3

BIF throw(Any)可用于从功能非本地返回。它必须在一个catch返回值中进行评估Any

例子:

5> catch throw(hello).
hello

如果throw/1未在捕获范围内计算,则nocatch出现运行时错误。

8.20试试

try Exprs
catch
    [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] ->
        ExceptionBody1;
    [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] ->
        ExceptionBodyN
end

这是对catch.它使下列方面有可能:

  • 区分不同的异常类。
  • 选择只处理所需的。
  • 将其他人传递给封闭的try或者catch默认的错误处理。

注意,尽管该关键字catch在所使用的try表达,不存在catch所述内表达try的表达。

它将返回Exprs(一系列表达式Expr1, ..., ExprN)的值,除非在评估期间发生异常。在这种情况下,将捕获异常,并将ExceptionPattern具有正确异常类的模式Class依次与捕获到的异常进行匹配。省略Class是简写throw。如果匹配成功并且可选的保护序列ExceptionGuardSeq为真,则相应ExceptionBody的评估将成为返回值。

如果在评估过程中发生异常,Exprs但是没有ExceptionPattern将权限Class与真正的保护序列进行匹配,则将异常传递为Exprs未包含在try表达式中。

如果在评估过程中发生异常ExceptionBody,则不会被捕获。

try表达式可以具有of部分:

try Exprs of
    Pattern1 [when GuardSeq1] ->
        Body1;
    ...;
    PatternN [when GuardSeqN] ->
        BodyN
catch
    [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] ->
        ExceptionBody1;
    ...;
    [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] ->
        ExceptionBodyN
end

如果Exprs没有例外的成功评估,Pattern则按照与case表达式相同的方式将模式顺序地与结果进行匹配,除非如果匹配失败,try_clause则会发生运行时错误。

Body没有发现评估过程中发生的异常。

try表达式还可以增加一个after部分,用于清理副作用:

try Exprs of
    Pattern1 [when GuardSeq1] ->
        Body1;
    ...;
    PatternN [when GuardSeqN] ->
        BodyN
catch
    [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] ->
        ExceptionBody1;
    ...;
    [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] ->
        ExceptionBodyN
after
    AfterBody
end

AfterBody在任一个Body或之后进行评估ExceptionBody,不管是哪一个。评估值AfterBody丢失; try表达式的返回值与after没有的部分相同。

即使评价期间发生异常BodyExceptionBodyAfterBody进行评价。在这种情况下,异常AfterBody在评估之后传递,因此try表达式的异常与after没有异常的节相同。

如果在评估过程中发生异常AfterBody,它不会被捕获。因此,如果AfterBodyExprs,中的异常之后进行评估Body,或者ExceptionBody该异常丢失并被异常所掩盖AfterBody

ofcatchafter部分都是可选的,只要至少有一个catch或一个after部分。所以以下是有效的try表达式:

try Exprs of 
    Pattern when GuardSeq -> 
        Body 
after 
    AfterBody 
end

try Exprs
catch 
    ExpressionPattern -> 
        ExpressionBody
after
    AfterBody
end

try Exprs after AfterBody end

接下来是一个使用的例子after。这会关闭该文件,即使在出现异常情况时file:read/2也是如此binary_to_term/1。例外的情况是一样的,而不try... after... end表达式:

termize_file(Name) ->
    {ok,F} = file:open(Name, [read,binary]),
    try
        {ok,Bin} = file:read(F, 1024*1024),
        binary_to_term(Bin)
    after
        file:close(F)
    end.

接下来是一个使用try模拟的例子catch Expr

try Expr
catch
    throw:Term -> Term;
    exit:Reason -> {'EXIT',Reason}
    error:Reason -> {'EXIT',{Reason,erlang:get_stacktrace()}}
end

8.21括号表达式

(Expr)

operator precedences例如,在算术表达式中,带括号的表达式可用于覆盖:

1> 1 + 2 * 3.
7
2> (1 + 2) * 3.
9

8.22块表达式

begin
   Expr1,
   ...,
   ExprN
end

块表达式提供了一种将表达式序列分组的方式,类似于一个子句体。返回值是最后一个表达式的值ExprN

8.23清单理解

列表推导是许多现代函数式编程语言的一个特征。根据一定的规则,它们为列表中的元素提供了简洁的符号。

列表理解类似于Zermelo-Frankel集合理论中的集合理解,在Miranda中被称为ZF表达式。他们是类似于setoffindall谓词的Prolog。

列表理解是用以下语法编写的:

[Expr || Qualifier1,...,QualifierN]

这里Expr是一个任意表达式,每个表达式Qualifier都是一个生成器或一个过滤器。

  • 一个生成器写成: Pattern <- ListExprListExpr必须是一个表达式,该表达式评估为术语列表。
  • 一个位串发电机被写为:

BitstringPattern <= BitStringExpr.

BitStringExpr 必须是一个表达式,它的计算结果是一个位串。

  • 滤波器是一个表达式,计算结果为truefalse在发电机模式.The变量,函数子句中阴影变量,周围列表comprehensions.A列表理解返回一个列表,其中,所述元件是评估的结果Expr为发电机的每个组合列表元素和位串生成器元素,所有过滤器均为真。

例如: 1> [X * 2 || X < - [1,2,3]]。[2,4,6]当没有生成器或位串生成器时,Expr如果所有过滤器都为真,则列表理解返回带有一个元素的列表(评估结果),否则返回空列表。

例如: 1> [2 || is_integer(2)]。[2] 2> [x || is_integer(x)]的。[]中提供了更多示例Programming Examples.8.24位串理解位串理解类似于列表理解。它们用于高效且简洁地生成位串。位串的解析使用以下语法编写:<< BitStringExpr || Qualifier1,...,QualifierN >> BitStringExpr是一个评估位串的表达式。如果BitStringExpr是函数调用,则必须用括号括起来。每个Qualifier都是发生器,位串发生器或过滤器。

  • 一个生成器被写为:

Pattern <- ListExpr.

ListExpr必须是计算为术语列表的表达式。

  • 一个位串发生器写成: BitstringPattern <= BitStringExprBitStringExpr必须是一个评估为位串的表达式。
  • 滤波器是中的一个表达式truefalse

生成器模式中的变量,函数子句中的影子变量,围绕位串综合。

位字符串理解返回一个位字符串,该字符串是通过连接计算结果来创建的。BitString对于位串生成器元素的每个组合,所有过滤器都是真的。

例子:

1> << << (X*2) >> || <<X>> <= << 1,2,3 >> >>.
<<2,4,6>>

中提供了更多的示例。Programming Examples.

8.25保护序列

后卫序列是警卫的序列,由分号(;)分隔。如果至少有一名守卫是真的,则守卫顺序为真。(剩下的警卫,如果有的话,没有评估。)

Guard1;...;GuardK

后卫是保护表达式的序列,由逗号(,)隔开。如果所有警卫表达评估为“守卫”的话true

GuardExpr1,...,GuardExprN

有效的守卫表达式集(有时称为守卫测试)是有效的Erlang表达式集的一个子集。限制这组有效表达式的原因是必须保证对警卫表达式的评估没有副作用。有效的警戒表达如下:

  • 原子true
  • 其他常量(术语和绑定变量),都被认为是错误的
  • 呼叫表中指定的BIF Type Test BIFs
  • 术语比较
  • 算术表达式
  • 布尔表达式
  • 短路表达式(andalso/ orelse

| is_atom/1 |

|:----|

| is_binary/1 |

| is_bitstring/1 |

| is_boolean/1 |

| is_float/1 |

| is_function/1 |

| is_function/2 |

| is_integer/1 |

| is_list/1 |

| is_map/1 |

| is_number/1 |

| is_pid/1 |

| is_port/1 |

| is_record/2 |

| is_record/3 |

| is_reference/1 |

| is_tuple/1 |

请注意,大多数类型测试BIF具有较旧的等值,没有is_前缀。这些旧的BIF仅保留用于向后兼容,不得用于新代码。他们也只被允许在顶层。例如,它们在守卫中不允许在布尔表达式中使用。

| abs(Number) |

|:----|

| bit_size(Bitstring) |

| byte_size(Bitstring) |

| element(N, Tuple) |

| float(Term) |

| hd(List) |

| length(List) |

| map_size(Map) |

| node() |

| node(Pid|Ref|Port) |

| round(Number) |

| self() |

| size(Tuple|Bitstring) |

| tl(List) |

| trunc(Number) |

| tuple_size(Tuple) |

如果算术表达式,布尔表达式,短路表达式或对防护BIF的调用失败(由于参数无效),则整个防护失败。如果守卫是守卫序列的一部分,则评估序列中的下一名守卫(即下一个分号后面的守卫)。

8.26运营商优先权

优先级降低的运营商优先级:

:

Unary + - bnot not

/ * div rem band and

Left associative

    • bor bxor bsl bsr or xor

Left associative

++ --

Right associative

== /= =< < >= > =:= =/=

andalso

orelse

= !

Right associative

catch

评估表达式时,首先评估具有最高优先级的运算符。具有相同优先级的运营商根据其关联性进行评估。

例子:

左边的关联算术运算符从左到右进行求值:

6 + 5 * 4 - 3 / 2 evaluates to
6 + 20 - 1.5 evaluates to
26 - 1.5 evaluates to
24.5
Erlang 20

Erlang 是一种通用的面向并发的编程语言,可应付大规模开发活动的程序设计语言和运行环境。

主页 https://www.erlang.org/
源码 https://github.com/erlang/otp
版本 20
发布版本 20.1