跳转到内容

Ruby 编程/数据类型

来自 Wikibooks,开放世界中的开放书籍

← Ruby 基础 | 编写方法 →


Ruby 数据类型

[编辑 | 编辑源代码]

如前一章所述,Ruby 中的一切都是对象。Ruby 有 8 种主要数据类型,以及 3 种从 Numeric 超类派生的数据类型。一切都有类。不信?试试运行这段代码

h = {"hash?" => "yep, it\'s a hash!", "the answer to everything" => 42, :linux => "fun for coders."}
puts "Stringy string McString!".class
puts 1.class
puts 1.class.superclass
puts 1.class.superclass.superclass
puts 4.3.class
puts 4.3.class.superclass
puts nil.class
puts h.class
puts :symbol.class
puts [].class
puts (1..8).class

显示

String
Fixnum
Integer
Numeric
Float
Numeric
NilClass
Hash
Symbol
Array
Range


看到了吧?一切都 是对象。每个对象都有一个名为class的方法,它返回该对象的类。你几乎可以对任何东西调用方法。之前你看到了 3.times 形式的例子。(从技术上讲,当你调用一个方法时,你是在向对象发送消息,但我将把这一点的重要性留到以后。)
对我来说,这种极端的 面向对象特性非常有趣,因为所有类都是开放的,这意味着你可以在代码执行期间随时向类添加变量和方法。不过,这与数据类型讨论无关。

我们先从常量开始,因为它们很简单。关于常量,有两点需要注意
1. 常量以大写字母开头。Constant是一个常量。constant不是常量。
2. 你可以更改常量的值,但 Ruby 会给出警告。(我知道这很蠢...... 但你能怎么办呢?)
恭喜。现在你已经成为 Ruby 常量的专家了。

你是否注意到第一个代码列表中有一些奇怪的东西?“那个冒号到底是怎么回事?”嗯,恰好 Ruby 的面向对象方式有成本:大量的对象会导致代码变慢。每次你输入一个字符串时,Ruby 都会创建一个新对象。无论两个字符串是否相同,Ruby 都将每个实例视为一个新对象。你的代码中可能有一次出现“live long and prosper”,然后再次出现,Ruby 甚至不会意识到它们几乎是一样的东西。下面是一个示例 irb 会话,演示了这一事实

irb> "live long and prosper".object_id
=> -507772268
irb> "live long and prosper".object_id
=> -507776538

请注意,irb Ruby 返回的 对象 ID 即使对于相同的两个字符串也是不同的。

为了解决这种内存消耗问题,Ruby 提供了“符号”。Symbol 是轻量级对象,最适合用于比较和内部逻辑。如果用户永远看不到它,为什么不使用符号而不是字符串呢?你的代码会感谢你的。让我们尝试使用符号而不是字符串运行上面的代码

irb> :my_symbol.object_id
=> 150808
irb> :my_symbol.object_id
=> 150808

符号由前面的冒号表示,如下所示::symbol_name

哈希在某种程度上就像字典。你有一个键,一个引用,你查找它以找到关联的对象,即定义。

我认为,最好的说明方法是通过快速演示

hash = { :leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi"}
puts hash[:leia]
puts hash[:han]
puts hash[:luke]

显示

Princess from Alderaan
Rebel without a cause
Farmboy turned Jedi

我也可以这样写

hash = { :leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi"}
hash.each do |key, value|
     puts value
end

这段代码遍历哈希中的每个元素,将键放在 key 变量中,将值放在 value 变量中,然后显示该值

Princess of Alderaan
Rebel without a cause
Farmboy turned Jedi

我本可以更详细地定义我的哈希;我可以这样写

hash = Hash.[](:leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi")
hash.each do |key, value|
     puts value
end

如果我想杀了卢克,我可以这样做

hash.delete(:luke)

现在卢克不再在哈希中了。或者假设我讨厌所有农场男孩。我可以这样做

hash.delete_if {|key, value| value.downcase.match("farmboy")}

这将遍历每个键值对并将其删除,但前提是它后面的代码块返回 true。在代码块中,我将值转换为小写(以防农场男孩开始做诸如“FaRmBoY!1!”之类的事情),然后检查“farmboy”是否与它的内容中的任何内容匹配。我本可以使用正则表达式,但那是另一回事了。

我可以通过将新值分配给哈希来将兰多加入进来

hash[:lando] = "Dashing and debonair city administrator."

我可以使用 hash.length 测量哈希。我可以使用 hash.keys 方法查看只有键,该方法将哈希的键作为 Array 返回。说到这个......

Array 很像 Hash,除了键总是连续的数字,并且总是从 0 开始。在一个包含五个项目的 Array 中,最后一个元素将位于 array[4],而第一个元素将位于 array[0]。此外,你刚学到的所有 Hash 方法也可以应用于 Array

以下两种方法可以创建 Array

array1 = ["hello", "this", "is", "an", "array!"]
array2 = []
array2 << "This"   # index 0
array2 << "is"     # index 1
array2 << "also"   # index 2
array2 << "an"     # index 3
array2 << "array!" # index 4

正如你可能猜到的那样,<< 运算符将值推送到 Array 的末尾。如果我在声明这两个 Array 后编写 puts array2[4],则输出将为 array!。当然,如果我想同时获得 array! 并将其从数组中删除,我可以简单地 Array.pop 它。 Array.pop 方法返回数组中的最后一个元素,然后立即将其从该数组中删除

string = array2.pop

然后 string 将保存 array!,而 array2 将减少一个元素。
如果我一直这样做,array2 将不再包含任何元素。我可以通过调用 Array.empty? 方法检查此条件。例如,以下代码段将所有元素从一个 Array 移动到另一个 Array

array1 << array2.pop until array2.empty?

这里有一件让我非常兴奋的事:Array 可以互相减去和加起来。我不能保证所有语言都这样,但我确信如果我尝试执行以下代码段,Java、C++、C# 和 perl 会觉得我疯了

array3 = array1 - array2
array4 = array1 + array2

在评估该代码后,以下所有内容都为真

  • array3 包含 array1 中的所有元素,除了那些也存在于 array2 中的元素。
  • 现在 array3 包含了 array1 中的所有元素,减去 array2 中的元素。
  • array4 现在包含了 array1array2 中的所有元素。

你可以使用 Array.include? 方法在 array1 变量中搜索特定值:array1.include?("Is this in here?")

如果你只想将整个 Array 转换为 String,你可以

string = array2.join(" ")

如果 array2 包含我们在上一个示例中声明的值,那么 string 的值将是

This is also an array!

我们可以不带任何参数调用 Array.join 方法

string = array2.join

string 的值现在将是

Thisisalsoanarray!

字符串

[edit | edit source]

如果你还没有阅读过关于 字符串替代引号 的章节,我建议你立即阅读。本章将涵盖一些关于 String 的非常酷的东西,并假设你已经了解这两章中的信息。

在 Ruby 中,有一些非常酷的内置函数与 String 相关。例如,你可以将它们相乘

"Danger, Will Robinson!" * 5

产生

Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!

String 也可以进行比较

"a" < "b"

产生

true

前面的计算实际上比较了字符的 ASCII 值。但是,你可能会问,给定字符的 ASCII 值是什么?在 1.9 之前的 Ruby 版本中,你可以使用以下方法查找字符的 ASCII 值

puts ?A

但是,在 Ruby 1.9 或更高版本中,这不再有效。相反,你可以尝试使用 String.ord 方法

puts "A".ord

无论哪种方法都会显示

65

这是A的 ASCII 值。只需将A替换为你想查询的任何字符。

要执行相反的转换(从65A,例如),使用 Integer.chr 方法

puts 65.chr

显示

A

连接的工作方式与大多数其他语言相同:在两个 String 之间放置一个 + 字符将产生一个新的 String,其值与其他值相同,一个接一个。

"Hi, this is " + "a concatenated string!"

产生

Hi, this is a concatenated string!

要处理讨厌的 String 变量而不使用连接运算符,你可以使用插值。在以下代码块中,string1string2string3 是相同的

thing1 = "Red fish, "
thing2 = "blue fish."
string1 = thing1 + thing2 + " And so on and so forth."
string2 = "#{thing1 + thing2} And so on and so forth."
string3 = "#{thing1}#{thing2} And so on and so forth."

如果你需要遍历(即,逐步浏览)String 对象中的每个字母,可以使用 String.scan 方法

thing = "Red fish"
thing.scan(/./) {|letter| puts letter}

显示 thing 中的每个字母(puts 会在每次调用后自动添加一个换行符)

R
e
d
 
f
i
s
h

但是参数中的那个奇怪的 "/./" 是什么?朋友,那是所谓的 正则表达式。它们是有用的小东西,非常强大,但超出了本次讨论的范围。你现在只需要知道 /./ 是 "regex" 的说法,意思是 "任何一个字符"。如果我们使用的是 /../,那么 Ruby 将遍历每组两个字符,并且会错过最后一个字符,因为字符数量是奇数!

正则表达式的另一个用途可以在以下操作中找到=~运算符。你可以使用匹配运算符 =~ 检查 String 是否与正则表达式匹配

puts "Yeah, there's a number in this one." if "C3-P0, human-cyborg relations" =~ /[0-9]/

显示

Yeah, there's a number in this one.

String.match 方法的工作方式基本相同,除了它还可以接受一个 String 作为参数。如果你从代码外部获取正则表达式,这很有用。以下是它的实际应用

puts "Yep, they mentioned Jabba in this one." if "Jabba the Hutt".match("Jabba")

好了,关于正则表达式的介绍就到这里。即使你可以在接下来的两个示例中使用正则表达式,我们也将只使用普通的旧 String。假设你在真理部工作,你需要用另一个词替换 String 中的一个词。你可以尝试以下操作

string1 = "2 + 2 = 4"
string2 = string1.sub("4", "5")

现在 string2 包含 2 + 2 = 5。但是,如果 String 包含很多像你刚刚修正的谎言一样的谎言呢?String.sub 只替换第一个出现的词!我想你可以使用 String.match 方法和 while 循环来遍历 String,但有一种更有效的方法可以实现这一点

winston = %q{   Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!}
winston.gsub("Down with", "Long live")

老大哥会非常高兴!String.gsub 是 "全局替代" 函数。所有出现 "Down with" 的地方现在都被替换成了 "Long live" 所以现在winston只宣称它对老大哥的爱,而不是对老大哥的厌恶。
在这个快乐的音符上,让我们继续讨论 IntegerFloat。如果你想了解更多关于 String 类中的方法的信息,请查看本章末尾的快速参考表。

数字(整数和浮点数)

[edit | edit source]

如果你了解所有标准的数字运算符,你可以跳过本段。对于那些不了解的人,这里有一个速成课程。+将两个数字加在一起。-将它们相减。/除。*乘。%返回两个除数的余数。

好了,整数是没有小数点的数字。浮点数是有小数点的数字。10 / 3产生3因为将两个整数相除会产生一个整数。由于整数没有小数点,你得到的只是3。如果你尝试10.0 / 3你将得到3.33333...如果你在混合中有一个浮点数,你将得到一个浮点数。明白了吗?

好了,让我们进入有趣的部分。Ruby 中的一切都是对象,让我重申一遍。这意味着几乎所有东西至少都有一种方法。整数和浮点数也不例外。首先我会展示一些整数方法。
这里我们有久负盛名的times方法。无论何时你想做某事不止一次,都可以使用它。示例

puts "I will now count to 99..."
100.times {|number| puts number}
5.times {puts "Guess what?"}
puts "I'm done!"

这将打印出数字 0 到 99,打印出猜猜看?五次,然后说我完成了!它基本上是一个简化的for循环。它比一个for循环慢几百分之一秒;如果你要为 NASA 编写 Ruby 代码,请记住这一点。;-)

好了,我们快完成了,还有六种方法要介绍。以下是其中的三种

# First a visit from The Count...
1.upto(10) {|number| puts "#{number} Ruby loops, ah-ah-ah!"}

# Then a quick stop at NASA...
puts "T-minus..."
10.downto(1) {|x| puts x}
puts "Blast-off!"

# Finally we'll settle down with an obscure Schoolhouse Rock video...
5.step(50, 5) {|x| puts x}

好了,这应该是有道理的。如果你不明白,upto从它被调用的数字开始,一直计数到它的参数中传递的数字。downto做同样的事情,只是它向下计数而不是向上计数。最后,step从它被调用的数字开始,一直计数到它参数中的第一个数字,每次计数增加它参数中的第二个数字。所以5.step(25, 5) {|x| puts x}将输出从五开始,以二十五结束的每个五的倍数。

最后三种方法

string1 = 451.to_s
string2 = 98.6.to_s
int = 4.5.to_i
float = 5.to_f

to_s将浮点数和整数转换为字符串。to_i将浮点数转换为整数。to_f将整数转换为浮点数。这就是 Ruby 中所有数据类型的概况。现在,这里是我向你承诺的字符串方法的快速参考表。

其他字符串方法

[edit | edit source]
# Outputs 1585761545
"Mary J".hash

# Outputs "concatenate"
"concat" + "enate"

# Outputs "Washington"
"washington".capitalize

# Outputs "uppercase"
"UPPERCASE".downcase

# Outputs "LOWERCASE"
"lowercase".upcase

# Outputs "Henry VII"
"Henry VIII".chop

# Outputs "rorriM"
"Mirror".reverse

# Outputs 810
"All Fears".sum

# Outputs cRaZyWaTeRs
"CrAzYwAtErS".swapcase

# Outputs "Nexu" (next advances the word up one value, as if it were a number.)
"Next".next

# After this, nxt == "Neyn" (to help you understand the trippiness of next)
nxt = "Next"
20.times {nxt = nxt.next}
华夏公益教科书