第一章 入门
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第一章入门
首先,你需要在计算机上安装Pike。
如果你还没有安装,那么请参考这里:
附录E:如何安装Pike。
还有一点很重要的是,要实现下面的例子,请确保Pike的binary 在你的Unix主机搜索路径下。
如果你对此有任何疑问,请参考相关UNIX文档或者在UNIX 的入门书籍中寻求答案。
1.1 第一个Pike程序
int main()
{
write("hello world\n");
return 0;
}
我们将这个文件命名为hello_world.pike, 接下来就可以运行它了:
$ pike hello_world.pike
hello world
$
很简单的一个例子,让我们来看看每个语句都有什么作用:
int main()
main方法的开始部分,在函数名之前指定返回值的类型,在这个例子中,int 代表Pike中的integer number 类型。
括号中没有内容,表示该方法没有任何参数。
一个Pike 程序中至少要包含一个函数,即主函数。
该函数是程序的入口,其他的函数都直接或间接地由该函数调用。
我们可以认为主函数是由操作系统调用的。
和其它语言一样,Pike 是建立在函数的概念上的,也就是说,程序被分割成许多小的模块或者函数,每个部分都拥有自己的功能(可能非常复杂)。
一个函数的定义包括几个固定的部分:返回值类型、函数名、参数(如果有的话)、函数体。
一个函数也是某个对象的一部分,你在使用Pike编程的时候可以不用考虑对象的问题,但是你应该清楚,你所编写的程序本身就是一个对象。
下面,让我们来看看例子的函数体:
{
write("hello world\n");
return 0;
}
在函数体中,程序说明和语句一般是放在一起的,多条语句组成的程序块放在大括弧中,每条语句都使用分号作为结束. 每次函数被调用,则函数体中的所有语句都将被执行。
write("hello world\n");
第一条语句调用了系统函数write。
将会把参数作为输入数据执行write函数中的语句。
在这个过程中,字符常量“hello world\n”(当然不是纯粹的常量,\n转义符表示在字符串后另起一行)被传递给write函数,write函数在执行时将会把这个字符串显示到stout上, Stdout是标准的Unix输出通道(standard output?),一般来说来是显示器。
return 0;
本语句结束此函数,并返回0。
在return语句后的任何语句都不会被执行。
1.2 改善hello_world.pike
通过键入“pike hello_world.pike”来运行程序显得并不是那么科学。
幸运的是,Unix为我们提供了可以自动运行的机制,如果我们对hello_world.pike做如下修改:
#!/usr/local/bin/pike
int main()
{
write("hello world\n");
return 0;
}
接下来我们告诉Unix主机hello_world.pike是可以运行的(即添加执行权限),然后我们在运行hello_world.pike的时候,就不会再为首先要运行Pike而烦恼了。
$ chmod +x hello_world.pike
$ ./hello_world.pike
hello world
$
注意: Hash bang (#!) 必须放在文件的开头,前面不允许有任何内容(包括空格)!Hash bang后面的文件名必须是Pike执行文件的完整路径,且不能超过30个字符。
1.3 进一步修改
现在,试想一下,如果显示“Hello world!”是不是比“Hello world”更好一些呢?但是我们又不想让新的程序与旧的版本发生冲突,因为可能有人会需要程序按照原有的样子运行。
所以我们在新的程序中添加相关的控制逻辑来使它能显示出原有的“Hello world”,同时,我们还需要让程序能够通过命令行中的操作,来决定最终会显示什么内容。
以下是修改后的代码:
#!/usr/local/bin/pike
int main(int argc, array(string) argv)
{
if(argc > 1 && argv[1]=="--traditional")
{
write("hello world\n"); // old style
}else{
write("Hello world!\n"); // new style
}
return 0;
}
运行结果:
$ chmod +x hello_world.pike
$ ./hello_world.pike
Hello world!
$ ./hello_world.pike --traditional
hello world
$
那么在新的版本中到底有哪些改变呢?
int main(int argc, array(string) argv)
在这个版本中,括弧中加入了两个变量,也就是说现在的main函数具有两个参数,其中一个是int类型的argc,另一个则是字符串数组argv。
参数取自于Pike运行的命令行,其中,argc是命令行中一共包含多少个单词(包括命令本身),argv则是包含了所有这些单词的字符串数组。
有关Main()函数中参数的说明:
关于argc和argv的取值,译者经过实践,其详细取值规则如下:
1、argc是命令中“pike”或“./”后单词的个数;
2、argv是命令中“pike”或“./”后单词的具体内容;(一般说来第一个是pike文件名,以后是需要带入的参数)
例如:命令“./hello_world.pike 1 2 3”和“pike hello_world.oike 1 2 3”中Argc=4
Argv[0]=文件hello_world.pike的绝对路径
Argv[1]=1
Argv[2]=2
Argv[3]=3
if(argc > 1 && argv[1] == "--traditional")
{
write("hello world\n"); // old style
}else{
write("Hello world!\n"); // new style
}
这是一个if-else结构,如果判断条件为真的话,将执行第一个括弧中的内容,反之将执行第二个括弧中的内容。
让我们来看看判断语句:
argc > 1 && argv[1] == "--traditional"
简单来说,这句话的含义是:argc大于1,并且argv中的第二个元素等于字符串“--traditional”(注意argv[1]表示第二个元素,)。
因为argc是命令行中单词的个数,所以只有当program invocation(运行命令?)后跟有其他内容的时候,该判断语句的第一部分才为真。
再来看看注释:
write("hello world\n"); // old style
“//”之后一直到本行结束前,都属于注释的内容。
在计算机解析源码的过程中,注释的部分将会被忽略。
注释用来告诉代码阅读者(包括你自己)这段代码的功能,从而使你的代码更易读懂。
Pike中的注释允许使用和C语言中的注释类似的风格,比如使用“/*…*/”,允许在其中放入几行内容。
而“//”只能包含本行内容。
(似乎大多数语言都支持这样的注释方式,原文作者问什么要强调C-style呢?)
1.4 控制结构
在学习使用Pike语言的过程中,你首先要意识到的是Pike语言和其他语言一样,一次只能执行一条语句。
在绝大多数时候,语句的执行是简单的自上而下逐条进行。
然而仅仅是一串长长的语句列表,并不能组成interesting program(有趣的程序?有用的程序是不是更通顺一点,谁知道外国人是怎么想的…)。
因此在Pike语言中,我们使用控制结构,来让程序按照比自上而下运行更有意思的顺序来运行。
我们已经见过了if结构:
if( expression )
statement1;
else
statement2;
if结构检查判断语句的值,如果结果是true,则执行statement1, 否则将执行statement2.如果你不许有statement2,则可以去掉整个else部分,如下:
if( expression )
statement1;
在这种情况下,如果判断条件为true,则执行statement1,否则将不执行任何语句。
新手提示:回过头看看我们的第一个例子,确保你已经理解if结构的功能。
另外一个简单的控制结构是while结构
while( expression )
statement;
执行逻辑是:如果条件语句为true,则执行statement。
然后返回继续对条件语句进行判断。
反复执行知道条件语句不再为true。
这种控制结构叫做循环,是构建有用的程序的基础。
1.5 方法
另外一种我们已经见过的控制结构是方法。
方法是一组Pike语句的集合,它可以结合
不同的参数在程序的不同地方运行。
一个方法应按如下格式定义:
modifiers type name(type varname1, type varname2, ...)
{
statements
}
“modifiers”是可变的,详细介绍参见section 6.8 "Modifiers". “type”指定了方法返回的数据类型。
例如:“int”则说明该方法将返回一个整型的数字(integer number)“name”用于指定方法的名称以便调用。
括号中的名称是指该方法的参数,一半是定
义为方法中的一些变量,参数都需要实现指定其数据类型。
省略号表示你可以为自己的方法指定最多256个参数。
花括弧中的“statements”是方法的主体,一旦方法被调用,这里面的语句就将被执行。
例:
int sqr(int x) { return x*x; }
上例中定义了一个叫做sqr的方法,包含一个int型的参数,其返回值类型也是int 型,调用该方法将返回参数的平方。
在实际调用该方法的时候,若你使用sqr(17),将得到一个整型的数值289.
如上例所示,“return”用来指定方法的返回值。
返回值的类型必须和方法预先指定的返回值类型一致。
若指定的返回类型是void,则不需要在return后面添加任何内容。
需要注意的是,一旦return语句被执行,整个方法将立即结束。
return语句以后的所有内容都会被忽略。
还有许多控制结构,将会在后面关于控制结构的章节中详细介绍。
1.6 True和false
在本章中多次涉及到true和false,但都没有介绍其含义。
在Pike中,对true 和false有明确的定义,0代表false,其余的非 0值都认为是true。
(除非你重写了相关的operator,这将在后面的章节中讲解)。
1.7 数据类型
正如在第一个例子中见到的那样,我们需要为方法的返回值指定数据类型,在定义变量的时候也需要指定其类型。
前文中我们曾使用过integers (int), strings (string),以及arrays (with the * notation). 另外还有mapping, mixed, void, float, multiset, function, object and program. 其中mixed和void 都不是真正的数据类型。
Void表示没有任何返回值;mixed说明返回类型可以是多种类型,或者其定义的变量可能是多种类型的值。
Function, object和program都是和面向行对象相关的数据类型。
在这里我们将详细介绍剩下的3种类型(其实介绍了6种,估计是笔误)。
Int
Integer类型用于存储一个带符号整数(即有正负之分)。
针对不同的操作系统和环境,可能是32位或者64位。
Float
这种类型的变量用于存储浮点数。
Array
Arrays用于存储一组数据。
在Pike中,Arrays是一组值的集合。
他们是动态
分配的,不需要像C语言那样在使用前申明。
Arrays在创建时可以指定其初始
值,如下:
arr=({1,2,3});
另一方面,如果你已经创建了一个array,你可以通过如下语句修改相关的值:arr [ ind ] = data;
上述语句将array中的第ind个值置为data。
ind必须是整数,起始值是0。
A negative index will count from the end of the array rather than from the beginning, -1 being the last element.(这句话实在不知道在讲什么,还是请
读者自己体会吧^-^)在定义一个变量是数组类型时,我们通过在变量名前加上
“array”来实现,如下:
array i;
我们可以在一条语句中同时定义几个数组:
array i, j;
如果我们需要指定数组元素类型是string型,那么可以做如下定义:
array (string) i;
String
string用于保存一串字符和文本(有区别吗?),比如一个单词、一句话或者一本书。
需要注意的是,除了从A到Z这些字母外,特殊字符、空字符、newlines (回车符?)等等,只要是8-bit的字符,都可以存储在string型变量中。
String 是Pike中的基本类型,它不像C语言那样,将string理解为元素类型为char
的数组。
这就意味这不能单独修改string中的某个字符。
另外,所有的string 都是“共享的”,也就是说,同一个string在程序的不同地方都用到了,他在内存中将只会被保存一次。
当你在程序中写入字符串时,可能有两重含义(即该字符串可能是程序代码本身,也可能是代码中所引用的字符串。
在此情况下,就需要使用特殊字符/转义字符),使用特殊字符\转义字符需要遵循如下规则:
\n 换行
\r 回车
\t tab
\b Backspace(回退)
\" " (引号)
\\ \ (反斜线)
Mapping
Mapping可以看作是不仅仅使用整数作为索引的array.他也可以被看作是将数
据(通常是字符串)联系在一起的方式。
Mapping由许多组“索引-数据”对应
关系组成,其组合方式是map[索引1]代表数据1。
Mapping可以使用和array
类似的初始化方式:
mapping(string:string) map=(["five":"good", "ten":"excellent"]);
你还可以使用map["five"]="good"这样的格式来初始化。
如果你尝试给一个并不存在的索引赋值,那么系统将自动创建这个索引。
Multiset
Multiset是一个没有“数据”的Mapping。
当引用到multiset的某个索引时,如果这个索引存在,则会返回1,否则将返回0。
(至于这种结构的用途是什么,到目前为止译者还没有发现)。