Ruby程序设计语言教程(中文版)


Rubylearning 教程-中文版 作者:Satish Talim 来自:http://rubylearning.com 译者:想飞的马 来自:http://mayabin.javaeye.com 版本号:Draft-20070926(完成至“Ranges”) 这是一份来自 http://rubylearning.com 的 ruby 教程,该教程即包含 ruby 的基本语法,又包 含高级应用,还不乏例子程序和项目实战。如果你想从头开始学 ruby,可以跟着教程,由 浅入深的进行。 这份教程的作者 Satish Talim 致力于 ruby 的教学及推广工作,他非常乐意把这份教程翻译成 中文。目前,这份教程已有意大利语,西班牙语,法语和中文正在翻译中。作者不收取任何 费用,只要保留他的名称以及网站地址即可。非常可敬。 同样,这份中文版,也可以免费下载和传播,但是必须要注明出处,保留作者和译者的署名 权。 由于水平有限,翻译中难免有错误和遗漏,欢迎指正。英文足够好的朋友,最好直接阅读英 文版本。 第一部分: 核心 Ruby 这一部分主要讲 ruby 的基本语法及一些简单的例子,不包含高级应用。 第一章 介绍 Ruby Study Notes 是一个一步一步教你如何用 ruby 编程的教程,主要介绍 ruby 的基本语法。 你可以边读边跟着做里面的例子,这些例子也都非常简单。 http://rubylearning.com 网站有这份教程的英文原版,如果英文足够好的话,最好直接读英文, 以免翻译遗漏。 在这份教程中,会使用带背景的文字做一些特殊说明或者解释,比如下面这样: 这是一个解释说明,可以略过不看 这份教程里的代码样例如下图所示: def hello puts 'hello' end 译者注: 蓝色的部分呢,代表是译者的补充 但是请记住 ruby 是一种语法灵活的语言,解决同一个问题可以有好多种途径。 第二章 安装 什么是 ruby? Ruby 是一种跨平台,面向对象的解释型语言。他是本着简单实用的原则设计出来的,他的 创始人 Matz 说,“我想用最省力的方法来写程序,这是我设计 ruby 的初衷。在 ruby 被广泛 使用后,大家也是这么认为的”。工具都是“懒人”发明的,这句话真对。 2004 年,随着 David Heinemeier Hansson 对 Ruby on Rails Web 应用框架的介绍,ruby 开始 火起来了。 既然说他简单实用,Ruby 能帮着做哪些事情? 在 David Black 的《Ruby for Rails》一书中,他总结了,对于一个 Rails 开发者来说,主要有 四点帮助: 1. By helping you know what the code in your application (including Rails boilerplate code) is doing 2. By helping you do more in, and with, your Rails applications than you can if you limit yourself to the readily available Rails idioms and techniques (as powerful as those are) 3. By allowing you to familiarize yourself with the Rails source code, which in turn enables you to participate in discussions about Rails and perhaps even submit bug reports and code patches 4. By giving you a powerful tool for administrative and organization tasks (for example, legacy code conversion) connected with your application 下载 ruby 和编辑器 Ruby 是一个开源的编程语言,提供了多种版本,可以运行在多种操作系统和平台上。你可 以把在一种平台下编写的代码直接移植到另一种平台下。 不过最简单的就是在Windows下的安装了,下载安装包,双击打开。安装程序除了会安装必 须的文件外,还会自动设置环境变量,还安装了文档和SciTE文本编辑器。 日本人Yukihiro Matsumoto,也就是Matz,1993 年创建ruby。有关ruby的历史可以见介绍An interview with him in 2001 SciTE 是一个类似于 Emacs 的文本编辑器。 Ruby 的发行版中,子版本号是偶数的是稳定版,例如,1.6,1.8 等等。 使用eclipse的朋友,可以下载RDT插件,非常不错。 其他平台的下载和安装,可以分别参考这里,这里,这里。 我下载安装了linux版本,源代码版本,需要编译安装。安装方法和标准方法一样。 1. tar xvzf ruby-xxx.tar.gz 2. ./configure --prefix=$HOME 3. make 4. make install 安装完后,可以使用 ruby –v 测试版本号。 Ruby 安装目录介绍 以 windows 下为例,假设安装在 c:/ruby 下 c:/ruby/bin 可执行文件 c:/ruby/lib/ruby/1.8 ruby 库文件 c:/ruby/lib/ruby/1.8/i386-mswin32 和平台相关的库文件,以 dll 或 so 形式存储 c:/ruby/lib/ruby/site_ruby 自己写的代码或者第三方库目录 c:/ruby/lib/ruby/gems Ruby-Gems c:/ruby/src ruby源文件 c:/ruby/samples/RubySrc-1.8.6/sample 例子程序 第三章 第一个 Ruby 程序 rubylearning 的作者推崇使用 SciTE 来编写程序,下面就介绍一下 SciTE 的使用。 首先要配置一下: Options/Open Global Options,找到'tabsize',设置 tabsize=2 和 indent.size=2。 设置 position.width=-1 和 position.height=-1,这样能是窗口全屏。保存。 Ctrl+Shift+I 打开 Indentation Settings 窗口,设置 tabsize=2 和 indent.size=2。 好了,就配置这些,开始写程序。 创建一个文件夹,比如 d:/rubyprogram 用来存放所有 ruby 程序。 打开 SciTE,编写如下代码: 选择 File/Save As...,记住,所有的 ruby 源文件都以.rb 作为扩展名,文件名和目录名要小写, 对应 ruby 中的类和模块。写好程序后,按 F5 运行。 ruby 程序的执行是从上到下一行行按顺序执行的。puts 标准输出语句,已经包含在 ruby 核 心中,他能够自动换行,类似于 java 中的 system.out.println()。 puts 'hello' a.圆括号在方法调用中是可选的,下面的写法都对。 foobar foobar() foobar(a, b, c) foobar a, b, c b.在 ruby 中,不管是整数,字符串,还是对象,都看作是对象。并且每一个对象都已经内置 了一系列方法。 使用时,在对象名后加“.”,然后跟方法名。有些方法比如 puts,gets 可以直接使用,前面 不用带对象名。 这些核心方法都内置在 ruby 核心模块中,可以被任何 ruby 对象调用。 当你运行一个程序时,一个叫 main 的 class 对象被自动创建,由它作为程序入口,当然这个 对象可以调用核心方法。 下面的程序可以验证上面的论述(不要害怕现在看不懂程序)。 puts 'I am in class = ' + self.class.to_s puts 'I am an object = ' + self.to_s print 'The object methods are = ' puts self.private_methods.sort 从上面程序,发现些什么? 1.对于 c 和 java 程序员来说,不用写 main()方法了。 2.字符串就是用单括号或双括号扩起来的连续字符,单括号比双括号要高效(后面会提到)。 3.ruby 是解释型语言,所以修改程序后,不需要重新编译。 4.ruby 版本号中,子版本号是偶数的代表稳定版本,比如 1.6,1.8。 5.ruby 规范声明,文件/目录的名称要小写,并且要和类/模块的名称对应,类文件要以.rb 作 为扩展名。 熟悉 eclipse 的程序员可以使用 RDT 插件,使用习惯和你编写 java 代码时是一样的,具体有 多像,使用一下就有体会了。 第四章 特性 ruby 的一些特性如下: 1.格式灵活。 2.大小写敏感。 3.单行注释以“#”开头,多行注释包括在“=begin”和“=end”中。 4.分行符:多行语句写在同一行时,必须用分号隔开,行末尾可以不写,换行符相当于分号。 单行语句要书写到多行时,要使用“\”作为结尾。 5.关键字。ruby 中有 38 个保留关键字,和其他语言一样,不能把他们作为变量,类或者方 法名。在有的语言中,0,null,空字符串可能被当作 false,但 是 在 ruby 中,他们被当作 true, 除了保留关键字“false”和“nil”外,其他都是 true。 文档:全部ruby在线文档,可以访问这儿。Ruby Cheat Sheet在这儿。Windows下的安装指导 可以看fxri - Interactive Ruby Help & Console。 第五章 ruby 中的数值类型 让我们看看 ruby 中的数值类型。在 ruby 中,不带小数点的叫整数,带小数点的叫浮点数或 实数。 看下面的程序 p002rubynumbers.rb。 =begin Ruby Numbers Usual operators: + addition - subtraction * multiplication / division =end puts 1 + 2 puts 2 * 3 # Integer division # When you do arithmetic with integers, you'll get integer answers puts 3 / 2 puts 10 - 11 puts 1.5 / 2.6 Fixnum 或者 Bignum 类的对象都是整数,他们的区别只是位长不同,他们继承自 Integer---Numeric。 浮点数是 Float 类的对象,包括了系统中的 float 和 double 类型。(后面章节会详述)。 类的继承关系见下图:(转载自: Donald Craig) 下面是 Peter Cooper 的《Beginning Ruby》书中的例子,(不要怕看不懂)。 计算 2 的 64 次方,可以得到一个用兆来衡量的数。 这个例子说明 ruby 可以处理很大数量级的数,不像有的语言有位数的限制。 Fixnum 类处理稍小数量级的数,Bignum 处理大数量级的数。ruby 会自动处理 Fixnum 和 Bignum,你只要关注运算就可以了,不用担心位数。 但是有时候运算结果依赖于你的系统架构,但是 ruby 依然会替你自动调整。 运算符和优先级 下图是按优先级从高到低排列的运算符,转自Hal Fulton - The Ruby Way。 rice_on_square = 1 64.times do |square| puts "On square #{square + 1} are #{rice_on_square} grain(s)" rice_on_square *= 2 end a.ruby 中没有++和--运算符 b.括号可以和其他运算符一起使用,括在括号中的表达式优先运算。 ruby 中的求模运算如下, puts (5 % 3) # prints 2 puts (-5 % 3) # prints 1 puts (5 % -3) # prints -1 puts (-5 % -3) # prints -2 作业:写一个程序计算一年有多少分钟(不考虑闰年)。 第六章 字符串类型 字符串是用单括号或者双括号括起来的字符组合。‘’中间没有任何字符,叫做空字符串。 程序 p003rubystrings.rb 是字符串的例子: =begin Ruby Strings In Ruby, strings are mutable =end puts "Hello World" # Can use " or ' for Strings, but ' is more efficient puts 'Hello World' # String concatenation puts 'I like' + ' Ruby' # Escape sequence puts 'It\'s my Ruby' # New here, displays the string three times puts 'Hello' * 3 # Defining a constant # More on Constants later, here # http://rubylearning.com/satishtalim/ruby_names.html PI = 3.1416 puts PI 在 ruby 中,字符串是变长的。它会根据需要扩展长度,这样做并不会耗费太多的时间和内 存。ruby 是按字节存储字符串的。 还有一种特殊的字符串,用``括起来。 puts `dir` 括起来的字符串会作为系统命令发送给操作系统。比如上面的‘dir’在 windows 下会执行 列目录的动作,并把结果返回给 ruby 程序。 第七章 变量和定义变量 为了把数值或字符串保存在内存中供后面程序使用,需要给他们命名。 程序员把这个过程叫定义变量,定义的名称叫变量。 只有当解释器看到有变量定义后,这个变量才会产生,也就是说,不会给变量预先分配地址 和空间。 s = 'Hello World!' x = 10 # p004stringusage.rb # Defining a constant PI = 3.1416 puts PI # Defining a local variable myString = 'I love my city, Pune' puts myString =begin Conversions .to_i, .to_f, .to_s =end var1 = 5; var2 = '2' puts var1 + var2.to_i # << appending to a string a = 'hello ' a<<'world. I love this world...' puts a =begin << marks the start of the string literal and is followed by a delimiter of your choice. The string literal then starts from the next new line and finishes when the delimiter is repeated again on a line on its own. =end a = < ,意思是你不能 把一个整数加到字符串中,或者不能用一个字符串乘另一个字符串。 24. 常量以大写字母开头。 25. 变量不会预留地址和空间,只有读到变量定义后才会定义变量。给变量初始化为 nil 是 一个好习惯。 26. x, y = y, x 会得到 y 的值,y 会得到 nil;变量和值的数目要对应。值的数目少了,用 nil 补;如果多了,丢弃。 27. 本地变量要以小写字母或者下划线开头,包含并且只能包含字母,数字和下划线。 28. .to_i, .to_f, .to_s 用来转换成 integer, float, string。 29. 操作符 << 用来 append 字符串。 第八章 作用域 作用域指的是变量的生命空间或者说作用范围 。不同类型的变量有不同的作用域。本章主 要讲两种,全局变量(Global)和局部变量(Local)。 全局作用域和全局变量 全局作用域指的是能够覆盖整个应用程序运行期(从应用程序开始运行到结束)。全局变量 的生命周期是全局作用域。 全局变量以一个美元符号($)开头,在整个应用程序运行期,任何地方都可以使用全局变 量。但是稍微有经验的人都知道,应该尽量少的使用全局变量,以减少空间的占用。 内建的全局变量 ruby 解释器在开始运行的时候初始化了一部分全局变量,在应用程序的任何地方都可以调 用这些内建全局变量。 比如‘$0’代表的是所运行应用程序的文件名称;‘$:’代表的是默认的文件搜索路径;'$$' 代表的是 ruby 程序的进程 id。 可以查看文档来了解更多的内建全局变量。 本地作用域 本地作用域存在于下面几种情况中: 1.最顶级的程序。---我的理解是 main 程序。 2.类和模块。 3.方法。 本地作用域的介绍有些抽象,可以在后面使用过程中慢慢体会。 第九章 输入方法 前面我们已经学到了使用 puts 命令把字符串输出,那么输入命令又是什么呢? 使用 gets 和 chomp 方法,下面是他们的例子程序: 运行上面的程序,需要你输入城市名称,然后回车,就会显示你输入的城市名称。 STDOUT 是一个全局常量,代表的是标准输出流。flush 方法的作用是把所有 io 里面缓冲的 数据输送给 STDOUT。 gets 方法得到输入字符串(包含一个\n 回车符),chomp 方法把字符串中的\n 回车符去掉。 # gets and chomp puts "In which city do you stay?" STDOUT.flush city = gets.chomp puts "The city is " + city 在 RAILS 中,数据来源有很多。 对于一个 Rails 开发者来说,典型的来源是数据库,Rails 会为你抓取数据。 如果你是一个用户,当你从键盘输入时,Web form 会接收你的输入。所以你很少会用到这 些技巧。 作业:输入一个华氏温度度数,转化成摄氏度输出(要求保留 2 位小数)。 Tip: 保留 2 位小数,一个方法是使用内置的 format 方法,比如 x = 45.5678,使用 format("%.2f", x) 后,会得到 45.57。另一个方法是使用 round 方法,puts (x*100).round/100.0 第十章 命名规则 ruby 中需要给常量,变量,方法,类,模块命名,保留的关键字不能作为这些名称使用。 ruby 可以根据名称的第一个字符来区分是什么名称。 名称可以是大小写字母,数字,下划线的任意组合(这些字符是合法字符),但是只能以字 母或下划线开头。 变量命名规则: ruby 中的变量可以包含任何类型的数据。使用变量时不需要类型声明,变量名决定了它的 作用域。 本地变量(在对象中定义)以小写字母或者下划线开头,后面跟合法字符。 实例变量以‘@’开头,后面至少跟一个合法字符。 在类中定义的变量,以‘@@’,两个‘@’开头,后面至少跟一个合法字符。 全局变量以‘$’开头,后面跟合法字符,也可以使用‘$-’开头,后跟一个字母(是字母)。 常量命名规则: 常量以大写字母开头,后跟合法字符。类和模块的命名规则和常量一样。 方法命名规则: 方法名以小写字母开头,后跟合法字符。但是在方法名的结尾,可以跟‘?’或‘!’(!或 者 bang 代表这是一个比较危险的方法,说危险是相对和它重名,但是不带‘!’的方法。后 面会有专门的章节介绍‘Bang Method’)。 Ruby 规范说明如果一个变量或者方法名由多个词组成,需要用下划线隔开。如果是类,模 块或者常量名称由多个词组成时,不需要用下划线,只要把后续词的第一个字母大写就可以。 需要注意的是,一个变量可以是任何类型对象的引用,而且引用是可以动态变换的。常量也 是对象的引用,可以在类和模块中定义常量,但是在方法中不可以。 Ruby 允许修改常量的值,但是会给一个警告信息。 变量只是对象的引用,引用失效后,ruby 会自动做垃圾收集。 下面的例子可以说明 ruby 程序的动态类型特性: Ruby 中的基本类型包括 Numeric (子类型包括 Fixnum, Integer, and Float), String, Array, Hash, Object, Symbol, Range, and RegEx。在 ruby 中,不需要维护对象的类型,如果它是整数,那 就是一个整型类型;如果是字符串,就是字符串类型。Class 方法用来得到对象的类型。 # Ruby is dynamic x = 7 # integer x = "house" # string x = 7.5 # real puts x # In Ruby, everything you manipulate is an object 'I love Ruby'.length s = 'hello' s.class # String 在 Ruby 中,需要处理的都是对象,并且返回的也是对象。 5.times { puts "Mice!\n" } # more on blocks later "Elephants Like Peanuts".length 第十一章 创建方法 如果说对象是名词的话,那么方法就是动词,代表了一个对象要执行什么动作。 方法是和对象相对应的,“.”前面的对象就是方法所属的对象。 但是有时候可以省略对象名,比如 puts,gets 是直接使用的,前面没有对象名。 在对象内部调用对象自己的方法时,是可以省略对象名的。 但是有时候我们不知道在哪个对象里(当前对象),可以使用 self 方法查看。 注意:ruby 会自动生成一个 main 对象。 更多self细节,查看这儿。 下面我们学着自己写一个方法。方法体需要包括在 def 和 end 中间,参数列表需要括在括号 里。 puts self 注意:方法中并没有定义返回类型,一个方法的返回值就是该方法的最后一行。 多个方法定义之间最好用空行隔开,方便区分。 ruby 规范说明,方法的参数需要用括号括起来。 但是看以前的例子,比如 puts,p,gets 等方法的参数都没有括起来。以后会讲到参数不需 要括起来的情况。 def hello puts 'Hello' end #use the method hello # Method with an argument - 1 def hello1(name) puts 'Hello ' + name return 'success' end puts(hello1('satish')) # Method with an argument - 2 def hello2 name2 puts 'Hello ' + name2 return 'success' end puts(hello2 'talim') 这段代码在我机器上的运行结果如下: 可以看到,参数没有括在括号中,一样输出了结果,但是给出了警告。 定义方法时,可以给参数定义默认值。如果调用方法时,没有传参数值过来,方法会使用这 些默认值。看下面的例子: 在我的机器上,输出结果如下: 在上面的例子中,有‘#{...}’这样的代码,它的作用是,计算{}中表达式的结果,并把结 果转换成字符串。运行这样的代码后,可以得到结果字符串,并不会看到’#{}‘在输出结 果中。 再比如下面的例子: 输出结果为: 在 ruby 中可以给一个方法指定别名。别名对应的是原方法的一个拷贝,如果原方法改变了, 别名不会跟着变化,看例子: Hello test2.rb:32 warning: parenthesize argument(s) for future version Hello satish success Hello talim success # interpolation refers to the process of inserting the result of an # expression into a string literal # the interpolation operator #{...} gets calculated separately def mtd(arg1="Dibya", arg2="Shashank", arg3="Shashank") "#{arg1}, #{arg2}, #{arg3}." end puts mtd puts mtd("ruby") Dibya, Shashank, Shashank ruby, Shashank, Shashank puts "100 * 5 = #{100 * 5}" 100 * 5 = 500 def oldmtd "old method" end alias newmtd oldmtd def oldmtd "old improved method" end puts oldmtd puts newmtd 运行结果为(别名并没有变化): ruby 支持可变数量参数的用法吗?答案是肯定的。 '*'会接受传过来的所有参数,并且把他们放到数组 my_string 中。 do end是程序块。 通过使用‘*’,即可以传递多个参数,也可以一个参数也不传。 上面例子的结果如下: 如果你想包含多个可选参数的话,一定要把可选参数放在最后,比如 那么一共可以给方法传递多少参数?数目是没有限制的,这个问题,可以参考这篇文档。 参数的排列顺序是从左至右排列的,比如: 那么传过去的参数是值传递还是引用传递?请参考这儿。再看下面的例子: 结果是: "hello"。可以看出是按引用传递,但是这个问题一直在讨论中。 old improved method old method # variable number of parameters example # The asterisk is actually taking all arguments you send to the method # and assigning them to an array named my_string as shown below # The do end is a Ruby block which we talk in length later def foo(*my_string) my_string.each do |words| puts words end end foo('hello','world') foo() hello world def opt_args(a,b,*x) # right def opt_args(a,*x,b) # wrong def mtd(a=99, b=a+1) [a,b] end puts mtd def downer(string) string.downcase! end a = "HELLO" # -> "HELLO" downer(a) # -> "hello" puts a 重磅方法:(bang or ! methods) Ruby 中,方法名后跟一个‘!’的方法叫重磅方法。带上‘!’标志,就说明这个方法是比 较危险的,当然这个危险是相对于同名不带‘!’的方法而言的。 你会经常发现这样成对的方法,有相同的方法名,但是一个带‘!’标志,一个不带。执行 不带‘!’的方法后,会返回一个全新的对象。但是如果带‘!’的方法,返回的是原来的对 象。这就是他们的区别。 看几个这样的例子,数组的 sort/sort!方法,字符串的 upcase/upcase!,chomp/chomp!方法, 字符串和数组的 reverse/reverse!方法。 方法这一章涉及到的知识点比较多,从方法定义,别名,参数的类型和个数,以及重磅。 幸好,每个知识点后面都有例子。运行这些例子,分析一下结果,对理解这些知识点,还是 很有帮助的。 总结二 1. 尽量少使用全局变量,因为全局变量能在整个应用程序运行期生存,占用资源。全局变 量以‘$’开头。Ruby 解释器在开始运行时,能自动初始化一定数目的内建全局变量。 2. gets 和 chomp 方法用来接收和处理用户输入。 3. gets 方法返回用户输入的字符串,并加上一个换行符, chomp 方法删除字符串中的换行符。 4. STDOUT 是一个全局常量,代表的是标准输出流。flush 方法能把缓存在 io 中的数据释放 出来。 5. 内置的 format 方法能够格式化数据,比如 format("%.2f",x) 。 6. 在 Ruby 中,可以给常量,变量,方法,类,模块命名,并且可以通过名称的第一个字符 确定是什么名称。 7. 小写字母指的是从‘a’到‘z’,下划线是‘_’,大写字母指的是从‘A’到‘Z’,数字 指的是从‘0’到‘9’,这些都是合法字符。 8. 名称以小写字母或者大写字母或者下划线开头,后面可以跟大小写字母,数字以及下划 线的任意组合。 9. 在 ruby 程序中,不需要给变量做任何声明(类型,作用域)。通过变量名可以区分出变 量的作用域。 10. 谨记本地变量,实例变量,类和模块,全局变量,常量,以及方法的命名规则。 11. "?" 和"!" 是唯一的可以跟在方法名后面的不合法字符。 12. ruby 规范说明,如果方法或变量名由多个词组成,词和词之间用“_”隔开。如果是类, 模块或者常量,不使用‘_’,只需要把单词的首字母大写就可以了。比如,my_variable, MyModule, MyClass, MyConstant. 13. 变量可以引用任何类型的对象,并且同一个变量可以在不同时刻引用不同类型的对象。 14. ruby 中的变量都是对象的引用,变量生效后,ruby 会自动做垃圾收集。 15. ruby 是动态类型的语言,你所操作的都是对象,返回的结果也是对象。 16. ruby 中的基本类型有 Numeric(包括 Fixnum, Integer 和 Float 子类型), String, Array, Hash, Object, Symbol, Range 和 RegEx。 17. 通过 self 方法可以查看在哪个对象中。 18. 使用 def 和 end 来声明方法。参数是扩在括号里的本地变量。 19. 方法不需要声明返回类型,返回的是最后一行的值。 20. 最好在不同的方法声明间加一行空行。 21. ruby 规范说明要把参数扩在括号中。 22. 方法名后面唯一可以跟的非合法字符就是'!'和'?'。 23. 以'!'结尾的方法总是返回对象本身,而不是一个拷贝。 24. 可以使用'='给参数指定默认值,如果调用方法时,没有传参数值,会使用默认值。 25. #{...}返回的是括号中的表达式的结果。 26. 方法的别名能给方法生成一个拷贝,所以即使原方法发生变化,别名不会跟着变化。 27. 方法的参数个数可以是变化的。 28. 方法的参数个数没有限制。 29. 参数的排列顺序是从左到右。 30. 方法是按值传递还是引用传递,一直还在讨论中,等待结果吧。 第十二章 method_missing 方法 当调用一个对象的方法时,会根据方法名查找是否有这个方法存在。如果不存在这个方法, 会抛出一个 NoMethodError 异常。 但是可以定义一个 method_missing 方法,来处理找不到方法这种情况,并且可以传递参数 给这个方法。 method_missing提供的是一个友好的处理办法,它拦截这种发生了错误并且没有返回结果的 信息(我们马上就要讲到自定义类)。看下面的例子: 输出结果是: class Dummy def method_missing(m, *args) puts "There's no method called #{m} here -- please try again." end end Dummy.new.anything >ruby tmp.rb There's no method called anything here -- please try again. >Exit code: 0 第十三章 更多字符串方法 有很多方法可以用于字符串对象。比如 reverse 方法得到一个反向排列的字符串(原来的字 符串不会改变)。length 方法得到字符串的长度。upcase 方法把字符串中的所有字母转换成 大写。downcase 方法把字符串中的所有字母转换成小写。swapcase 方法也是转化字符串中 字母的大小写,但是它会把大写字母换成小写,小写字母换成大写。capitalize 方法把字符 串的第一个字母换成大写,其他都换成小写。slice 方法返回字符串的一个子字符串。 upcase, downcase, swapcase 和 capitalize 方法返回的都是原字符串的一个拷贝。但是 upcase!, downcase!, swapcase!和 capitalize!这些重磅方法,都是直接修改并返回原对象。如果你确认 没有必要保留原字符串,可以使用它们。 前面我们提到过字符串是括在单引号或者双引号中间的字符序列,当时我们还提到双引号和 单引号是不同的。那么他们的区别是什么? 他们的主要区别就是构造字符串的过程。使用单引号时很简单,几乎不做什么检查,就构造 完成。但是使用双引号就要复杂一些。首先它会检查字符串中是否有转义字符(比如'\n','\r'), 如果有,它会把转义字符转换成特定的二进制值。然后它还会检查是否有#{expression}存在, 如果有,它会先计算表达式,并返回一个字符串。 在下面的例子中,第二个方法去掉了临时变量 result 和 return 语句,因为 ruby 方法可以自动 返回最后一行的值,所以去掉他们不会对程序有影响。 def say_goodnight(name) result = "Good night, #{name}" return result end puts say_goodnight('Satish') # modified program def say_goodnight2(name) "Good night, #{name}" end puts say_goodnight2('Talim') ruby 怎样管理内存中的字符串?有专门的字符串管理池吗? 所有的字符串都是类 string 的对象,有多于 75 个方法在 string 类中,用于处理字符串。 如果你去读Ruby User's Guide,就会看到“我们不必考虑字符串的位长问题,因为他们是变 长的,不需要为字符串做特殊的内存管理。” 列出一个类或对象的所有方法 String.methods.sort 列出类 string 的所有方法。 String.instance_methods.sort 列出 string 实例的所有实例方法。 String.instance_methods(false).sort 列出 string 实例的所有非继承的实例方法。 比较两个字符串 ruby 中比较字符串的方法有好几种。其中最普通的就是“==”,它比较两个字符串的内容是 否相同;还有 String.eql?,它比较的也是两个字符串的内容。还有 String.equal?,它比较两 个字符串是否同一个对象。看下面例子: 使用%w 有时候定义一个字符串或字符数组,需要给每一个元素加上引号和逗号,比较麻烦。 ruby 提供了一种简洁的方法,使用%w。 # String#eql?, tests two strings for identical content. # It returns the same result as == # String#equal?, tests whether two strings are the same object s1 = 'Jonathan' s2 = 'Jonathan' s3 = s1 if s1 == s2 puts 'Both Strings have identical content' else puts 'Both Strings do not have identical content' end if s1.eql?(s2) puts 'Both Strings have identical content' else puts 'Both Strings do not have identical content' end if s1.equal?(s2) puts 'Two Strings are identical objects' else puts 'Two Strings are not identical objects' end if s1.equal?(s3) puts 'Two Strings are identical objects' else puts 'Two Strings are not identical objects' end names1 = [ 'ann', 'richard', 'william', 'susan', 'pat' ] puts names1[0] # ann puts names1[3] # susan # this is the same: names2 = %w{ ann richard william susan pat } puts names2[0] # ann puts names2[3] # susan 练习: 给你一个字符串,把字符串中的单词反转。 首先用 String.split 方法得到包含每个单词的数组,然后用数组的 reverse 方法给单词重新排 序,最后用 join 方法把单词重新组成一个字符串。 words = 'Learning Ruby - Your one stop guide' puts words.split(" ").reverse.join(" ") # guide stop one Your - Ruby Learning 所有有关字符串的文档,见这儿。 第十四章 控制语句 在 ruby 中,除了 nil 和 false 是假外,其他所有的都是真。nil 也是一个真实的对象,你可以 调用 nil 的方法,也可以为 nil 定义新的方法。 下面讲一下 ruby 的控制语句。 ruby 规范说明,if 和 while 语句不需要括起来。 下面的例子用的是 if else end 语句。 下面的例子用到了 elsif: # In Ruby, nil and false evaluate to false, # everything else (including true, 0) means true # nil is an actual object # if else end var = 5 if var > 4 puts "Variable is greater than 4" puts "I can have multiple statements here" if var == 5 puts "Nested if else possible" else puts "Too cool" end else puts "Variable is not greater than 5" puts "I can have multiple statements here" end # elseif example # Original example puts "Hello, what\'s your name?" STDOUT.flush name = gets.chomp puts 'Hello, ' + name + '.' if name == 'Satish' puts 'What a nice name!!' else if name == 'Sunil' puts 'Another nice name!' end end 在条件判断语句中,可以使用一些常用的关系运算符,比如,==, != >=, <=, >, <。 ruby 还有一种条件判断语句,unless end。除非 unless 后面跟的条件为真,才会运行 unless end 间的程序,否则跳过。 这段程序的意思是除非数组 ARGV 的元素个数为 2 个(也就是程序的 2 个参数都给定),才 会运行程序块中的语句。 exit 是 ruby 内核内置方法,意思是退出当前应用程序,并返回给操作系统一个值。 循环语句的使用像下面这样: # Modified example with elseif puts "Hello, what\'s your name?" STDOUT.flush name = gets.chomp puts 'Hello, ' + name + '.' if name == 'Satish' puts 'What a nice name!!' elsif name == 'Sunil' puts 'Another nice name!' end # Further modified puts "Hello, what\'s your name?" STDOUT.flush name = gets.chomp puts 'Hello, ' + name + '.' # || is the logical or operator if name == 'Satish' || name == 'Sunil' puts 'What a nice name!!' end unless ARGV.length == 2 puts "Usage: program.rb 23 45" exit end # Loops var = 0 while var < 10 puts var.to_s var += 1 end 作业: 1. 写一个程序,接受用户输入的年份,输出是闰年还是平年。 2. 写一个方法 leap_year,接受用户输入的年份,输出是闰年还是平年,并且输出该年共有 多少分钟。 分支选择语句(case) 分支选择语句类似于多个 if 语句。它会在一系列条件语句中找到第一个为真的语句。 比如闰年年份要求能被 4 或 400 整除,但是不能被 100 整除。 分支选择语句也会返回运行过程中,最后一条语句的值。 注意是运行过程中最后一条语句,如果这条语句根本没执行,不会返回它的值。 year = 2000 leap = case when year % 400 == 0: true when year % 100 == 0: false else year % 4 == 0 end puts leap # output is: true 第十五章 数组 数组包含了一系列按顺序排列起来的元素。元素可以是同一种类型,也可以是不同类型。定 义数组的时候使用方括号([])把元素括起来。仔细看下面的例子: # Arrays # Empty array var1 = [] # Array index starts from 0 puts var1[0] # an array holding a single number var2 = [5] puts var2[0] # an array holding two strings var3 = ['Hello', 'Goodbye'] puts var3[0] puts var3[1] flavour = 'mango' # an array whose elements are pointing # to three objects - a float, a string and an array var4 = [80.5, flavour, [true, false]] puts var4[2] # a trailing comma is ignored name = ['Satish', 'Talim', 'Ruby', 'Java',] puts name[0] puts name[1] puts name[2] puts name[3] # the next one outputs nil # nil is Ruby's way of saying nothing puts name[4] # we can add more elements too name[4] = 'Pune' puts name[4] # we can add anything! name[5] = 4.33 puts name[5] # we can add an array to an array name[6] = [1, 2, 3] each 方法的作用是遍历数组中的每一个对象,并且可以处理这些对象。 从上面的例子看到,遍历数组对象并不需要根据数组长度做循环。 但是要记住下面几点: 1. '||'之间的变量名可以是任意的,代表的是数组中的每一个对象。 2. 对每一个数组元素,do end 之间的程序块都会执行。以后我们会详细的讲解程序块。 下面的例子中,方法返回了一个数组,非常有意思: puts name[6] # some methods on arrays newarr = [45, 23, 1, 90] puts newarr.sort puts newarr.length puts newarr.first puts newarr.last # method each (iterator) - extracts each element into lang # do end is a block of code # we shall talk about blocks soon here - # http://rubylearning.com/satishtalim/ruby_blocks_and_procs.html # variable lang refers to each item in the array as it goes through the loop languages = ['Pune', 'Mumbai', 'Bangalore'] languages.each do |lang| puts 'I love ' + lang + '!' puts 'Don\'t you?' end # delete an entry in the middle and shift the remaining entries languages.delete('Mumbai') languages.each do |lang| puts 'I love ' + lang + '!' puts 'Don\'t you?' end # if you give return multiple parameters, # the method returns them in an array # The times method of the Integer class iterates block num times, # passing in values from zero to num-1 输出结果是: def mtdarry 10.times do |num| square = num * num return num, square if num > 5 end end # using parallel assignment to collect the return value num, square = mtdarry puts num puts square 6 36 整数的 times 方法会迭代整数次,并且初始值是 0。上面的例子中,num 会从 0 到 9 迭代, 但是由于有一个 if 条件存在,所以当 num=6 时,跳出了循环。 方法返回多个参数时,参数被放在数组中返回,可以定义多个变量来接收数组中的元素。 ruby 支持像 awk 一样的关联数组吗?是的,Hashes 也是可以用在 ruby 中的。 更多的数组的文档,请看这儿。 作业: 1. 写一个程序,计算数组[1, 2, 3, 4, 5]中所有元素的和。 2. 写一个程序,判断数组[12, 23, 456, 123, 4579]中每一个元素是偶数还是奇数。 总结三 1. method_missing 提供了一个友好的办法,处理找不到方法这种情况,代替了返回一个异常。 2. 查找文档能得到详细的有关 string 的方法。 3. 双引号括起来的字符串处理过程要复杂一些。它会处理转义字符和#{expression}中的表达 式。 4. 记住那些能列出类和对象的所有方法的方法。 5. == 或者 eql?比较的是两个字符串的内容,equal?比较两个字符串是否同一个对象。 6. %w 可以用来简化数组的初始化。 7. 熟悉常用控制语句:if else end, while, if elsif end 8. 另一个条件语句:unless end 9. 多分支选择语句:case when else end。 10. 数组是按顺序排列的对象,数组元素可以是同一类型,也可以是不同类型。定义数组使 用方括号。 11. 数组的索引都是整数,并且以 0 开始。 12. 数组定义时,如果末尾有逗号,忽略它。 13. 如果访问的数组元素越界,会返回 nil。 14. 可以动态的给数组添加元素。 15. 查找相关文档获得更多数组信息。 16. each 方法能遍历数组中的所有元素,并且可以操作这些元素。 17. 包含在'||'中的变量名称是任意的,代表的是数组中的每个元素。 18. 遍历数组中的每一个元素时,都会运行 do end 程序块。 It is to be noted that every time a string literal is used in an assignment or as a parameter, a new String object is created. 原来的总结中有上面一句话,意思是当定义一个字符串时,就生成了一个字符串对象。 如果按字面翻译,这句话容易和按值传递还是引用传递冲突,所以我把它放在这个注释里, 可以先不理解这句话。 第十六章 范围 范围这个概念用在一系列元素(序列)的情况下。这一系列元素有一个起点,有一个终点, 中间是连续的,有规律的元素。 在 ruby 中,可以使用'..'和'...'生成这样的范围对象。用'..'生成的序列包含终点,而'...'生成的 序列不包含终点。 范围可以转换成数组,比如,(1..10).to_a -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 。 范围也有一系列方法用来迭代和对范围元素进行操作。 范围对象使用最多的就是判断某个值是否在某个范围内,比如, =begin Sequences have a start point, an end point, and a way to produce successive values in the sequence In Ruby, sequences are created using the ".." and "..." range operators. The two dot form creates an inclusive range. The three-dot form creates a range that excludes the specified high value The sequence 1..100000 is held as a Range object =end digits = -1..9 puts digits.include?(5) # true puts digits.min # -1 puts digits.max # 9 puts digits.reject {|i| i < 5 } # [5, 6, 7, 8, 9] (1..10) === 5 -> true (1..10) === 15 -> false (1..10) === 3.14159 -> true ('a'..'j') === 'c' -> true ('a'..'j') === 'z' -> false 在这儿使用的是'==='操作符。 作业: 给定一个字符串,s = 'key=value',从这个字符串中分离出两个子字符串,一个包含'key',另 一个包含'value'。
还剩34页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 3 人已下载

下载pdf

pdf贡献者

sonia

贡献于2011-04-24

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf