2.7 浮点数
让我们来做一些浮点数的运算:
1> 5/3.
1.66667
2> 4/2.
2.00000
3> 5 div 3.
1
4> 5 rem 3.
2
5> 4 div 2.
2
6> Pi = 3.14159.
3.14159
7> R = 5.
5
8> Pi * R * R.
78.5397
不要在此感到困惑。在第一行结尾的数是整数3。表达式的结尾句号不是一个小数点。如果我想要一个小数点应该这样写3.0。
“/”常常返回一个浮点数;由此,4/2的值是2.0000(在shell)。N除M
和N(rem)M是用来进行整数除法和余数运算的;由此5除3是1,5除3的余数是2。
浮点数必须在小数点后有至少一位的十进制数。当你使用“/”来使两整数相除时,结果是自动转化为浮点数的。
2.8 常量
在Erlang中,常量被用来表示不同非数值得常数。
如果你以前列举过C和Java中的数据类型,你可能已经了解常量。
C程序员对使用宏定义的格式很熟悉。典型的C程序将定义一系列的全局常量,这些常量包含在一个定义常量的文件中;例如;这可能是一个glob.h的文件包含的内容。
#define OP_READ 1
#define OP_WRITE 2
#define OP_SEEK 3
...
#define RET_SUCCESS 223
...
典型的C代码使用宏定义可能如下所示:
#include "glob.h"
int ret;
ret = file_operation(OP_READ, buff);
if( ret == RET_SUCCESS ) { ... }
在一个C程序中,这些常量的值并不感兴趣;他们在这里只是意味着他们是不同的,他们能够与等式比较。
在Erlang中,等效的表达可能是这样的:
Ret = file_operation(op_read, Buff),
if
Ret == ret_success ->
...
在Erlang中,常量是全局的,这不需要使用宏定义或者是包括头文件。
假设你想要写编写一段处理星期的程序。你怎样来在Erlang中表示星期,你可以使用常量星期一、星期二…..
常量开始于小写字母,紧接着是一连串的字母或者是下划线或者是(@)符。例如:red, december, cat, meters, yards, joe@somehost, and a_long_name.
常量能够以(‘)符来引用。使用引用的格式,我们创建的常量以大写字母打头(否则会被当成变量)或者是包括字符字母。例如:‘Monday’, ‘Tuesday’,
’+’, ’*’, ’an atom with spaces’。你甚至可以不使用引号引用常量,所以‘a’的含意与a一样。
常量的含义就是常值。所以如果你你所给的命令就是一个常量,在Erlang shell中将在打印这个常量:
1> hello.
hello
这可能有一些奇怪,当我们讨论常量的值或者是整数的值。但是因为Erlang是一个功能编程语言,每一个表达式必须有一个值。这些包括整数和常量,并且都是简单的表达式。
2.9 Tuples
假设你想一系列复杂的元素组成一个整体。在这种情况下你使用Tuples。你可以通过将有用Tuples用括号围起来,用逗号分割这种方式来创建一个Tuples。所以,例如,如果你想表示某人的名字和身高,你可以这样使用{Joe,1.82}。这就是一个Tuples包括一个常量和一个浮点数。
Tuples类似于C中的结构体,不同点就是Tuples是无名的。在C中,指针变量P可能如下定义:
struct point {
int x;
int y;
} P;
你可以使用点操作符得到结构体内部的变量。所以为了设置point中x和y的值,你可以这样写:P.x = 10; P.y = 45;
Erlang没有类型的声明,所以为了创建一个“point”,我们可以这样写:
P = {10, 45}
这就创建了一个Tuples同时把它绑定到了变量P。和C不一样的是,Tuple没有名字。因为Tuple本身就含有两个整数,我们不得不记住他的用途。为了便于记忆,我们常常使用常量作为Tuple的第一个元素,这个常量描述了它代表的含义。所以我们写{point, 10, 45} 而不是 {10, 45},这使得程序易于理解。
Tuples是易用的。假设我们想要表达某个人的一些事实—名字、身高、鞋大小和眼睛的颜色。我们可以这样做:
1> Person = {person,
{name, joe},
{height, 1.82},
{footsize, 42},
{eyecolour, brown}}.
注意:我们使用常量来定义域和(例如名字和眼睛的颜色)来给这个域一个值。
创建Tuples
在我们声明时,自动创建Tuples;当我们不使用是将自动销毁。Erlang使用内存收集来回收所有的未用内存,所以我们不需要担心内存的回收。
如果我们建立一个新的Tuple时使用一个变量,新的Tuple将会使用分享数据结构中的变量的值。这有一个例子:
2> F = {firstName, joe}.
{firstName,joe}
3> L = {lastName, armstrong}.
{lastName,armstrong}
4> P = {person, F, L}.
{person,{firstName,joe},{lastName,armstrong}}
如果你试图创建一个包含未知变量的数据结构,你将会看到错误。所以在下一行,如果我们无奈试图使用变量没有定义的Q,得到以下的错误:
5> {true, Q, 23, Costs}.
** 1: variable 'Q' is unbound **
这就意味着变量Q没有定义。
从Tuples中提取值
早先,我们说=,就像一个赋值的声明,它不仅仅是一个赋值的声明还是一个格式匹配运算符。你想知道为什么为什么我们这么书生气。当然,它表明格式匹配时Erlang中的基础,他被用来做许多不同的工作。他被用来提取数据结构中的值,他还被用来在函数中控制循环,还有但你给进程发消息时,它负责将选择哪条消息是由平行的程序发出的。
如果你想从一个Tuples中选出一些值,我们使用=操作符。
让我们来看看我们的Point Tuples:
1> Point = {point, 10, 45}.
{point, 10, 45}.
假设我们想要从Point中取出X、Y并给他们赋值,我们可以这样做:
2> {point, X, Y} = Point.
{point,10,45}
3> X.
10
4> Y.
45
在命令的第二行,X对应着10而Y对应着45.表达式Lhs = Rhs是定义Rhs的,所以shell打印{point,10,45}。
正如你所见,等号两边的数据必须有相同的元素数据,并且等号一边的元素必须和与他类似的数值绑定。
现在假设你这样输入:
5> {point, C, C} = Point.
=ERROR REPORT==== 28-Oct-2006::17:17:00 ===
Error in process <0.32.0> with exit value:
{{badmatch,{point,10,45}},[{erl_eval,expr,3}]}
会发生什么?{point,C,C}的格式和{point,10,45}并不匹配,因为C不同同时为10和45。因此,格式匹配失败,系统打印出一个错误的消息。
如果你有一个复杂的元祖,你可以从元祖中取出这些值,但是需要通过编写一个有相同格式的元祖,然后在此格式中赋给你想要的值。
为了说明这些,我们首先定义一个包括复杂结构的变量person:
1> Person={person,{name,{first,joe},{last,armstrong}},{footsize,42}}.
{person,{name,{first,joe},{last,armstrong}},{footsize,42}}
现在我们来提取person的名字:
2> {_,{_,{_,Who},_},_} = Person.
{person,{name,{first,joe},{last,armstrong}},{footsize,42}}
最后,打印出who的值:
3> Who.
Joe
注意:在之前的例子中,我们使用_作为变量的占位符。符号_叫做匿名变量。与以往的常规变量不同,_并不用与任何值绑定。
2.10 列表
我们使用列表来存储变量的这些值:这些你想从内存中得到的东西,行星的名字,你的主要参数返回的结果等等。
我们建立列表通过把列表的元素封入到方括号中,并且把他们用逗号分开。这儿使我们建立的购物的例子:
1> ThingsToBuy = [{apples,10},{pears,6},{milk,3}].
[{apples,10},{pears,6},{milk,3}]
单个的元素能够是任意类型的,所以,例如,我们可以这样写:
2> [1+7,hello,2-2,{cost, apple, 30-20},3].
[8,hello,0,{cost,apple,10},3]
术语:
我们调用列表开头的第一个元素。如果你想设想移除列表的开头元素,剩下的叫做列表的尾元素。
例如,如果我们有一个这样的列表[1,2,3,4,5],列表的头元素是1,列表尾是[2,3,4,5]。注意列表的头能够是任意的东西,然后可以处理列表的尾。
定义列表:
如果T是一个list,那么[H|T]也是一个列表,它的头元素是H尾元素是T。符号|分隔了头和尾。[]是一个空的列表。
无论何时我们构造一个列表使用[…|T]构造,我们应当保证T是一个列表。如果他是,那么新的列表将会是“模板形成”。如果T不是一个列表,那么新列表可以说成是“不当列表”。大多数的库函数假定列表是模板形成,并且不能为不当列表工作。
通过编写[E1,E2,….En|T],我们能够添加超过一种的元素来开始T。例如:
3> ThingsToBuy1 = [{oranges,4},{newspaper,1}|ThingsToBuy].
[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}]
从列表中引用元素
正如此,我们能够从列表中引用元素使用格式匹配操作。如果我们有非空的L列表,那么表达式[X|Y] = L,(X和Y并没有绑定变量)将会引用列表的头到X,列表尾到Y。
所以,我们在商店中,我们我购物清单1——我们做的第一件事是解包并存贮到他的头和尾:
4> [Buy1|ThingsToBuy2] = ThingsToBuy1.
[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}]
绑定成功
Buy1|->{oranges,4}
同时
ThingsToBuy2 7→ [{newspaper,1}, {apples,10}, {pears,6}, {milk,3}].
我们去买桔子,我们可以取出新的元素:
5> [Buy2,Buy3|ThingsToBuy3] = ThingsToBuy2.
{newspaper,1},{apples,10},{pears,6},{milk,3}]
Buy2 7→ {newspaper,1}, Buy3 7→ {apples,10},取出成功,ThingsTo-
Buy3 7→ [{pears,6},{milk,3}]。
2.11 字符串
严格的说,Erlang没有字符串。字符串实际上仅仅是整数的列表。字符串在成对出现的(“)中间,所以,例如我们能这样写:
1> Name = "Hello".
"Hello"
注意:在一些编程语言中,字符串能够使用单引号或者是双引号。在Erlang中,你必须使用双引号。
“Hello“是列表的速记他代表了单个的字符。
当shell打印列表的值作为一个字符串,但是只有列表中的所有整数代表可打印的字符:
2> [1,2,3].
[1,2,3]
3> [83,117,114,112,114,105,115,101].
"Surprise"
4> [1,83,117,114,112,114,105,115,101].
[1,83,117,114,112,114,105,115,101].
在表达式2中,列表[1,2,3]将会保持原样的打印出来。这是因为1、2和3不是可打印字符。
在表达式3中,所有的列表中的元素是可打印字符,所以打印出来也是字符串。
表达式4和表达式3一样,除了列表从1开始,这不是一个可打印字符。正因为这些打印结果没有改变。
我们不需要知道那个整形代表什么特殊的字符。我们使用“dollar syntax “来达到这个目的。所以,例如,$a是一个整形代表着字符a,等等。
5> I = $s.
115
6> [I-32,$u,$r,$p,$r,$i,$s,$e].
"Surprise"
在字符串中设置字符
字符串中的字符代表着Latin-1(ISO-8859-I)字符代码。例如,字符串中包括Swedish的名字Håkan将被编为[72,229,107,97,110]。
注意:一旦你输入[72,229,107,97,110]作为shell的表达式,你可以得不到你想要的:
1> [72,229,107,97,110].
"H/345kan"
“Håkan”发生了什么—它去了哪里?这实际上与Erlang没有什么关系,但是对于Erlang的语言环境和终端的字符代码设置有关。
至于Erlang而言,在一些编码中,字符串仅是一个整形的列表。如果他们打印Latin-1编码,他们应当正确的运行(如果你的终端设置是正确的)。
2.12 重谈模式匹配
为了是本节圆满的结束,我们再一次谈到模式匹配。
下面的表格是一些模式匹配和条件的例子。表格的目录3,标记结果,表示的是匹配的条件,这样变量的绑定将会创建。浏览这些例子,确保你真的理解他们:
Pattern Term Result
{X,abc} {123,abc} Succeeds X |→ 123
{X,Y,Z} {222,def,"cat"} Succeeds X |→ 222, Y |→ def,
Z |→ "cat"
{X,Y} {333,ghi,"cat"} Fails—the tuples have
different shapes
X true Succeeds X |→ true
{X,Y,X} {{abc,12},42,{abc,12}} Succeeds X |→ {abc,12}, Y |→ 42
{X,Y,X} {{abc,12},42,true} Fails—X cannot be both
{abc,12} and true
[H|T] [1,2,3,4,5] Succeeds H |→ 1, T |→ [2,3,4,5]
[H|T] "cat" Succeeds H |→ 99, T |→ "at"
[A,B,C|T] [a,b,c,d,e,f] Succeeds A |→ a, B |→ b,
C |→ c, T |→ [d,e,f]
如果以上你有什么不明白请把表达式打入shell中,看看结果如何。
例如:
1> {X, abc} = {123, abc}.
{123,abc}.
2> X.
123
3> f().
ok
4> {X,Y,Z} = {222,def,"cat"}.
{222,def,"cat"}.
5> X.
222
6> Y.
def
...
注意:f()命令告诉shell忘记所有绑定的信息。在这个命令之后,所有变量都没有设置,所以第四行的X对于1、2行的X没有作用。
现在我们熟悉了基本数据类型,简单指定和模式匹配,所以我们可以更上一个台阶来看看如何定义函数和系数。我们来看下一章节。