Ruby3 之旅 Part02:速览

Ruby3 之旅 Part02:速览

编程既是一门学科也是一门艺术,告诉计算机如何处理计算机程序需要能够像科学家一样进行分析思考,并且在概念上像艺术家一样思考,成为一名艺术家对未来至关重要,有远大的想法,灵活地采取独特的方法,成为一名科学家是一件很有趣的事情。

幸运的是,你不需要成为一名艺术家或科学家。就像训练身体一样,编程练习和思考如何解决问题可以训练大脑,使你成为一名优秀的程序员。任何人都可以学习编程,最大的障碍是缺乏动力和承诺。Ruby 是最容易学习的编程语言之一,你学习的目的可能是为了创建一个特定的应用程序,或解决一个特定的任务,这就是你的动机。

当你读完这篇文章时,我希望你能领略到一种强大但看似简单的编程语言所带来的乐趣,并开始对构建自己的东西感到兴奋!

1 启蒙

在 Part01 中,重点介绍了如何安装 Ruby,以便计算机能够理解该语言。在本章末尾,加载了一个名为 irb 的程序。

1.1 irb: Ruby 交互

irb 为 Ruby 的交互式环境,交互式意味着,只要你输入某个内容并按 Enter 键,计算机就会立即尝试处理它。有时,这种环境被称为即时或交互环境。

启动 irb 并确保出现提示,如下所示:

irb(main):001:0>

这个提示看上去并不那么复杂,这意味着在 irb 程序中,在输入第一行 (001),深度是 0,此时不需要对深度元素进行任何重视。

输入 1 + 1 后按 Enter,结果显示出为 2,整个过程如下所示:

irb(main):001:0> 1 + 1
=> 2
irb(main):002:0>

Ruby 现在已经准备好接受来自你的另一个命令或表达式。

作为一名新的 Ruby 程序员,将花费大量时间在 irb 和建立对 Ruby 的深入了解上。它为调整和测试语言提供了完美的环境。

irb 的互动环境为你提供即时反馈,这是学习时的一个重要工具。与在文本编辑器中编写程序、保存程序、让计算机运行程序,然后查看错误以查看错误所在不同,只需键入小代码片段,按 Enter 键,然后立即执行看看会发生什么。

如果你想做进一步的实验,可以尝试其他算法,比如 100 * 5、57 + 99、10 – 50 或 100 / 10 (如果你觉得最后一个算法不合适,在 Ruby 中,正斜杠字符 /,是除法的运算符)。

1.2 Ruby 是 “计算机英语”

在最低层次上,计算机处理器是由晶体管组成的,这些晶体管对电子信号做出响应和作用,但考虑在这个层次上执行操作既耗时又复杂,因此我们倾向于使用更高级的 “语言” 来传达我们的意图,就像我们使用英语等自然语言一样。

计算机可以理解语言,尽管与大多数人理解语言的方式截然不同。作为无法理解微妙或歧义的逻辑设备,英语和法语等语言对计算机没有吸引力。计算机需要具有逻辑结构和定义良好的语法的语言,这样你告诉计算机要做的事情在逻辑上就清晰了。

清晰是必需的,因为编程时几乎所有传递到计算机的东西都是指令 (或命令)。指令是所有程序的基本组成部分,为了让计算机正确地执行 (或执行) 指令,程序员的意图必须清晰而准确。数以百计的指令被绑定到执行特定任务的程序中,这意味着几乎没有出错的空间。

你还需要考虑其他程序员可能需要维护你编写的计算机程序。如果你只是为了好玩而编程,情况就不会是这样了,但重要的是你的程序要很容易理解,这样以后再看时就可以理解它们了。

1.3 为什么 Ruby 是一种伟大的编程语言

尽管英语会成为一种糟糕的编程语言,但由于其模糊性和复杂性,Ruby 有时会让人惊讶地感觉像英语。Ruby 只是数百种编程语言中的一种,但它很特别,因为对于许多程序员来说,让人感觉很像一种自然语言,同时具有计算机所需的清晰度。考虑以下示例代码:

10.times do print "Hello, world!" end

大声朗读这段代码,真的很有帮助!它不像英语那么流畅,但意思应该马上就清楚了。它要求计算机打印 10 次 “Hello, world!”。如果已运行irb,请键入前面的代码,然后按 Enter 键查看结果:

图片[1]-Ruby3 之旅 Part02:速览-零度非安全

结果输出 Hello, world! 10 次应该不会令人惊讶。然而,结尾的 => 10 可能看起来更令人困惑,但我们稍后将讨论它的含义。

下面是一个更复杂的例子,可能发生在 web 应用程序中:

user = User.find_by_email('me@privacy.net')
user.country = 'Belgium'

不要复制和粘贴此代码,它不会在特定应用程序的上下文之外工作。

这段代码远不如 Hello, world! 你仍然应该能够很好地猜测它的作用。首先,它告诉计算机你想使用一个叫做 User 的概念。接下来,它尝试查找具有指定电子邮件地址的用户。最后,将用户的国家数据更改为比利时,此时不必担心数据是如何存储给用户的,那是以后的事了。

这是一个相当高级和抽象的示例,但它演示了一个潜在复杂应用程序中的单个概念,你可以在其中处理不同的概念,例如 “users”。在本章结束时,你将看到如何在 Ruby 中创建自己的真实概念,并以与本例类似的方式对其进行操作,你的代码几乎可以像英语一样容易阅读。

1.4 心灵的足迹

学习本身就是一项有趣的活动,但仅仅阅读一些东西并不能让你成为这方面的专家。我读过几本烹饪书,但当我不断尝试时,这似乎并没有改善我的烹饪。缺少的是实验和测试,因为没有这些,你的努力充其量只是学术性的。

考虑到这一点,从使用 Ruby 的第一天开始就有必要进行实验和测试。在整本书中,我将要求你尝试不同的代码块,并对它们进行处理,看看是否能得到想要的结果。你偶尔会给自己一个惊喜,有时会把代码推向死胡同,但这都是乐趣的一部分。不管发生什么,所有优秀的程序员都会从实验中学习,你只能通过不断的实验来掌握一门语言和编程概念。

这本书将引导你穿过一片代码和概念的森林,但是如果不测试并证明代码对你自己是正确的,你很快就会迷失方向。使用 irb 和我将经常介绍的其他工具,并尽可能多地使用代码进行实验。

在 irb 提示符下键入以下代码,然后按 Enter 键:

print "test"

输出结果很简单:

test => nil

从逻辑上讲,print “test” 会将测试结果打印到屏幕上。然而,=>nil 后缀是代码作为表达式的结果 (更多信息请参见第 3 章)。这是因为 Ruby 中的所有代码行都由返回值的表达式组成。但是,print 会将数据显示到屏幕上,而不是以表达式的形式返回任何值,因此得到的结果为零。更多关于这一点,请参见第 3 章。在这个阶段,对此半信半疑是完全可以理解的。

让我们试试别的:

print "2+3 is equal to " + 2 + 3

从表面上看,这个命令似乎合乎逻辑。如果 2 + 3 等于 5,你把它加到 “2+3 is equal to” 的末尾,你应该得到 “2+3 is equal to 5″,对吗?不幸的是,会出现以下错误:

Traceback (most recent call last):
    5: from bin/irb:23:in `<main>'
    4: from bin/irb:23:in `load'
    3: from exe/irb:11:in `<top (required)>'TypeError (no implicit
        conversion of Integer into String)
    2: from (irb):2
    1: from (irb):2:in `+'
    TypeError (no implicit conversion of Integer into String)

Ruby 在你出错时会抱怨,这里它抱怨你不能将一个数字转换成一个字符串 (其中 “string” 是一组文本,比如这个句子)。数字和字符串不能以这种方式混合。解释原因还不重要,但像这样的实验将帮助你学习和记住更多关于 Ruby 的知识,而不仅仅是阅读这本书。当出现这样的错误时,你可以使用错误消息作为解决方案的线索,无论是在本书中、在互联网上还是通过询问其他开发人员。

作为一项快速的辅助活动,将 “无隐式整数转换为字符串” 错误复制并粘贴到 Google 中,看看会出现什么。如果你和大多数程序员一样,在你的编程生涯中,你会经常这样做。并不是你找到的每一篇文章都有用,但有时你可以通过在网上看到其他人的建议来摆脱棘手的情况。

上述问题的临时解决方案是:

print "2+3 is equal to "
print 2 + 3

或者这样:

print "2+3 is equal to " + (2 + 3).to_s
图片[2]-Ruby3 之旅 Part02:速览-零度非安全

两个都试试,让我们再举一个例子,10 除以 3 怎么样?

irb(main):002:0> 10 / 3
=> 3

计算机应该是精确的,但任何有基本算术技能的人都会知道,10 除以 3 等于 3.33,而不是 3!

产生这个奇怪结果的原因是,默认情况下,Ruby 假设 10 或 3 这样的数字是一个整数。Ruby 中带有整数的算术给出整数结果,因此有必要为 Ruby 提供一个浮点数 (一个带小数点的数字),以获得浮点数的答案,如 3.33。下面是一个如何做到这一点的例子:

Irb(main):001:0> 10.0 / 3
=> 3.333333333333

这样的结果使得测试和实验不仅是一个很好的学习工具,而且在开发更大的项目时也是必不可少的策略。现在这些错误已经够多了,让我们做些有用的东西!

2 将想法转化为 Ruby 代码

编程的艺术部分在于能够将你的想法转化为计算机程序。一旦你精通一门编程语言,你就可以把你的想法直接转化为代码。然而,在你能做到这一点之前,你需要了解 Ruby 如何理解现实世界的概念,以及如何将你的想法转化为 Ruby 欣赏的形式。

2.1 Ruby 如何理解对象和类的概念

Ruby 是一种面向对象的编程语言。从最简单的意义上讲,这意味着 Ruby 程序可以以一种模仿我们在现实世界中处理概念的方式来定义和操作概念。你的程序可以包含诸如 “people”、”boxes”、”tickets”、”map” 等概念,或者任何其他你想使用的概念。面向对象的语言使实现这些概念变得很容易,你可以基于它们创建对象。作为一种面向对象的语言,Ruby 可以用任何你可以定义的方式来处理和理解这些概念之间的关系。

例如,你可能希望创建一个可以管理体育赛事门票预订的应用程序。涉及的概念包括 “events”、”people”、”tickets”、”venues” 等等。Ruby 允许你将这些概念直接放入程序中,创建它们的对象实例 (“event” 的实例可能是 Super Bowl 或世界杯决赛),并对它们执行操作并定义它们之间的关系。有了程序中的所有这些概念,可以快速地将 “events” 与 “venues”、”tickets” 与 “people” 关联起来,这意味着代码从一开始就形成了一个逻辑系统。

如果你以前没有编写过很多程序,那么把现实生活中的概念直接用在计算机程序中似乎是一种让软件开发更容易的明显方式。然而,面向对象在软件开发中是一个相当新的概念 (这个概念是在 20 世纪 60 年代提出的,但直到 20 世纪 90 年代才在主流编程中流行)。对于非面向对象的语言,程序员必须采取更为手动的方法来处理概念以及它们之间的关系,虽然这增加了更多的控制,但也带来了额外的复杂性。

2.2 Person 的形成

让我们直接演示一个简单的概念,person:

class Person
    attr_accessor :name, :age, :gender
end

Ruby 以前看起来很像英语,但在定义概念时却不像英语。让我们一步一步地看一遍:

class Person

这一行是你开始定义 “person” 概念的地方,当我们用 Ruby (或者其他大多数面向对象语言) 定义概念时,我们称它们为类。类是单一类型对象的定义。Ruby 中的类名总是以大写字母开头,因此你的程序将以 User、Person、Place、Topic、Message 等名称结束:

attr_accessor :name, :age, :gender

前一行为 Person 类提供了三个属性。一个人有 name、age 和 gender,这一行创建了这些属性。attr 代表 “attribute”,accessor 的大致意思是 “使这些属性可以随意设置和更改”,这意味着,在代码中处理 Person 对象时,可以更改此人的 name、age 和 gender (或者更准确地说,更改对象的 name、age 和 gender 属性):

end

end 应具有明显的实用性。它与第一行的类定义相匹配,并告诉 Ruby 不再定义 Person 类。

总而言之,一个类定义了一个概念 (比如 Person),而一个对象是基于类的单一事物 (比如 “Chris” 或 “Mrs.Smith”)。

所以,让我们用 Person 类进行实验。转到 irb 提示符,输入前面找到的 Person 类。应该是这样的:

irb(main):001:0> class Person
irb(main):002:?>     attr_accessor :name, :age, :gender
irb(main):003:?> end
=> nil
irb(main):004:0>

你会注意到,irb 可以识别你何时在类定义中,因为它会自动缩进代码。

一旦完成了类定义,Ruby 处理了它,就会返回 nil,因为定义一个类不会导致返回值,而 nil 是 Ruby 表示 “nothing” 的方式 (没发生错误),Person 类现在存在于 Ruby 中,所以让我们对它做些事情:

person_instance = Person.new
person_instance.inspect
=> #<Person:0x007fbb0c625f88>

第一行所做的是创建 Person 类的一个新实例,因此你正在创建一个新 Person,并将其分配给 Person_instance —— 一个表示新 Person 的占位符,称为变量。第二行是 Ruby 对创造一个新 Person 的回应,在这个阶段并不重要。0x007fbb0c625f88 将因计算机而异,只代表 Ruby 分配给新 Person 的内部引用,你根本不需要考虑它。

让我们马上做点什么:

person_instance.name = "Christine"

在这个基本示例中,引用 person_instance 的 name 属性,并将其值设为 “Christine”。你刚刚给你的 person 起了个名字。Person 类还有两个属性:age 和 gender。让我们设定这些:

person_instance.age = 52
person_instance.gender = "female"

这很容易理解,你给了这个人一个基本的身份。把这个人的名字打印回屏幕怎么样?

puts person_instance.name

按 Enter 键时,Christine 出现,age 和 gender 也一样。

在前面的示例中,你已经使用打印将内容显示在屏幕上。也使用了 puts。print 和 puts 之间的区别在于,puts 会自动将输出光标移动到下一行,即它会添加一个换行符以开始新行,而 print 会继续将文本打印到与上一次相同的行上。一般来说,你会想使用 puts,但我使用 print 使前面的示例在大声朗读时更直观。

图片[3]-Ruby3 之旅 Part02:速览-零度非安全

2.3 基本变量

在上一节中,你创建了一个 person,并将其分配给一个名为 person_instance 的变量。变量是编程的一个重要组成部分,它们很容易理解,尤其是如果你有最基本的代数知识。考虑一下:

x = 10

此代码将值 10 分配给变量 x。由于 x 现在等于10,你可以执行以下操作:

x * 2

输出结果显示 20。

Ruby 中的变量可以指 Ruby 理解的任何与值相关的概念,比如数字、文本和我将在本书中介绍的其他数据结构。在上一节中,person_instance 是一个引用 person 类的对象实例的变量,就像 x 是一个包含数字 10 的变量一样。更简单地说,将 person_instance 视为一个引用特定、唯一的 person 对象的名称。

当你想在一个程序中存储一些东西并在多行中使用它时,你将使用变量作为你正在处理的数据的临时存储位置。

2.4 从 People 到 Pets

在之前你创建了一个简单的类 (Person),并创建了该类的一个对象,将其指定为 person_instance 变量,并为其提供了一个查询的标识 (我们称之为 Christine)。如果这些概念对你来说很简单,那么很好,你已经了解了面向对象的基本知识!如果没有,请重新阅读上一节,确保你在计算机上遵循这一节,但也要阅读这一节,因为我将进一步深入讨论。

你最初使用的是 Person 类,但现在需要更复杂的东西,所以让我们创建一些 “Pets”。再创造一些 cats,dogs 和 snakes。第一步是定义类。你可以这样做:

class Cat
    attr_accessor :name, :age, :gender, :color
end
class Dog
    attr_accessor :name, :age, :gender, :color
end
class Snake
    attr_accessor :name, :age, :gender, :color
end

这就像创建 Person 类一样,你可以继续用 lassie = Dog.new 或者 sammy = Snake.new 这样的代码创建动物,并为它们设置属性,lassie.age = 12 或 sammy.color = “Green”。输入前面的代码,如果愿意,可以尝试一下。

然而,以这种方式创建类会错过面向对象编程的一个更有趣的特性:继承。

继承允许不同的类相互关联,并根据它们的相似性对概念进行分组。在这种情况下,cat、dog 和 snake 都是宠物。继承允许你创建一个父宠物类,然后让 cat、dog 和 snake 类继承 (“is-a”) 所有宠物都具有的特性。

现实生活中几乎所有的东西都存在于与你的课程相似的结构中。cat 可以是宠物,反过来又是动物;它们又是生物;它们又是存在于宇宙中的物体。类的层次结构无处不在,面向对象语言允许你在代码中定义这些关系。

2.4.1 合理安排你的宠物

现在,我们已经想出了一些改进代码的想法,让我们从头开始重新键入它。要彻底清理并重置您正在处理的内容,可以重新启动 irb。irb 不记得在你使用它的不同时间之间的信息。因此,重新启动 irb (要退出 irb,请键入 exit 并按 Enter 键),然后重写类定义,如下所示:

class Pet
    attr_accessor :name, :age, :gender, :color
end
class Cat < Pet
end
class Dog < Pet
end
class Snake < Pet
end

首先,创建 Pet 类并定义 Pet 对象可用的 name、age、gender 和 color 属性。接下来,定义从 Pet 类继承的 Cat、Dog 和 Snake类 (在本例中,< 运算符表示从哪个类继承)。这意味着 cat、dog 和 snake 对象都将具有 name、age、gender 和 color 属性,但由于这些属性的功能是从 Pet 类继承的,因此不必在每个类中专门创建这些功能。如果你想存储更多关于 pets 的信息,或者想添加其他类型的动物,这使得代码更容易维护和更新。

那么与每种动物无关的属性呢?如果你想存储 snake 的长度,但不想存储 dog 或 cat 的长度,该怎么办?幸运的是,继承给你很多好处,没有坏处。你仍然可以在任何地方添加特定于类的代码。像这样重新进入 Snake 类:

class Snake < Pet
    attr_accessor :length
end

Snake 类现在有一个 length 属性。然而,这被添加到 snake 从 Pet 继承的属性中,因此 snake 有 name、age、gender、color 和 length 属性,而 cat 和 dog 只有前四个属性。你可以这样测试:

irb(main):001:0> snake = Snake.new
irb(main):002:0> snake.name = "Sammy"
irb(main):003:0> snake.length = 500
irb(main):004:0> lassie = Dog.new
irb(main):005:0> lassie.name = "Lassie"
irb(main):006:0> lassie.age = 20
irb(main):007:0> lassie.length = 10

输出:

NoMethodError (undefined method 'length=' for #<Dog:0x32fddc @age=20,@name=“Lassie">)
图片[4]-Ruby3 之旅 Part02:速览-零度非安全

在这里,你创造了一条 dog 和一条 snake。你给 snake 的长度是 500,然后给 dog 的长度是 10 (单位并不重要)。尝试为 dog 指定长度会导致未定义方法的错误,因为你只为 Snake 类指定了 length 属性。

尝试使用其他属性并创建其他 pets。尝试使用不存在的属性,看看错误消息是什么。

2.4.2 控制你的宠物

到目前为止,已经创建了具有各种可变属性的类和对象。属性是与单个对象相关的数据。snake 可以有 length,dog 可以有 name,cat 可以有某种 color。我之前提到的说明呢?你如何给你的对象指令去执行?为每个类定义方法。

方法在 Ruby 中很重要。它们使你能够告诉对象执行操作。例如,你可能希望向 Dog 类添加一个 bark 方法,如果对 Dog 对象调用该方法,它将打印 Woof!对着屏幕。你可以这样写:

class Dog < Pet
    def bark
        puts "Woof!"
    end
end

输入此代码后,创建的任何 dog 现在都可以 bark。让我们试试:

irb(main):0> a_dog = Dog.new
irb(main):0> a_dog.bark

输出:

Woof!
图片[5]-Ruby3 之旅 Part02:速览-零度非安全

Eureka!你会注意到,你让 dog bark 的方式只是指 a_dog,并包括点号 (.) 然后是 bark 方法的名称,即你的 dog “barks”,让我们仔细分析一下到底发生了什么。

首先,你在你的 Dog 类中添加了一个 bark 方法。这样做的方式是通过定义方法。要定义方法,请使用 def 一词,后跟要定义的方法的名称。这意味着我在这个类中定义 bark 方法,直到我说 end。接下来的一行简单地写上 “Woof!” 在屏幕上,方法的最后一行结束了该方法的定义。最后一个 end 结束了类定义 (这就是缩进有用的原因,因此可以看到哪个 end 与哪个定义对齐)。然后,Dog 类包含一个名为 bark 的新方法,正如你之前使用的那样。

考虑一下如何为其他 Pet 类或 Pet 类本身创建方法。有什么方法是所有 pets 通用的吗?如果是这样的话,上 Pet 类。有专门针对 cats 的方法吗?那就去上 Cat 类。

3 所有事物都是对象

在本部分中,我们了解了 Ruby 如何理解类和对象形式的概念。我们创建了虚拟 cats 和 dogs,给它们取名字,并触发它们的方法 (例如 bark 方法)。这些基本概念构成了面向对象编程的核心,在本书中你将不断使用它们。dogs 和 cats 只是面向对象提供的灵活性的一个例子,但到目前为止,我们使用的概念可以应用于大多数概念,无论我们是向 “ticket” 发出更改其价格的命令,还是向 “user” 发出更改其密码的命令。开始考虑你想要开发的程序的一般概念,以及如何将它们转换成可以用 Ruby 操作的类。

甚至在面向对象编程语言中,Ruby 也是相当独特的,因为语言中几乎所有的东西都是对象,甚至是与语言本身有关的概念。考虑以下代码行:

puts 1 + 10

如果你在 irb 中输入这个,然后按 Enter 键,你会看到数字 11 作为回应。你已要求 Ruby 将 1 + 10 的结果打印到屏幕上。看起来很简单,但信不信由你,这条简单的一行使用了两个对象。1 是一个对象,10 也是。它们是 Integer 类的对象,这个内置类已经定义了一些方法来执行数字运算,比如加法和减法。

我们已经考虑了概念如何与不同的类相关联。我们的 pets 就是一个很好的例子。然而,即使将程序员用来编写计算机程序的概念定义为类和对象也是有意义的。当你写一个简单的和,比如 2 + 2,你希望计算机把两个数字加在一起,得到 4。Ruby 以面向对象的方式将两个数字 (2 和 2) 视为数字对象。2 + 2 只是要求第一个数字对象将第二个数字对象添加到自身的简写。事实上,+ 符号实际上是一种加法!(这是真的;2. + (2) 很好用!)

你可以通过询问 Ruby 中的所有东西是哪个类的成员来证明它们都是对象。在前面的 pet 示例中,你可以让 a_dog 用以下代码告诉你它是哪个类的成员:

puts a_dog.class

输出:

Dog

类不是你自己创建的方法,例如 bark 方法,而是 Ruby 默认为所有对象提供的方法。这意味着可以使用任何对象的类方法询问该对象是哪个类的成员。所以 a_dog.class 等于 Dog。

如果你问一个数字它的类别是什么呢?试试看:

puts 2.class

输出:

Integer

数字 2 是 Integer 类的一个对象。这意味着 Ruby 所要做的就是实现在 Integer 类中将数字相加的逻辑和代码,就像你为 Dog 类创建了 bark 方法一样,然后 Ruby 将知道如何将任意两个数字相加!不过,比这更好的是,可以在 Integer 类中添加自己的方法,并以你认为合适的任何方式处理数字。

3.1 Kernel 方法

Kernel 是一个特殊的类 (实际上是一个模块,但在 part06 之前不要担心!) 其方法在 Ruby 中的每个类和作用域中都是可用的 (如果这听起来很复杂,请考虑 Kernel 方法在任何情况下都是可用的)。你已经使用了 Kernel 提供的 key 方法。

以 puts 方法为例。使用 puts 方法将数据打印到屏幕上,如下所示:

puts "Hello, world!"

但是,与你自己的类上的方法不同,puts 的前缀不是要完成该方法的类或对象的名称。完整的命令应该类似于 Screen,这似乎是合乎逻辑的。放置或显示。在屏幕上放置文本。然而,在现实中,puts 是 Kernel 模块提供的一种方法 —— 一种特殊类型的类,包含了大量标准的、常用的方法,使代码更易于读写。

当你输入 “Hello, world!”,Ruby 可以判断出不涉及任何类或对象,因此它会在默认的预定义类和模块中查找一个名为 puts 的方法,在 Kernel 模块中找到它,然后执行它的操作。当看到代码行中没有明显的类或对象时,请花时间考虑方法调用的去向。

为了保证使用的是 Kernel 中的 puts 方法,可以显式地引用它,尽管 puts 很少这样做:

Kernel.puts "Hello, world!"

3.2 将数据传递给方法

用 Ruby 让 dog bark 或询问对象其类很简单。只需引用一个类或对象,并在其后加上点号 (.) 以及方法的名称,例如 a_dog.bark、2.class 或者 Dog.new,然而,在某些情况下,你不想发出简单的命令,但也希望将一些数据与之关联。

让我们创建一个表示 dog 的非常简单的类:

class Dog
    def bark
        puts "Woof!"
    end
end

现在,我们可以通过调用相关方法简单地让 dog bark:

my_dog = Dog.new
my_dog.bark

输出:

Woof!

这很简单,但是如果我们有一个用户输入有用的操作呢?我们可以编写在调用数据时接受数据的方法,例如:

class Dog
    def bark(i)
        i.times do
            puts "Woof!"
        end
    end
end

这一次,我们可以通过将一个值传递给 bark 方法,使 dog bark 一定次数:

my_dog = Dog.new
my_dog.bark(3)

输出:

Woof!
Woof!
Woof!

当我们在 my_dog.bark(3) 中指定 3 的参数时,它被传递给 bark 方法,并被放入定义的参数 i 中。然后,我们可以使用 i 作为源值,使用 times 块运行 puts 命令三次 (或者更准确地说,是 i 次)。

在这个早期阶段,还有一些其他的事情需要注意。首先,可以指定方法可以接受的许多不同参数,例如:

class Dog
    def say(a, b, c)
        puts a
        puts b
        puts c
    end
end

现在传递 3 个参数:

my_dog = Dog.new
my_dog.say("Dogs", "can't", "talk!")

输出:

Dogs
can't
talk!

你还应该注意,当只有一个参数且方法调用未与任何其他参数联接时,方法调用末尾的参数周围的括号是可选的。例如,你以前见过这样的代码:

puts "Hello"

但你可这样简写:

puts("Hello")

在本书中,你将继续看到许多调用方法和向它们传递参数的示例。仔细观察发生这种情况的各种方式,有无参数,有无括号。

3.3 使用 String 类中的方法

你已经玩过 dogs 和数字,但是文本行 (字符串) 也很有趣:

​x puts "This is a test".length

输出:

14

字符串 “This is a test” 使用 length 方法在屏幕上打印其长度,该字符串是 string 类的一个对象 (用 “This is a test.class” 确认)。length 方法适用于所有字符串,因此可以将 “This is a test” 替换为你想要的任何文本,你将得到一个有效的答案。

询问字符串的长度并不是你唯一能做的事情。考虑一下:

puts "This is a test".upcase

输出:

THIS IS A TEST

String 类有很多方法,我将在下一章中介绍这些方法,但要尝试以下几种方法:capitalize、downcase、chop、next、reverse、sum 和 swapcase。下表中展示了字符串可用的一些方法。

表达式输出
“Test” + “Test”TestTest
“test”.capitalizeTest
“Test”.downcasetest
“Test”.chopTes
“Test”.nextTesu
“Test”.reversetseT
“Test”.sum416
“Test”.swapcasetEST
“Test”.upcaseTEST
“Test”.upcase.reverseTSET
“Test”.upcase.reverse.nextTSEU

上表中的一些例子是显而易见的,比如改变文本的大小写或颠倒,但最后两个例子特别有趣。与其针对文本处理一种方法,不如连续处理两三种方法。可以这样做的原因是,在方法调整原始对象后,方法将返回该对象,因此有了一个新的字符串对象来处理另一个方法。”Test”.upcase 导致返回字符串 TEST,在该 TEST 中调用 reverse 方法,生成 TSET,在该 TSET 中调用 next 方法,该方法递增最后一个字符,生成 TSEU。

在下一部分中,我们将更深入地研究字符串,但是将方法链接在一起以获得快速结果的概念在 Ruby 中很重要。没有多少其他编程语言能让你达到这种即时熟悉程度!

4 以非面向对象的方式使用 Ruby

到目前为止,在本部分中,我们已经了解了几个相当复杂的概念。对于一些编程语言来说,面向对象几乎是事后才会谈到的,在读者了解该语言的基础知识 (尤其是 Perl 和 PHP,以及其他流行的 web 开发语言) 之前,这些语言的入门书籍不会介绍面向对象。然而,这对 Ruby 不起作用,因为 Ruby 是一种纯面向对象的语言,通过理解这些概念,可以比其他语言的用户获得显著的优势。

不过,Ruby 源于其他语言。Ruby 深受 Perl 和 C 等语言的影响,这两种语言通常被认为是过程性的非面向对象语言 (尽管 Perl 有一些面向对象的特性)。因此,尽管 Ruby 中几乎所有的东西都是对象,但如果你愿意,可以以类似于非面向对象语言的方式使用 Ruby,即使它不太理想。从本质上说,你将忽略 Ruby 的面向对象特性,即使它们仍在幕后运行。

Perl 或 C 等语言的常见演示程序包括创建一个子例程 (本质上是一种没有关联对象或类的方法) 并调用它,就像在 Dog 对象上调用 bark 方法一样。下面是一个用 Ruby 编写的类似程序:

def dog_barking
    puts "Woof!"
end
dog_barking

这看起来和你之前的实验有很大不同。如果你看起来是在定义一个类,而不是完全独立地定义一个类,那么你可以在类中定义它。该方法是一种通用方法,似乎不与任何特定的类或对象绑定。在 Perl 或 C 之类的语言中,这个方法被称为过程、函数或子函数,因为 method 是一个通常用来指代对象上可能发生的动作的词。在 Ruby 中,这个方法仍然是在一个类 (对象类) 上定义的,但是我们可以在这个上下文中忽略它。

在定义方法之后,它仍然被称为方法,即使其他语言会认为它是一个子例程或函数,它可以立即使用,而无需使用类或对象名,就像 puts 如何在不直接引用 Kernel 模块的情况下可用一样。只需使用方法本身的名称即可调用该方法,如前一个示例的最后一行所示。在 irb 中键入前面的代码将导致调用 dog_barking 方法,并给出以下结果:

Woof!

在 Ruby 中,几乎所有的东西都是一个对象,这包括了无类方法结束的神奇空间!在这个阶段,准确地理解何处并不重要,但记住 Ruby 的面向对象方法总是很有用的,即使你试图不使用面向对象技术!

5 总结

在本篇文章中,学习了几个重要的概念,不仅适用于 Ruby 编程,也适用于一般编程。如果这些概念对你来说已经是合乎逻辑的了,那么你就很快成为一名可靠的 Ruby 开发人员了。在继续之前,让我们回顾一下主要概念:

  • 类 (class):类是面向对象语言 (如 Ruby) 中概念的定义。我们创建了名为 Pet、Dog、Cat、Snake 和 Person 的类。类可以继承其他类的功能,但仍然有自己独特的功能。
  • 对象 (object):对象是一个类的单个实例 (或者,可以是一个类本身的实例)。类的对象 Person 是一个人。Dog 类的对象是一只狗。把物体想象成现实生活中的物体。类是分类,而对象是实际对象或事物本身。
  • 面向对象 (object orientation):面向对象是一种使用类和对象在编程语言 (如 Ruby) 中对现实世界的概念建模的方法。
  • 变量 (variable):在 Ruby 中,变量是单个对象的占位符,可以是数字、字符串、列表 (其他对象) 或定义的类的实例,如本文中的 Pet。
  • 方法 (method):方法表示类或对象中的一组代码 (包含多个命令和语句)。例如,我们的 Dog 类对象有一个 bark 方法,输出 “Woof!”。方法也可以直接链接到类,比如 fred=Person.new,其中 new 是基于 Person 类创建新对象的方法。方法还可以接受被称为参数或参数的数据,这些数据包含在方法名称后面的括号中,比如 puts(“Test”)。
  • 参数 (Arguments/parameters):这些是用括号传递给方法的数据 (或者,在某些情况下,在没有括号的方法名称后面,如 puts “Test”)。从技术上讲,将参数传递给方法,而方法接收参数,但出于实用目的,这些术语可以互换。
  • Kernel:有些方法不需要类或模块名才能使用,比如 puts。这些通常是内置的通用方法,与任何类或模块都没有明显的联系。其中许多方法都包含在 Ruby 的 Kernel 模块中,该模块提供的函数可以在 Ruby 代码中的任何位置工作,而无需显式引用 (如果你愿意,这是一个有用方法的全局 “抓包”)。
  • 实验:编程最令人满足的事情之一就是你可以把梦想变成现实。你需要的技能数量随着你的梦想而变化,但一般来说,如果你想开发某种类型的应用程序或服务,你可以尝试一下。大多数软件都来自于需要或梦想,所以保持你的眼睛和耳朵对你可能想要开发的东西开放是很重要的。当你第一次获得一门新语言的实用知识时,这一点就更重要了,就像你在读这本书时一样。如果你想到了一个想法,把它分解成最小的组件,你可以用 Ruby 类来表示,看看你是否可以用你迄今为止所学的 Ruby 来组合构建块。你的编程技能只有通过练习才能提高。

在接下来的文章中,将更详细地介绍本篇文章简要介绍的主题。

© 版权声明
THE END
喜欢就支持一下吧
点赞1赞赏 分享
评论 抢沙发

请登录后发表评论