💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 数据类型 Ruby核心库(core,不包括标准库)的数据类型分为: - 数字(Numeric -> 包含了Integer/Float子类, Integer又包含了Fixnum和Bignum子类) - 字符串(String) - 符号 (Symbol) - 数组 (Array) - 哈希 (Hash) - 范围 (Range) ### 数字Numeric 数字,分为很多种,整数、小数。 Ruby中数字类型很简单: ~~~ 1.class #=> Fixnum 1.class.superclass #=> Integer 1.class.superclass.superclass #=> Numeric ~~~ 一个数字字面量,即为一个数字类型,ruby中一切皆对象,所以这个数字字面量也可以响应一个叫class的方法,来返回它自己的类别,是Fixnum。 superclass方法,则返回一个类的父类。 Ruby允许我们像上面代码那样,连续调用,「返回的值」即为下一次调用的「消息接收者」,当然你得保证返回的值是可以响应那个消息的。 还有小数: ~~~ 0.1.class #=> Float 0.1.class.superclass #=> Numeric ~~~ 还有比较大的整数呢。 ~~~ (2**200).class #=> Bignum (2**200).class.superclass #=> Integer # 当值很大的时候 (2**2000000000) #=> 返回一个Infinity,代表无穷大 (2**2000000000).class #=> Float ~~~ 结论: Ruby中整数的值在一个小范围的时候,是Fixnum类型, 当数值很大的时候,则变成了Bignum类型,太大的值会返回一个固定的值Infinity,代表无穷大。而这个Infinity是一个Float对象。 Ruby中的小数, 是一个Float类型。 Float类型在Ruby中算是一个缺陷,因为它在计算过程中会产生误差,会带来一些bug。实际应用中都不会去使用这个数字类型。我们一般用Ruby标准库中的BigDecimal类型。 ### BigDecimal 示例: ~~~ require 'bigdecimal' sum = BigDecimal.new("0") 10_000.times do sum = sum + BigDecimal.new("0.0001") end print sum #=> 0.1E1 ~~~ 因为是Ruby标准库,使用的时候一定要require那个库。 ### 字符串(String) Ruby的字符串,分为两种。一种是双引号包含的东西,一种是单引号所包含的东西。 ~~~ str = " hello world " str = ' hello world ' ~~~ 他们的区别,就是单引号的字符串,基本是原样输出,只能识别'\''这样形式的转义符。而双引号则完全识别转义符。 ~~~ str = '\'' #=> "'" str = "\'" #=> "'" str = 'hello \n \t world!' #=> "hello \\n \\t world!" str = "hello \n \t world!" #=> "hello \n \t world!" ~~~ ### #{} 使用#{}可以把一个变量,「镶嵌」到一个字符串里面: ~~~ hello = "hi" str = " #{hello} world " #=> "hi world" str = '#{hello} world' #=> "\#{hello} world" ~~~ 通过上面的代码,也可以看出来单引号字符串和双引号字符串之间的差别,就是单引号字符串,无法识别#{}这个操作符。 ### %q / %Q 操作符 对于一些比较复杂的字符串,像: ~~~ "#{name} said: \"Clap your hands!\"" ~~~ 这种字符串里面,双引号也作为了字符串的内容, 影响了可读性。 那么Ruby就提供了一个%Q操作符来帮助我们解决这样的问题: ~~~ name = 'Alex' %Q|#{name} says: "Try ftp://ruby-lang.org/pub/ruby/1.9/"| %Q-#{name} says: "Clap your hands!"- %Q/#{name} says: "Play tic-tac-toe!"/ # 省略 Q %-#{@name} says: "Clap your hands!"- %/#{name} says: "Play tic-tac-toe!"/ %[#{name} says: "Play tic-tac-toe!"] ~~~ %Q后面可以跟任何一对对称的符号,只要是对称就可以。 %Q代表了双引号,当%后面省略了Q,也是一样的。 而%q, 则是代表单引号. ~~~ name = 'Lee' %q[#{name} says: "Play tic-tac-toe!"] #=> "\#{name} says: \"Play tic-tac-toe!\"" ~~~ ### 符号 (Symbol) 符号类型,在Ruby中用一个冒号加名字或者是字符串来表示。 ~~~ :name :_name :"name" ~~~ 你不能使用冒号和数字来声明一个符号型,否则会报错。 ~~~ :1 #=> SyntaxError: unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END ~~~ ### 符号是什么 在Ruby中,符号表示一个「名字」。它有点类似于字符串,但和字符串又有不同。在Ruby中,一切皆对象,符号类型,也是对象,每个对象都有一个唯一的对象标识符object_id, 对象标识符一样,就代表是同一个对象,否则就是不同的对象。我们来比较下符号和字符串。 ~~~ :name.object_id #=> 66088 :name.object_id #=> 66088 :name.object_id #=> 66088 "name".object_id #=> 10285860 "name".object_id #=> 10058700 "name".object_id #=> 8904020 ~~~ 从上面的代码里可以看出, 连续三个:name,都是指同一个Symbol对象,而连续三个"name",则是三个不同的对象。 所以,Symbol对象是一种具有唯一性的类「字符串」, 因为Symbol也拥有字符串的一些行为。比如: ~~~ :name.upcase #=> :NAME "name".upcase #=> 'Name' ~~~ 当然,不是全部的字符串方法它都可以响应,你可以去ruby-doc.org去比较他们的方法。 Symbol对象,从创建开始,一直到程序退出运行,都是被存放到一个叫做符号表的地方, Ruby的垃圾回收(GC)不会清理这些符号对象。而字符串不一样, GC会清理掉那些无用的字符串对象。 在认识到符号的这些特性之后, 应该不难理解我们把符号类型用来表示一个「名字」的概念。 举个例子: 有三个双胞胎字符串对象:'name', 'name', 'name', 我们可以用符号类型来形容他们的长相, 都是:name。 ### 数组 (Array) Ruby中的数组,是一个任何对象的有序的、用整数来索引的集合。 ~~~ arr = [1, 2, :a, 'name', nil] arr = Array[1, 2, 3, :name] arr.class #=> Array arr[0] #=> 1 arr[1] #=> 2 #... ~~~ Ruby的数组,可以存放任何对象。 ~~~ arr = [1, 2, 3, [4, 5, 6]] arr[0] #=> 1 arr[3][0] #=> 4 ~~~ 上面,我们定义了一个二维数组,通过[]方法,传入索引参数,可以取得数组的值。 数组的概念很容易理解。可以结合ruby-doc.org来查看并且练习数组中内建的很多方法。 ### 哈希 (Hash) Ruby中的哈希,是一个键值对的集合。在Ruby1.8中是无序的,但是在Ruby1.9开始,哈希变成有序的了。 ~~~ h = {a: 100, b: 200} h = Hash["a" => 1000, "b" => 2000] h[:a] #=> 100 h['a'] #=> 1000 ~~~ Hash是在{}中被包含的字面量,也可以通过Hash[]方法来创建一个hash,参考上面的代码。我们可以通过一个key来获取其所对应的值。 Hash的key,必须是唯一的。 ~~~ h = {a: 1, b: 2, a: 3} h[:a] #=> 3 ~~~ key必须是唯一,所以上面的代码中,h[:a]取出的值是3. 我们提倡使用符号类型来作为Hash的key,这也是Ruby1.9开始引入下面的hash结构的原因之一: ~~~ h = {a: 1, b: 2} #等同于 h = {:a => 1, :b => 2} ~~~ 但也不是所有的情况都试用于上面第一种写法,比如你必须要以数字作为key,就只能用第二种形式。但是大部分情况,第一种写法已经够用了。 使用字符类型做为key,可以节省内存。因为上面我们讲过,字符串和符号的区别,就是每个字符串都是不同的对象,而Ruby中的对象是占用内存的,所以我们尽量让其少生成点对象。这也是Ruby鼓励我们在Hash中使用字符类型作为key的原因。 同样,Hash有很多内建方法,可以去ruby-doc.org中自行查看并且练习。 ### 范围 (Range) 范围(Range)发生无处不在:一月至十二月,0到9,50至67行,依此类推。 (1..3) 和 (1...3) 都表示一个范围。 ~~~ (1..3).class #=> Range (1...3).class #=> Range ~~~ 范围有一个起点,一个终点,产生连续的序列值。如果是两个点把起点和终点相连,则范围的连续值包含终点值。如果是三个点,则范围的连续值不包含终点值。 可以参考我们学过的数学中的开闭区间来理解这个概念。 ~~~ (1..3).each{|i| puts i} #=> 1 2 3 (1...3).each{|i| puts i} #=> 1 2 ~~~ 我们使用each方法来迭代范围中的值,可以看得出两个点和三个点范围类型的区别。 ### 结语 Ruby中的主要数据类型大概就讲这么多,Ruby还有其他的数据类型,比如时间日期, 表示真假的Boolean型,甚至标准库中所包含的数据类型,这些大家可以参考ruby-doc.org去自行学习。