进入Ruby的世界——基础篇

举报
Tom forever 发表于 2020/02/09 12:34:47 2020/02/09
【摘要】 本文主要内容Ruby语法的生存工具箱[1]Ruby基础编程指引:程序编写、保存、运行和错误检查Ruby安装指南Ruby的扩展机制Ruby中易用的命令行工具,包括交互式Ruby解释器(irb)本文是ruby基础中的基石。目标是让读者在开始学习Ruby之前掌握足够的知识和技巧。接下来读者将看到Ruby的基本语法和技术,以及Ruby的运行机制:如何写一个程序,怎样使用Ruby运行程序,以及如何把一...

本文主要内容

  • Ruby语法的生存工具箱[1]

  • Ruby基础编程指引:程序编写、保存、运行和错误检查

  • Ruby安装指南

  • Ruby的扩展机制

  • Ruby中易用的命令行工具,包括交互式Ruby解释器(irb)

本文是ruby基础中的基石。目标是让读者在开始学习Ruby之前掌握足够的知识和技巧。

接下来读者将看到Ruby的基本语法和技术,以及Ruby的运行机制:如何写一个程序,怎样使用Ruby运行程序,以及如何把一个程序分写到多个文件中。此外,读者还将学习到一些开关(switch)[2]的用法,即它们如何改变Ruby解释器(名为ruby的程序,用于执行用Ruby语言写的程序文件)的作用,也会学习使用一些重要的辅助工具,让读者的Ruby程序员生涯更加轻松和高效。

本章将Ruby领域的观点分为以下3个基本层次。

  • 语言核心:设计原则、语法、语义。

  • Ruby支持的扩展(extension)和类库(library),以及用户自己添加到扩展中的工具。

  • Ruby自带的命令行工具,用于运行解释器和其他一些重要工具。

这3个部分在一个系统中环环相扣,因此在本书中它们的内容会相互贯穿,不过在本章中会尽量分开讨论。尽管如此,这3个部分的内容都会贯穿在书的每一章中。

Ruby,ruby,还是RUBY?!

 

Ruby是一门编程语言。我们会谈论“学习Ruby”,还会问一些问题,比如“你知道Ruby吗?”ruby是指一个计算机程序,特指Ruby的解释器,它可以读取并运行程序。可以看到这个命名方式在一些文章中出现,如“你用ruby运行我的文件,但什么也没发生。”,或者“ruby可执行文件的完全路径是什么?”最后是RUBY,准确来说,没有这样的写法。Ruby不是一个缩略语词汇,所有字母都大写的拼写方式向来都是错的。人们在对待Perl语言的时候也出现过同样的错误,或许是因为他们看到了如BASIC和COBOL这样的拼写方式,但Ruby不同。一般用“Ruby”来表示编程语言,用“ruby”来表示Ruby的解释器。

第1章不是只为服务于其他章节而存在的,它也有自己存在的价值:学习真正的Ruby技术和这门语言设计中的要点。其目标是为了引导和启发读者,但即使如此,学习过程还是会深入Ruby语言的一些关键层面。

1.1 Ruby语言基础知识

本节的目标是让读者开始接触Ruby。这里采用广度优先的方法:大致围绕语法学习、代码编写、程序运行这样一个循环过程展开。

说到这里,读者需要在自己的电脑上安装好Ruby。书中的例子都使用Ruby 2.1.0[3]编写。还需要准备一个文本编辑器(任何偏好的编辑器都可以,也可以用纯文本编辑器,但文字处理软件不行)和一个存放Ruby程序文件的目录(亦称文件夹)。可以命名目录为rubycode或者rubysamples,无论什么名字都可以,只要能够区别于其他工作区并方便地找到练习程序文件即可。

交互式Ruby控制台程序(irb)是最好的朋友


随Ruby发布的irb实用工具,是一个Ruby命令行工具,irb使用的广泛程度高于Ruby解释器本身。启动irb之后,键入Ruby代码,它将执行代码并输出结果值。

在命令行中键入irb,然后输入在文中所见的示例代码。例如:


打开irb会话意味着可以随时测试任意数量的Ruby代码段。大多数Ruby程序员都发现irb是不可缺少的,而且本章中的一些示例也是使用irb运行的。

读者将看到下面irb的示例使用了命令行选项,它的输出结果更易于阅读。


运行irb时加入此选项或者不加,就可以看到--simple-prompt选项的效果。正如所见,这个简单的提示符选项将使屏幕更简洁明了。默认的(非简单的)提示符显示了更多的信息,如在交互会话时的行号统计。但从众多的示例可以看出,简单的提示符已足够使用。

因为irb是一个用于运行Ruby语言的命令行工具,所以直到1.4.2节才会详细讨论它。如有需要,可以转到那一节去看一下,这是最直接的方式。

完成Ruby的安装,并创建好工作目录的,就继续学习Ruby吧,这样我们就能分享Ruby学习和探索中的见地。掌握Ruby的语法将会是一个良好的开端。

1.1.1 Ruby语法生存包

下面3个表总结了Ruby的一些特性,这对于理解本章的例子和开始体验Ruby语言大有益处。不必记住它们,只要看一下并在稍后用到的时候回查即可。

表1-1包含了Ruby的一些基本运算。表1-2中介绍了获取基础键盘输入、将输出发送到屏幕以及基本的条件语句。表1-3简要描述了Ruby的特殊对象和注释的语法。

表1-1 Ruby的基本运算

操  作

示  例

注  解

算术运算

2+3(加法) 
2–3(减法) 
23(乘法)
2/3(除法) 
10.3 + 20.25
103 - 202.5
32.9 10
100.0 / 0.23

所有的运算都使用整型或浮点型。如例子中所示,混合使用整型和浮点型数值进行运算,其结果为浮点型 



注意:需要使用0.23而不是.23

赋值

x=1 
string = "Hello"

本操作将一个对象(右边)绑定到一个局部变量(左边)之上。与此同时,把对象看作是用变量表示的一个值

值比较

x == y

注意:使用两个等号(不同于赋值使用一个等号)

字符串到数值的转换

x = "100".toi
s = "100" 
x = s.toi

为了算术运算的顺利执行,必须保证使用的是数字而不是字符构成的字符串。to_i用于执行字符串到整型的转换

表1-2 Ruby的基本I/O(输入/输出)方法和流控制

操  作示  例注  解
打印到屏幕print "Hello" puts "Hello" x = "Hello" puts x x = "Hello" print x
 x = "Hello" px如果字符串的行尾没有包含换行符,puts就会添加一个,但print不会 print会精确打印内容并让光标留在末尾(提示:在某些系统平台,在程序输出的末尾会自动换行) P会输出一个审查字符串,它通常会包含一些额外的输出信息
获取键盘单行输入gets string = gets直接将输入的一行赋值给变量(第二个例子中的变量string
条件语句执行if x == y  puts "Yes!" else  puts "No!" end条件语句总是以单词end结束(更多细参见第6章)

表1-3 Ruby的特殊对象和注释

操  作

示  例

注  解

特殊值对象

true false nil

对象truefalse通常用于返回条件表达式的值。对象nil是“nonobject”的一种,表明值或者结果的缺失。false和nil使得条件表达式失败,所有的其他对象(包括true,当然也包括0和空字符串)使得其成功(更多细节参见  第7章)

默认对象

self

关键字self引用默认对象。self是一个依赖于运行时上下文,由不同对象扮演的角色。没有指定调用者的方法,调用的时候会默认为被self调用(更多细节参见第5章)

代码中的注释

#一个注释x = 1 #一个注释

注释部分,会被编译器忽略

以上摘要表中已经包含了许多Ruby的基础和语法。读者需要能够辨认出Ruby标识符(identifier)的几种不同写法,尤其是对Ruby中的对象和方法调用有一个感性认识。我们稍后将会谈论这些内容。

1.1.2 多种多样的Ruby标识符

Ruby的标识符类型很少,一眼就能辨认和区分它们。标识符体系如下。

  • 变量(variable):

    • 局部变量(local variable);

    • 实例变量(instance variable);

    • 类变量(class variable);

    • 全局变量(global variable)。

  • 常量(constant);

  • 关键字(keyword);

  • 方法名(method name)。

这是一个很小的体系,很容易掌握,这一节将讨论它们。记住本节的目标是学习辨认不同的标识符。本书后面将学习如何使用和使用它们的时机。这只是标识符知识的第一课。

1.变量

局部变量以小写字母或者下划线开头,包含字母、下划线或数字。xstringabcstart_valuefirstName都是有效的局部变量命名方式。然而,值得注意的是,在组合多个单词以命名局部变量时,Ruby的约定是使用下划线作为命名规范,而不使用驼峰命名法,如使用first_name而不使用firstName

实例变量为独立的对象存储信息,它通常以一个单独的符号(@)开头,后面的字符使用与局部变量相同的命名规则,如@age@last_name。尽管局部变量不能以大写字母开头,但是实例变量可以在@符号之后的第一个位置使用大写字母(但不能使用数字)。但是通常来说,@符号之后还是使用小写字母。

类变量在每一个类层级上存储信息(同样,现在也不用担心它的语义)。它与实例变量使用相同的命名规则,只有一点不同,它以两个@符号(@@)开头,如@@running_total

全局变量可以通过它的美元引导符号($)辨认出来,如$population。跟在$符号之后的语句不使用局部变量的命名规则。有一些全局变量名为$:$1$/,还有$stdin$LOAD_PATH。但只要以$符号开头,它就是一个全局变量。这些非字母的标识符是预定义的,因此不必担心其中的标点符号是否合法。


2.常量

常量使用大写字母开头。AStringFirstNameSTDIN都是有效的常量命名。在Ruby命名规范中,如遇到命名多词组合的常量时,可以使用驼峰命名法(FirstName)也可以使用下划线分隔且所有字母大写(FIRST_NAME)的方式。

3.关键字

Ruby拥有很多的关键字,它们是预定义的保留词,与特定编程任务和上下文关联。关键字包括def(用于方法定义)、class(用于类定义)、if(条件执行)和FILE(当前被执行文件的名称)。关键字大约有40个,通常是简短的、单一词汇(与下划线组合多单词方式相反)的标识符。

4.方法名

Ruby中的方法命名遵从与局部变量相同的规则和约定(除了以?!=结尾的符号,其重要作用稍后会讲述)。这是一种设计理念:方法并不因其自身作为方法而被人关注,而是简单地作为提供值的表达式融入到程序的结构中。在一些上下文中,很难一眼就区分出一个表达式是一个局部变量还是一个方法名,这一切源自设计,是有意为之。

讲完方法,现在已经有了一张Ruby标识符的“路线图”,让我们谈一下编程语言的语义,尤其是对象及其方法的重要作用。

1.1.3 方法调用、消息和Ruby对象

Ruby把所有的数据结构和值都看做对象,从整数和字符串这样简单的标量(原子的)值,到数组(array)这样的复杂的数据结构一概如此。每个对象都能响应一组特定的消息,对象能够接收的每个消息直接对应一个方法——有名称的、可以被有执行能力的对象触发的可执行例程。

对象也可以用字面量构造器表示,如字符串用双引号,或者已绑定值的变量。消息通过特殊的点运算符(.)送达:点右边的消息被发送到点左边的对象上。(另外,有许多特殊的给对象发送消息的方式,但是点是最常用和基础的方式。


何苦为该说“发送to_i消息”还是说“调用to_i方法”而烦恼?对于同一操作为什么有两种不同描述?因为它们不完全相同。大多数时候,发送消息给接收的对象,对象就会执行对应的方法。但有些时候是没有对应方法的,对于点右边的任意标识符,并不能确保接收者拥有的方法与发送的消息相匹配。

这听起来有些混乱,其实不然。因为对象可以拦截未知的消息并使它们拥有具体含义。例如,Ruby on Rails Web开发框架大量使用了如下的技术:发送未知消息到对象并拦截那些消息,然后能够在使用当前数据库表的列名作为动态条件的情况下顺畅运行。

方法可以带有参数,这个参数同时也是对象。(虽然有些用于创建和操作对象的语法结构本身不是对象,但在Ruby中几乎所有一切都是对象。)下面是一个带有参数的方法调用:


这里尽管缺少消息发送所需要的点以及该消息的显式接收者,却依然发送了消息puts并传递了参数"Hello."给一个对象:默认对象self。在程序运行期间,虽然作为self的对象会通过特定规则发生改变,但self总是被预定义好的。在第5章中将会对self进行详细阐述。现在,只要知道像puts这样的裸词的方法调用方式即可。

对象的概念在Ruby中是最为重要的,与此紧密相关并扮演重要角色的概念是类(class)。

1.1.4 编写和保存一个简单程序

在本节中,将会在之前创建的Ruby示例程序目录中创建一个程序文件。第一个程序是一个摄氏—华氏度转换工具。

注意

 当然,在真实场景中,温度的转换使用的是浮点数。而这里在输入输出时使用整数,主要是为了专注于程序的结构和程序的执行。

这个例子将被反复提到,并根据本书的需要进行添加和修改。它遵循如下的迭代过程。

  • 整理程序的输出结果。

  • 从用户的键盘输入中接收输入数据。

  • 从文件中读取数值。

  • 将程序结果写入一个文件。

第1个版本很简单,仅关注文件创建和程序运行的过程,而不用深入程序逻辑的细节。

创建第一个程序文件

使用一个纯文本编辑器,输入代码清单1-1所示的代码到一个文本文件并保存为c2f.rb到示例目录中。

代码清单1-1 简单的专用摄氏—华氏度转换工具 (c2f.rb)



注意

 根据用户的操作系统的不同,Ruby程序文件有可能仅用文件名或一个短名称就可以独立运行,并不需要使用文件扩展名。尽管如此,请记住,.rb的文件扩展名在一些情况下是强制的,这主要是涉及拥有多个文件(后面会详述)的程序和有文件间相互查找机制的程序。在本书中,所有Ruby程序文件名都以.rb结尾,这是为了确保示例程序可以在尽可能多的平台上运行,同时尽可能少地对系统进行管理干预。

现在,已经有了一个完整的(虽然很小)Ruby程序,可以运行它了。

1.1.5 给Ruby提供程序

运行一个Ruby程序需要给Ruby解释器传递程序的源码文件(或者多个文件),这个Ruby解释器名为ruby,后面会依次解释。在提供一个程序给ruby而不是请求Ruby运行程序之后,它会检查程序代码的语法错误。

1.检查语法错误

如果在转换公式中使用31替换32,就会发生一个程序性错误。Ruby还是会适时地运行程序并给出有缺陷的结果。但是假如程序的第2行中不小心遗漏了右圆括号,那就是语法错误,Ruby将不能运行这个程序。



(错误出现在第5行,即程序的最后一行,因为Ruby一直在耐心等候右圆括号出现,结果却没有。)

Ruby 解释器提供一种便捷的方式来检查语法错误而不必运行程序。它会读取文件并指出语法是否有错。为了检查源文件的语法错误,可以这样做:



命令行中的-cw标志(flag)是两个标志的简写,它们分别是:-c-w。标志-c意味着检查语法错误。标志-w可以激活高级别的警告:如果程序都合乎Ruby语法,Ruby就不会发出警告,除非有比语法更值得商榷的理由。

假如输入的文件正确,将在屏幕上会看到如下信息:



2.运行程序

为了运行程序,再次提供源文件给解释器,但是这一次不用加入-c-w标志。



如果一切顺利,将可以看到计算的结果输出如下:



计算的结果正确,但是计算的结果超过了3行,这看起来不够好。

3.温度转换器的第二次迭代

问题要追溯到puts命令和print命令的区别。假如字符串没有以一个已经存在的换行符结束,puts命令会在它打印的字符串尾部插入新换行符。相反,print打印字符串之后就停止了,它不会自动跳转到下一行。

为了修正这个问题,把前两行的puts命令改为print



(注意,is后面的空格,它是为了确保在is和数值之间有一个空格。)现在输出的是:



puts是“put(就是print)string”的缩写。尽管put没有直观的表示会调用换行符,但是puts会这样做:如同print,打印用户的数据,之后自动地转到新一行。假如让puts打印已经以换行符结束的一行,它不会再次添加换行符。

假如读者已经在其他编程语言中使用过打印的工具,而这些工具没有自动添加换行符(如Perl语言的print函数),那么可以自己编写类似Ruby中的实现,一个能打印值并添加换行符的工具:



尽管如此,大可不必这样去做,puts已经实现了。习惯使用puts吧,并在这个过程中遵循使用其他的Ruby习语和约定。

警告

 在一些操作系统平台中(尤其是在Windows中),程序运行的结尾会打印输出额外的换行符。这意味着实际上print已经代替了puts,而puts很难被系统检测到。意识到这两者的区别,并在最常用的场景中使用其中一个,可以充分确信得到所期望的结果。

看一下屏幕的输出,接下来将扩展一点I/O领域的知识,包括键盘的输入和文件的操作。

1.1.6 键盘和文件I/O

Ruby提供了很多在程序执行过程中读取数据的技术,包括从键盘读取和从磁盘文件读取。它们有许多用途:不仅仅在编写每个应用程序的过程中会用到,在编写维护、转换、管理或者操纵用户的工作环境的代码时也几乎一定会用到。本章中包括了一些输入处理的技术,更多关于I/O操作的技术详见第12章。

1.键盘输入

反复执行100摄氏度等于212华氏度的程序,其价值是有限的,更有价值的程序则是可以自由指定华氏温度并获得对应的摄氏温度。

修改程序完成如上的功能需要几个步骤,分别需要使用表1-1和表1-2中提及的方法gets(获取键盘输入的一行数据)和to_i(转换为整型),读者应该已经熟悉它们其中的一个。由于这不仅仅是修订版本而是一个新程序,所以把代码清单1-2中的代码版本放到名为c2fi.rb的新文件中(i意味着交互)。

代码清单1-2 交互式温度转换器(c2fi.rb)

如果不深究更多细节,现在这个版本已经是一个通用的摄氏度转华氏度的解决方案。接下来,让我们学习文件的读取。

2.读取文件

从Ruby程序中读取文件并不困难,至少在大多数情况下,比键盘输入还要容易。温度转换器的下一个版本将从一个文件中读取一个数组,然后从摄氏度转换为华氏度。

首先,创建一个文件并命名为temp.dat(温度数据),同时包含一个数字:



现在,创建第三个程序,命名为c2fin.rb(in意为文件输入),如代码清单1-3所示。

代码清单1-3 使用文件输入的温度转换器(c2fin.rb)



这一次,示例运行的输出结果如下:



自然地,如果在文件中改变数字,结果将会不同。那么,怎样将计算的结果写入文件中呢?

3.写入文件

从最简单的操作来说,文件写入要比文件读取复杂一些。正如代码清单1-4所示,执行写入文件的操作时,主要的额外步骤是要指定文件的模式,在这个例子中是w(意为写入)。保存代码清单1-4所示的这个版本,命名为c2fout.rb并运行它。

代码清单1-4 使用文件输入的温度转换器(c2fout.rb)  



调用方法 fh.puts Fahrenheit的作用,是将 Fahrenheit的值输出到由fh对象进行写入处理的文件中。如果检查文件temp.out,无论输入什么数字,都可以看到转换好的华氏温度值。

作为练习,可以尝试着把前面的例子进行合并,让它从一个文件读取数字,转换为华氏度,之后把结果写入不同的文件中。在适时引入一些Ruby语法的同时,下一节会检验Ruby的安装,然后很快还会依次看到Ruby如何管理扩展和库。

1.2 剖析Ruby的安装

在系统上安装Ruby意味着在许多磁盘目录中安装了Ruby语言的库和支持文件。大多数时候,Ruby都知道如何找到其所需要的这些目录而不用弹出提示。但是了解Ruby安装的知识对了解Ruby本身大有益处。


表1-5 关键的Ruby目录和它们的RbConfig术语

术  语

目 录 内 容

rubylibdir

Ruby标准库

bindir

Ruby命令行工具

archdir

特定架构的扩展和库文件(已编译,二进制文件)

sitedir

用户自己或第三方扩展和库文件(用Ruby编写)

vendordir

第三方扩展和库文件(用Ruby编写)

sitelibdir

用户自己的Ruby语言扩展(用Ruby语言编写)

sitearchdir

用户自己的Ruby语言扩展(用C语言编写)

这就是对Ruby主要的安装目录和其包含内容的一个概述。此刻不必记住它们,但要意识到需要时如何找到它们(或者好奇时浏览一下并查看Ruby的代码示例!)。

1.2.1 Ruby标准库子目录(RbConfig::CONFIG[rubylibdir]

在rubylibdir目录中,可以看到用Ruby编写的程序文件。这些文件提供了标准库的功能,如果需要它们提供的功能,可以在程序中请求(require)它们。在本目录中可以找到以下文件。

  • cgi.rb:CGI编程工具库。

  • fileutils.rb:易用的文件操作实用工具。

  • tempfile.rb:自动创建临时目录的机制。

  • drb.rb:分布式编程工具。

在标准库中有一些库,如drb,由多个文件构成。可以看到目录中有一个drb.rb文件,并且整个drb子目录包含了drb库的所有组件。

浏览rubylibdir子目录能够对Ruby提供的编程工具所能完成的任务有一个感性认识(也许一开始就是颠覆性的)。大多数程序员只使用了这些工具的子集,但这些巨大的编程库集合的子集也已经能够提供许多功能。

1.2.2 C语言扩展目录(RbConfig::CONFIG[archdir]

通常archdir目录位于rubylibdir下的第一级目录中,它包含了特定架构的扩展和库文件。通常在目录中,这些文件都是以.so、.dll、.bundle(依赖于硬件和操作系统)为文件扩展名的。这些文件是C语言扩展:它们是二进制文件,是运行时可加载的文件,由Ruby的C语言扩展代码生成,并在Ruby安装过程中编译成为二进制格式。

如在rubylibdir目录中的Ruby语言程序文件,archdir目录中的文件包含了能够加载到用户程序的标准库组件。(除此之外,还有一些rbconfig的扩展文件,这些扩展可以使用irb命令去发现它们。)这些文件不是用户可读的,但是Ruby的解释器可以读懂它们。从Ruby程序员的视角来说,由于它们都被编译为了二进制格式文件,所以所有的标准库使用都是一样的,不管它们是用Ruby编写的还是用C语言编写的。

安装在archdir目录的文件,每个平台安装的都不相同,它们依赖于其被编译的扩展。这个扩展又反过来取决于由个人请求编译的内容和Ruby能够编译的扩展所组成的代码库。

1.2.3 site_ruby(RbConfig::CONFIG[sitedir])vendor_ruby (RbConfig::CONFIG[vendordir])目录

在Ruby安装目录中包括一个名为site_ruby的子目录,它用于存储用户和系统管理员安装的第三方扩展和库文件。该目录中可能包括了用户所写的程序,还有一些从其他网站下载的工具包,以及Ruby库文件的存档。

site_ruby目录包含Ruby语言和C语言的不同子目录(是RbConfig::CONFIG中不同的两个项,分别为sitelibdir和sitearchdir),就这个意义而言,其与Ruby主安装目录并存在一个目录下。当用户请求一个扩展,Ruby解释器检查site_ruby下的子目录,也同时检查主rubylibdir目录和主archdir目录。

与site_ruby目录并存的是vendor_ruby目录。第三方的扩展都安装于此。vendor_ruby目录首次出现是在Ruby1.9中,从这两个目录中获得的包仍然在不断发展中。

1.2.4 gems目录

RubyGems实用工具是打包和发布Ruby库文件的标准方式。当用户安装gems(被称为包)时,未绑定的库文件则会放置于gems目录。这个目录没有在配置数据结构(RbConfig::CONFIG) 中列出,但是通常都和site_ruby在同一级目录。假如读者找到了site_ruby,便可以看看gems目录中还安装了什么。在1.4.5节中将会了解更多关于gems的知识。

在这一节中,学到了Ruby调用扩展的机制和语义,这也是编写和安装扩展时需要用到的知识。

1.3 Ruby扩展和编程库

本节的要点并不是关于Ruby标准库的参考。曾在引言中解释过,本书的目标不是编写一本Ruby语言的参考文档,而是教会读者使用Ruby语言并掌握它,并最终拓宽视野。

相应地,本节的目标是讲述扩展的工作方式,即如何使用Ruby运行这些扩展、它们之间技术实现的不同,并最终能让用户自己编写扩展和库文件的扩展架构。

随Ruby发布的扩展通常全部作为标准库来引用。标准库包括为不同项目和任务所提供的扩展,如数据库管理、网络、数学领域、XML处理等。标准库精密的结构每次改变,哪怕只有一点,也都要随着Ruby新版本而发布。使用最多、最广泛的库,已经证明了其存在的价值,所以通常都趋于稳定。

使用扩展和库的关键是require方法,与之密切相关的是load方法。这些方法让读者可以在运行时加载扩展,包括自己编写的扩展。我们将通过加载内置的扩展来学习它们并拓展我们的视野。

1.3.1 加载外部文件和扩展

可以手动把程序存储在单一文件中,但如果有成百上千行或者成千上万行的代码,这将是一种负担而不是优势。尽管如此,可以将程序分解为能运行的不同文件。在Ruby中使用requireload方法将会使得这一过程变得容易。这里先阐述load的使用,它是两个方法中设计得最简单的一个。

功能、扩展,还是库?


用户在运行时加载到程序中的程序代码有许多不同的名称。功能(feature)是最为抽象的,很少听到过有人说“请求一个功能”(requiring a feature,这里使用require)这样特殊的使用方式。库(library)是更为具体和通用的。它意味着存在一组真实的易于编程的代码,并能够被加载调用。扩展(extension)可以被任意可加载的附加库所引用,但是在Ruby中这通常意味着它们不是用Ruby而是用C语言编写而成的。如果要说正在编写的是Ruby扩展,就意味着已经假定那是用C语言编写的。


1.3.2 加载位于默认加载路径中的文件

Ruby解释器的加载路径是一系列目录,请求加载时,Ruby会在这些目录中搜索文件。可以使用$:(美元符号及冒号)检查加载目录下的这些子目录的名称,看到的结果取决于用户所使用的平台。在Mac OS X上,一个典型的加载目录如下(这个示例包含了.rvm的路径,它是Ruby版本管理器所决定的Ruby版本的路径):



在你的电脑中,“ruby-2.1.0”左边的部分可能会有所不同,如“/usr/local/lib/”,但是子目录的基本模式都是一样的。当加载一个文件时,Ruby将会自上而下地在每一个子目录中搜索。

注意

 当前的工作目录,通常使用一个点(.)表示,这不会包含在加载目录中。加载命令的作用如前面所介绍,这只是一个特例。

可以在load命令中使用代表上级目录的双点(..)符号导航到相对目录。当然,这比起使用加载目录或者相对路径来说兼容性会差一些,但是这可能会很有用处,尤其是如果拥有一个字符串变量,其中包含一段绝对路径并想要加载它的时候。

load命令总是会加载所请求的文件,不论这个文件是否已经加载过。假如一个文件在几次加载过程中发生改变,那么最新版本的文件将优先使用并覆盖之前加载的版本。尤其是在irb会话中,当在编辑器中修改一个文件时,想要立刻测试修改的效果,这将非常有用。

另一个加载文件的方法是require,它同样也搜索默认的加载路径中的目录。但是require有一些load不具有的特点。

1.3.3 请求功能

loadrequire最大的不同在于,require就算调用多次也不会重新加载已经加载过的文件。Ruby会持续追踪已经被请求的那些文件而不会重复加载它们。

require比起load来说更为抽象。严格来说是不能请求一个文件的,而只能请求一个功能。一般来说,做到这一点甚至不用指定文件的扩展名。为了验证之前所述,将loaddemo.rb中的这一行进行修改,从



修改为



当运行loaddemo.rb时,即使提供了需要加载的文件的完整文件名,得到的结果也可能与之前相同。

通过把loadee看作一个“功能”而不是一个文件,require对用Ruby编写的扩展和使用C语言写成的扩展都用一样的方式。另外,.rb扩展名的文件与其他扩展名为.so、.dll或者.bundle的文件使用方式也是一样的。

指定工作目录


require不能辨识出当前的工作目录(.)。用户可以显式地指定它,例如:


或者可以使用数组添加运算符(<<),把当前目录添加到加载路径当中。


这样,就不必在调用require的时候显式地指定工作目录了。


也可以给require指定完全限定的路径,和使用load一样,把文件或者功能加载进来。读者也可以混合使用该规则,例如,即使是将静态路径与路径末端更为抽象的语法功能混合使用,以下语法也是可以正常运行的,例如:



尽管load很有用,尤其是当加载多于一个文件的时候,但是require是一个日常使用的技术,用于请求Ruby扩展和库,不论是标准库还是第三方库。与加载loadee文件相比,请求标准库的功能要简单得多,只使用require就可以请求想要的任何库文件。之后,扩展中新的类和方法都可以使用了。下面的例子中,给出了请求前后在irb会话中的不同。



第一次调用scanf的时候失败并报错❶。但是在调用了require之后❷,没有编程人员的介入,"David Black"这个字符串对象也响应了scanf消息。(在这个例子中❸,使用空格为隐式分隔符,用于把原字符串抽取为两个连续的字符串)

1.3.4 require_relative指令

require_relative是第三种加载文件的方式。这个指令会搜索相对于所在文件的目录来加载功能。因此在前一个例子中,可以这样:



这样就可以不用把当前目录加入加载路径中。当需要导航到某个本地目录结构中的时候,require_relative是一个便捷的方式,例如:



接下来将对随Ruby发布的命令行工具进行一次测验,以此总结这一章所学到的知识。

1.4 易用的Ruby工具和应用程序

安装Ruby后,就可以得到一组重要的命令行工具,它们被安装在配置信息bindir所指定的文件夹中,通常是/usr/local/bin、/usr/bin或者/opt同等的目录中。(可以使用require "rbconfig"去测试一下RbConfig::CONFIG["bindir"]返回的结果。)这些命令行工具具体是以下几个。

  • ruby:解释器。

  • irb:Ruby交互式解释器。

  • rdocri:Ruby文档工具。

  • rake:Ruby的make工具,一套任务管理实用工具。

  • gem:一套Ruby库和应用程序包管理实用工具。

  • erb:一套模版系统。

  • testrb:一个用于测试框架的高级工具。

这一节将会讲述除erbtestrb之外的所有工具。它们在特定的条件下都很有用,但是不作为学习Ruby语言首要的目标和基础知识。

在这里读者可以不必记住本节的所有技术点,而是通读之后先留下感性认识。不久之后将会经常(尤其是一些命令行参数和ri实用工具)用到这些内容,随着使用频率的提高,对Ruby语言的理解会逐步加深。

1.4.1 解释器的命令行开关

从命令行启动Ruby解释器时,不仅可以提供程序的名称,也可以提供一个或多个命令行开关,正如在本章中所见那样。用户选择的开关将会通知解释器使用不同的特殊行为方式,抑或使用特殊的功能。

Ruby有超过20个命令行开关。其中一些用得很少,而另外一些对于Ruby程序员来说使用频率较高。表1-6概括了最常用的一些Ruby命令行开关。

表1-6 最常用的Ruby命令行开关

开  关

描  述

用  例

-c

不执行程序只检查程序文件的语法

ruby -c c2f.rb

-w

在程序执行过程中给出警告信息

ruby -w c2f.rb

-e

执行在命令行中引号内的代码

ruby -e 'puts "Code demo!"'

-l

行模式:在每一行输出后打印换行

ruby -le 'print "+ newline!"'

-rname

请求指定名字的扩展

ruby –rprofile

-v

显示Ruby版本信息,在详细模式(verbose mode)下执行程序

ruby –v

--version

显示Ruby版本信息

ruby –-version

-h

显示所有解释器开关的信息

ruby –h

接下来仔细看一看每一个开关的使用细节。

1.语法检查(-c

开关-c通知Ruby对一个或者多个文件的语法精确性进行代码检查,但不执行代码。它通常会和-w开关一起使用。

2.开启警告(-w

使用-w开关运行程序,解释器将会进入警告模式(warning mode)。这意味着用户将会看到比预期更多的警告输出,从而提示用户在程序中需要注意的地方,尽管这些不是语法错误,但都有文体上和逻辑上值得斟酌的地方。根据Ruby的设计:“你所编写的程序没有语法错误,但是它很怪异。你确定你所编写的程序吗?”甚至不用添加这个开关,Ruby也会给出确定的警告,只是相对而言要少于完全警告模式。

3.执行字符串脚本(-E

开关-e会通知解释器,命令行中的引号内包含了Ruby的代码,应该去执行这段真实的代码,而不是执行文件中的代码。这样可以很方便地用于快速执行脚本代码,而不用把代码写在文件中再去执行文件。


4.行模式运行(-L

开关-l将会产生一个效果:程序的每一次字符串输出都会被它自身的一行所替代,即使通常情况不是这样的。通常情况下,使用print打印输出不会自动以一个换行符结束,它与puts的使用不一样,但是使用了开关-l后,print也会以换行符结束。

下面使用温度转换器的程序作为例子来对比printputs的区别,用以确信程序不会在它们的输出中间插入额外的换行符(见1.1.5节)。可以使用-l开关逆向实现这个效果,这甚至将致使print打印输出的每一行都在它们自己所属的那一行中。下面展示了差别:


5.请求名称文件和扩展(-rname

开关-r用于调用require,并依赖其命令行参数,ruby -rscanf将会在运行开始时加载scanf。可以放置多个-r在单行的命令行参数中。

6.运行在详细模式之下(-v--verbose

使用-v运行程序将会执行两个任务:打印输出当前使用Ruby的版本信息,然后开启与-w开关相同的警告机制。最常使用-v的场景是找出Ruby的版本号:



本例中使用的是Ruby 2.1.0(补丁级别0),于2013年12月25日发布,并基于i686机器编译,运行在Mac OS X系统上。这里因为没有程序需要运行,所以Ruby立刻就打印输出了版本信息。

7.打印Ruby版本信息(--version

这个开关可以让Ruby打印一次版本信息,然后退出。就算指定了程序文件名,也不会执行任何的代码。可以回忆一下-v开关在详细模式下打印版本信息并执行代码的过程。可以说,-v开关是悄悄地完成了打印版本和运行代码的模式,而-- version仅仅输出了版本  信息。

8.打印帮助信息(-h--help

这两个开关(-h--help)将会打印输出一个包含全部可用的命令行开关的列表,并概述它们的功能。

除了可以单独使用这些开关,也可以在Ruby调用的时候合并两个或者多个一起使用。

9.合并使用开关(-cw

之前已经介绍过-cw这样的合并使用方式了,它会检查文件的语法错误而不执行文件,当然也会给出警告:



另一个很常用的合并使用的例子是-v-e,这将显示运行的Ruby的版本,同时运行单引号内的代码内容。有很多关于Ruby合并开关使用的讨论,如邮件列表和其他地方,人们使用它来验证相同的代码在不同版本之间的差异。例如,如果想要清晰地显示一个字符串调用方法start_with?在Ruby 1.8.6中不能运行而在Ruby 2.0中可以,那么可以分别运行这两个版本:



(当然,这要求系统中安装了两个版本)第一次运行(使用1.8.6版本)程序获得了undefined method 'start_with?'消息❶,这意味着执行了一个不存在的操作。但是使用Ruby2.1.0运行相同的代码段时,运行就正常了❷,Ruby打印输出了true。这是一个非常便捷的方式去分享和提出关于Ruby版本切换时的问题。

现在,将讲述前面所提到的交互式Ruby解释器irb,并进一步地了解它。这一部分在本章的一开头已经有过叙述,所以读者可能已经有所了解。如果没有,那么借此机会可以学习一下这个特殊且有用的Ruby工具。


1.4.2 走近Ruby的交互式解释器irb

正如本书中所见,irb是一个交互式的Ruby解释器,这意味着它不用处理文件,而是处理会话中所输入的代码。irb是一个很好的工具,可以用于测试Ruby代码,同时也是一个学习Ruby的好工具。

在命令行中输入irb,就可以开启irb会话,irb将显示以下提示符:



正如之前所述,还可以使用--simple-prompt选项使irb输出更为简单明了:



irb一旦启动,便可以输入Ruby命令。这里可以运行单次摄氏-华氏度转换程序。在本例中,irb就像一个可装入口袋的计算器:它能计算任何输入并打印结果。因此不必使用print或者puts命令:



如果想要知道一年有多少分钟(假如读者手边没有Rent乐队热门金曲的CD[4]),就输入适当的乘法计算表达式:



当然,irb也会处理输入的Ruby指令。比如,将天数、小时数、分钟数赋值给变量,然后将这些变量的值相乘,可以在irb中这样做:



上面就是期望的计算结果。但是看一下上面条目的前3行,当输入days = 365的时候,irb会相应输出365,为什么?

表达式days = 365是一个赋值表达式:将值365赋给了名为days的变量。赋值表达式最重要的任务就是将值赋给变量,因此可以在之后使用该变量。但是赋值表达式(days = 365这一整行)也有一个值,赋值表达式的值就是它右边的值。当irb发现任何表达式时,它就会输出这个表达式的值。因此,当irb发现days = 365时,它就输出365。这看起来有些输出过度,这是因为其处在irb之中,与在irb中输入2+2就看到了结果,而没有显式地使用print语句去打印结果是同样的道理。

同样,调用puts方法也有一个返回值,名为nil。假如在irb中输入一个puts语句,irb将会严格地执行它,同时还会打印输出puts的返回值。



这里有一种方法可以让irb不再喋喋不休,即使用-noecho参数。下面展示它如何使用:



幸好有-noecho,附加的表达式不会回显其结果了。puts命令确实被执行了(可以看到输出"Hi"),但是puts的返回值(nil)被屏蔽了。

一旦掌握了使用irb的窍门并用于打印输出一切的值,以及如何关闭它,就会发现irb是一个极为有用的工具(和玩具)。

Ruby的源代码采用一种能自动生成文档的方式进行标记,然后通过一些工具解析和显示它们,这些工具是ri和RDoc,下面就会讲述到。

1.4.3 ri和RDoc

最早由Dave Thomas编写的ri(Ruby索引)和RDoc(Ruby文档)是为Ruby程序提供文档的关系很紧密的一对工具。ri是一个命令行工具,而RDoc体系中包含了命令行工具rdocrirdoc是独立的程序,通过命令行来运行。(虽然用户也可以使用Ruby程序内所提供的工具,但这里暂时不讨论这方面的内容。)

RDoc是一个文档系统。假如在程序文件(Ruby或者C)中使用规定的RDoc格式编写了注释,rdoc会扫描程序文件,并抽取这些注释,智能地(通过注释的内容进行索引)组织它们,最后创建出漂亮的格式化文档。在Ruby源码树以及许多Ruby安装目录的Ruby文件中,在许多的源码文件中可以看到RDoc标记,包括了Ruby文件和C语言文件。

ri与RDoc相互配合:它提供了一种方式用于查看RDoc萃取过和组织过的文档信息。具体来说(虽然不是唯一的,除非用户定制它),ri被用于组织和显示从Ruby源文件而来的RDoc的信息。在完全安装好Ruby的任何系统中,都可以使用一个简单的命令行工具ri获得关于Ruby语言的详细信息。

Stringupcase中间的散列标记(#),用于在ri命令中表明查找的实例方法,并与类方法区分。要查询类方法,可以使用分隔符::替换#。第3章将会讲述实例方法和类方法的区分。这里的要点是通过命令去处理大量的文档。

注意

下一节将会讲述Ruby命令行工具中的rake

1.4.4 任务管理实用工具:rake

正如其名字所代表的含义(由“Ruby make”而来),rake是一个极具make特性的任务管理实用工具。它是由已故的Jim Weirich编写完成的。同make一样,rake读取和执行定义在一个文件中的任务,这个文件名为Rakefile。和make不同,rake必须使用Ruby语法定义任务。

代码清单1-5显示了一个Rakefile。假如列表中的内容保存到一个称为Rakefile的文件,就能够在命令行中运行它:



rake执行了admin命名空间中的clean_tmp任务。

代码清单1-5 Rakefile在admin命名空间中定义了clean_tmp任务



这里,rake定义任务的时候用到了许多本书中还没出现的Ruby技术,但其基本逻辑是非常简单的。

(1)循环进入/tmp目录下的每一个目录条目❶。

(2)除非当前条目是一个文件,否则跳出当前的循环。提示一下,隐藏文件也不会被删除,因为列出目录的操作不会包括它们❷。

(3)提示删除文件❸。

(4)假如用户输入y(或者任何以y开头的字符串),则删除文件❹。

(5)假如用户输入q,中断循环,任务结束❺。

主要的编程逻辑是这样组成的:通过循环目录列表中的条目(参见“使用each遍历一个集合类型”),然后通过case语句进行条件判断并执行(这些技术将会在第6章中讲述)。

使用each遍历一个集合类型

上面任务定义中的命令desc提供了对任务描述的方式。这不仅在用户研究文件的时候可以派上用场,还可以用于在特定时间查看rake可以执行的所有任务。假如所处的目录中包含一个代码清单1-5中的Rakefile,便可以使用如下命令:



可以看到所有定义过的任务列表。



对于rake的命名空间和任务名称可以任意命名。甚至可以不需要一个命名空间,可以在顶级命名空间定义一个任务,如下所示:



然后使用简单的名称调用它:



但是,在任务中使用命名空间是个好主意,尤其是在用户定义的任务数量成倍增长的时候。可以定义命名空间至任意的深度,例如,下面的结构是合法的:



定义好的任务是这样调用的:



正如目录清理的例子所示,rake任务不会被限制在与Ruby编程相关的操作中。使用rake,用户可以随意使用Ruby语言以达到编写任何所需任务的目的。

下一个将要讲述的是gem命令,它使得安装第三方Ruby组件包变得非常容易。

1.4.5 使用gem命令安装组件包

RubyGems库和实用工具集合包括了打包、安装Ruby库和应用程序的工具。这里不会覆盖创建gem的内容,只是先看一下gem安装程序和使用的方法。

使用gem安装一个Ruby的程序包通常是很简单的,在gem之后使用简单的install命令即可



这个命令将会输出如下内容(这依赖于已经安装的gem和新安装的gem的依赖包):



这些状态报告之后跟随着许多行信息,它展示了对于已经安装好的不同gem的ri和RDoc文档信息。(文档的安装是通过RDoc与正在处理的gem源文件关联,所以要耐心等待这个过程,它通常是gem安装中最长的过程。)

在gem安装的过程中,gem从rubygem.org(www.rubygems.org)下载所需的gem文件。这些文件都是使用.gem格式,被保存在用户gems目录下的缓存子目录中。当然也可以安装存放在本地磁盘或者其他媒介中的gem文件。


1.5 小结

在本章中,看到了许多重要的Ruby语言基础知识,包括:

  • Ruby(编程语言)和ruby(Ruby解释器)的不同;

  • Ruby变量的命名规范(稍后还会讨论并深入学习);

  • 基本Ruby运算符和内置结构;

  • 编写、存储、运行一个Ruby程序文件;

  • 键盘输入和屏幕输出;

  • 使用requireload操作Ruby库;

  • 解析Ruby安装目录;

  • 随Ruby发布的命令行工具。

读者现在拥有了一份关于Ruby工作原理和Ruby编程环境所需工具的蓝图。它包含了之前看到和练习过的重要Ruby技术。接下来,本书准备开始系统地介绍Ruby。



这是一本深受好评的书。它不仅是一本纯Ruby的书,也不仅是一本纯Rails的书,而是一本为Rails程序员“优化”过的Ruby书。

本书从Ruby编程语言的基础开始一直讲到动态特性,其中包含大量的真实代码示例并附有详细的注解,对日常使用Ruby进行编程中会遇到的每个知识点都进行了清晰的讲解。本书的内容由浅入深,主要包含Ruby编程语言的语法、面向对象的特性、默认对象self、控制流技术、常用的内置类、正则表达式、I/O操作,最后用大量的篇幅讲述了Ruby中最值得关注的动态特性。

【预售+样章试读入口】http://www.epubit.com.cn/book/details/1850


本文转载自异步社区

文链接:https://www.epubit.com/articleDetails?id=NC7E3EF91AA300001B6941D5915E01D20


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。