Ruby核心模块与类的深度解析
1. Marshal模块
Marshal模块在Ruby中扮演着重要角色,它能将Ruby对象集合转换为字节流,方便将对象存储在当前活动脚本之外,后续还能读取这些数据并重构原始对象。
1.1 版本信息
Marshaled数据会存储主版本号和次版本号,与对象信息一同保存。一般情况下,Marshal只能加载主版本号相同,次版本号相等或更低的数据。若设置了Ruby的“verbose”标志(通常使用 -d、-v、-w 或 –verbose),主版本号和次版本号必须完全匹配。Marshal的版本号与Ruby的版本号相互独立,可通过读取Marshaled数据的前两个字节来提取版本信息。示例代码如下:
str = Marshal.dump("thing")
RUBY_VERSION
# → "1.8.2"
str[0]
# → 4
str[1]
# → 8
1.2 不可转储的对象
某些对象无法进行转储,若要转储的对象包含绑定、过程或方法对象、IO类的实例、单例对象,或者尝试转储匿名类或模块,会引发TypeError异常。
1.3 自定义序列化
若类有特殊的序列化需求,或者包含原本不可序列化的对象,可以实现自己的序列化策略。在Ruby 1.8之前,需定义 _dump 和 _load 方法。而Ruby 1.8引入了更灵活的接口,使用实例方法 marshal_dump 和 marshal_load。若要进行编组的对象能响应 marshal_dump 方法,会调用该方法而非 _dump。marshal_dump 可以返回任何类的对象,实现 marshal_dump 的类也必须实现 marshal_load。示例代码如下:
class TimedDump
attr_reader :when_dumped
def marshal_dump
Time.now
end
def marshal_load(when_dumped)
@when_dumped = when_dumped.strftime("%I:%M%p")
end
end
t = TimedDump.new
t.when_dumped
# → nil
str = Marshal.dump(t)
newt = Marshal.load(str)
newt.when_dumped
# → "10:38PM"
1.4 模块常量和方法
- 常量 :
- MAJOR_VERSION:编组格式版本号的主要部分。
- MINOR_VERSION:编组格式版本号的次要部分。
- 方法 :
- dump(obj <, io >, limit=-1):序列化 obj 及其所有后代对象。若指定了 io,序列化数据将写入其中;否则,数据将作为字符串返回。若指定了 limit,子对象的遍历将限制在该深度;若 limit 为负数,则不进行深度检查。示例代码如下:
class Klass
def initialize(str)
@str = str
end
def say_hello
@str
end
end
o = Klass.new("hello\n")
data = Marshal.dump(o)
obj = Marshal.load(data)
obj.say_hello
# → "hello\n"
- load(from <, proc >):将 from 中的序列化数据转换为 Ruby 对象(可能包含相关的从属对象)。from 可以是 IO 实例或能响应 to_str 的对象。若指定了 proc,在反序列化每个对象时会将其传递给 proc。
- restore(from <, proc >):是 Marshal.load 的同义词。
2. MatchData类
MatchData类封装了模式匹配的所有结果,所有模式匹配都会将特殊变量 $~ 设置为一个 MatchData 对象,Regexp#match 和 Regexp#last_match 方法也会返回 MatchData 对象。
2.1 实例方法
- [ ] :MatchData 可像数组一样使用,通过正常的数组索引技术访问。match[0] 等同于特殊变量 $&,返回整个匹配的字符串;match[1]、match[2] 等返回匹配的反向引用值。示例代码如下:
m = /(.)(.)(\d+)(\d)/.match("THX1138.")
m[0]
# → "HX1138"
m[1, 2]
# → ["H", "X"]
m[1..3]
# → ["H", "X", "113"]
m[-3, 2]
# → ["X", "113"]
- begin(n) :返回匹配数组中第 n 个元素在字符串中的起始偏移量。
- captures :返回所有匹配组的数组,与 MatchData#to_a 不同,后者还会返回完整的匹配字符串。示例代码如下:
m = /(.)(.)(\d+)(\d)/.match("THX1138.")
m.captures
# → ["H", "X", "113", "8"]
f1, f2, f3 = /(.)(.)(\d+)(\d)/.match("THX1138.").captures
f1
# → "H"
f2
# → "X"
f3
# → "113"
- end(n) :返回匹配数组中第 n 个元素在字符串中结束位置的下一个字符的偏移量。
- length :返回匹配数组中的元素数量。
- offset(n) :返回一个包含第 n 个匹配的起始和结束偏移量的二元数组。
- post_match :返回原始字符串中当前匹配之后的部分,等同于特殊变量 $’。
- pre_match :返回原始字符串中当前匹配之前的部分,等同于特殊变量 $`。
- select {| val | block } :返回一个数组,包含所有使 block 为真的匹配元素。
- size :是 MatchData#length 的同义词。
- string :返回传递给 match 的字符串的冻结副本。
- to_a :返回匹配结果的数组,与 MatchData#captures 不同,会返回完整的匹配字符串。
- to_s :返回整个匹配的字符串。
- values_at( *) :使用每个索引访问匹配值,返回相应匹配结果的数组。
3. Math模块
Math模块包含用于基本三角函数和超越函数的模块方法。
3.1 模块常量
- E:自然对数的底数 e 的值。
- PI:圆周率 π 的值。
3.2 模块方法
方法名 | 描述 |
---|---|
acos(x) | 计算 x 的反余弦值,返回范围为 0..π。 |
acosh(x) | 计算 x 的反双曲余弦值。 |
asin(x) | 计算 x 的反正弦值,返回范围为 0..π。 |
asinh(x) | 计算 x 的反双曲正弦值。 |
atan(x) | 计算 x 的反正切值,返回范围为 -π/2..π/2。 |
atanh(x) | 计算 x 的反双曲正切值。 |
atan2(y, x) | 根据 y 和 x 计算反正切值,返回范围为 -π..π。 |
cos(x) | 计算 x 的余弦值(以弧度表示),返回范围为 -1..1。 |
cosh(x) | 计算 x 的双曲余弦值(以弧度表示)。 |
erf(x) | 返回 x 的误差函数值。 |
erfc(x) | 返回 x 的互补误差函数值。 |
exp(x) | 返回 e 的 x 次幂。 |
frexp(numeric) | 返回一个二元数组,包含 numeric 的归一化分数(Float 类型)和指数(Fixnum 类型)。 |
hypot(x, y) | 返回直角三角形斜边的长度,即 √(x² + y²)。 |
ldexp(float, integer) | 返回 float × 2^integer 的值。 |
log(numeric) | 返回 numeric 的自然对数。 |
log10(numeric) | 返回 numeric 以 10 为底的对数。 |
sin(numeric) | 计算 numeric 的正弦值(以弧度表示),返回范围为 -1..1。 |
sinh(numeric) | 计算 numeric 的双曲正弦值(以弧度表示)。 |
sqrt(numeric) | 返回 numeric 的非负平方根,若 numeric 小于零,会引发 ArgError 异常。 |
tan(numeric) | 计算 numeric 的正切值(以弧度表示)。 |
tanh(numeric) | 计算 numeric 的双曲正切值(以弧度表示)。 |
4. Method类
Method对象由 Object#method 创建,与特定对象相关联,可用于在对象内调用方法,也可作为与迭代器关联的块。还可以从一个对象解绑(创建 UnboundMethod)并绑定到另一个对象。
4.1 实例方法
- [ ] :是 Method.call 的同义词。
- == :若 meth 与 other 是同一个方法,返回 true。示例代码如下:
def fred()
puts "Hello"
end
alias bert fred
# → nil
m1 = method(:fred)
m2 = method(:bert)
m1 == m2
# → true
- arity :返回方法接受的参数数量的指示。对于接受固定数量参数的方法,返回非负整数;对于接受可变数量参数的 Ruby 方法,返回 -n - 1,其中 n 是所需参数的数量;对于用 C 编写的方法,若调用接受可变数量的参数,返回 -1。示例代码如下:
class C
def one; end
def two(a); end
def three(*a); end
def four(a, b); end
def five(a, b, *c); end
def six(a, b, *c, &d); end
end
c = C.new
c.method(:one).arity
# → 0
c.method(:two).arity
# → 1
c.method(:three).arity
# → -1
c.method(:four).arity
# → 2
c.method(:five).arity
# → -3
c.method(:six).arity
# → -3
"cat".method(:size).arity
# → 0
"cat".method(:replace).arity
# → 1
"cat".method(:squeeze).arity
# → -1
"cat".method(:count).arity
# → -1
- call :使用指定的参数调用 meth,返回方法的返回值。
- eql?(other) :若 meth 与 other 是同一个方法,返回 true。
- to_proc :返回与该方法对应的 Proc 对象,可用于传递块参数。
- unbind :将 meth 与其当前接收器分离,得到的 UnboundMethod 可随后绑定到同一类的新对象。
5. Module类
Module是方法和常量的集合,模块中的方法可以是实例方法或模块方法。
5.1 类方法
- constants :返回系统中定义的所有常量的名称数组,包括所有模块和类的名称。
- nesting :返回调用点嵌套的模块列表。
- new :创建一个新的匿名模块。若提供了块,会将模块对象传递给块,并使用 module_eval 在该模块的上下文中评估块。示例代码如下:
Fred = Module.new do
def meth1
"hello"
end
def meth2
"bye"
end
end
a = "my string"
a.extend(Fred)
# → "my string"
a.meth1
# → "hello"
a.meth2
# → "bye"
5.2 实例方法
- <, <=, >, >= :用于查询模块层次结构,一个模块被认为比另一个模块大,若它包含在(或为另一个模块的父类)另一个模块中。若模块之间没有关系,所有运算符都返回 false。
- <=> :比较两个模块,若 mod 包含 other_mod,返回 -1;若 mod 与 other_mod 相同,返回 0;若 mod 被 other_mod 包含或与 other_mod 没有关系,返回 1。
- === :用于 case 语句中,判断 obj 是否是 mod 或其后代的实例。
- ancestors :返回包含在 mod 中的模块列表(包括 mod 本身)。
- autoload(name, file_name) :注册在 mod 的命名空间中首次访问模块 name(可以是字符串或符号)时加载 file_name(使用 Kernel.require)。
- autoload?(name) :返回在 mod 的上下文中引用字符串或符号 name 时将自动加载的文件名,若没有关联的自动加载,返回 nil。
- class_eval :是 Module.module_eval 的同义词。
- class_variables :返回 mod 及其祖先中类变量的名称数组。
- clone :创建模块的新副本。
- const_defined?(symbol) :判断 mod 中是否定义了给定名称的常量。
- const_get(symbol) :返回 mod 中指定名称的常量的值。
- const_missing(symbol) :当引用 mod 中未定义的常量时调用,传递未定义常量的符号并返回用于该常量的值。
- const_set(symbol, obj) :将指定名称的常量设置为给定对象,若之前不存在该常量,则创建一个新常量。
- constants :返回 mod 中可访问的常量名称的数组,包括任何包含模块中的常量名称。
- include?(other_mod) :判断 other_mod 是否包含在 mod 或其祖先中。
- included_modules :返回包含在 mod 中的模块列表。
- instance_method(symbol) :返回表示 mod 中给定实例方法的 UnboundMethod。示例代码如下:
class Interpreter
def do_a() print "there, "; end
def do_d() print "Hello "; end
def do_e() print "!\n"; end
def do_v() print "Dave"; end
Dispatcher = {
?a => instance_method(:do_a),
?d => instance_method(:do_d),
?e => instance_method(:do_e),
?v => instance_method(:do_v)
}
def interpret(string)
string.each_byte {|b| Dispatcher[b].bind(self).call }
end
end
interpreter = Interpreter.new
interpreter.interpret('dave')
# 输出: Hello there, Dave!
- instance_methods(inc_super=true) :返回接收器中公共实例方法的名称数组。对于模块,是公共方法;对于类,是实例(非单例)方法。若没有参数或参数为 true,返回 mod 及其超类中的方法;若参数为 false,返回 mod 中的实例方法。
- method_defined?(symbol) :判断 mod 中是否定义了指定名称的方法(包括其包含的模块和祖先,若 mod 是类),匹配公共和受保护的方法。
- module_eval :在 mod 的上下文中评估字符串或块,可用于向类添加方法。
- name :返回模块的名称。
-
private_class_method(
+) :将现有类方法设置为私有,常用于隐藏默认构造函数 new。 - private_instance_methods(inc_super=true) :返回 mod 中定义的私有实例方法列表,若可选参数为 true,包括任何祖先的方法。
- private_method_defined?(symbol) :判断 mod 中是否定义了指定名称的私有方法(包括其包含的模块和祖先,若 mod 是类)。
- protected_instance_methods(inc_super=true) :返回 mod 中定义的受保护实例方法列表,若可选参数为 true,包括任何祖先的方法。
- protected_method_defined?(symbol) :判断 mod 中是否定义了指定名称的受保护方法(包括其包含的模块和祖先,若 mod 是类)。
-
public_class_method(
+) :将现有类方法设置为公共。 - public_instance_methods(inc_super=true) :返回 mod 中定义的公共实例方法列表,若可选参数为 true,包括任何祖先的方法。
- public_method_defined?(symbol) :判断 mod 中是否定义了指定名称的公共方法(包括其包含的模块和祖先,若 mod 是类)。
5.3 私有实例方法
- alias_method(new_id, old_id) :创建 old_id 方法的新副本 new_id,可用于保留对被重写方法的访问。
- append_features(other_mod) :当该模块包含在另一个模块中时,Ruby 会调用此方法,将该模块的常量、方法和模块变量添加到 other_mod 中。
- attr(symbol, writable=false) :为模块定义一个命名属性,创建实例变量 (@name) 和相应的访问方法来读取它。若可选参数 writable 为 true,还会创建一个名为 name= 的方法来设置属性。
-
attr_accessor(
+) :等同于依次调用 “attr symbol, true”。 -
attr_reader(
+) :创建实例变量和相应的方法来返回每个实例变量的值。 -
attr_writer(
+) :创建一个访问器方法,允许对属性 symbol.id2name 进行赋值。 - define_method(symbol, method) :在接收器中定义一个实例方法,方法参数可以是 Proc 或 Method 对象。若指定了块,将其用作方法体。
- extend_object(obj) :通过添加该模块的常量和方法(作为单例方法)来扩展指定的对象。
- extended(other_mod) :当接收器用于扩展对象时调用的回调。
- include( +) :调用 Module.append_features 对每个参数(按相反顺序)进行操作。
- included(other_mod) :当接收器包含在另一个模块或类中时调用的回调。
- method_added(symbol) :每当向接收器添加方法时作为回调调用。
- method_removed(symbol) :每当从接收器中移除方法时作为回调调用。
- method_undefined(symbol) :每当在接收器中取消定义方法时作为回调调用。
-
module_function(
*) :为指定的方法创建模块函数,这些函数可以以模块作为接收器调用,并且作为实例方法提供给混入该模块的类。 -
private(
*) :无参数时,将后续定义的方法的默认可见性设置为私有;有参数时,将指定的方法设置为私有可见性。 -
protected(
*) :无参数时,将后续定义的方法的默认可见性设置为受保护;有参数时,将指定的方法设置为受保护可见性。 -
public(
*) :无参数时,将后续定义的方法的默认可见性设置为公共;有参数时,将指定的方法设置为公共可见性。 - remove_class_variable(symbol) :移除符号的定义,返回该常量的值。
- remove_const(symbol) :移除指定常量的定义,返回该常量的值。
- remove_method(symbol) :从当前类中移除由 symbol 标识的方法。
-
undef_method(
+) :防止当前类响应调用指定的方法,与 remove_method 不同,Ruby 不会再搜索超类和混入模块中可能的接收器。
综上所述,Ruby中的这些核心模块和类为开发者提供了丰富的功能和灵活的编程方式,深入理解它们的使用方法和特性,能帮助开发者更高效地编写代码。
Ruby核心模块与类的深度解析(续)
6. 各模块与类的应用场景分析
6.1 Marshal模块的应用
Marshal模块主要用于对象的序列化和反序列化,在需要将对象持久化存储或在不同进程、网络之间传输对象时非常有用。以下是一个简单的应用场景示例:
场景 :将一个复杂的对象保存到文件中,后续再从文件中恢复该对象。
操作步骤
:
1. 定义一个包含复杂数据的类:
class ComplexData
def initialize(data)
@data = data
end
def show_data
@data
end
end
- 创建对象并使用Marshal.dump将其序列化到文件:
complex_obj = ComplexData.new([1, 2, 3, { key: 'value' }])
file = File.open('complex_data.dat', 'wb')
Marshal.dump(complex_obj, file)
file.close
- 从文件中读取数据并使用Marshal.load反序列化恢复对象:
file = File.open('complex_data.dat', 'rb')
restored_obj = Marshal.load(file)
file.close
puts restored_obj.show_data
6.2 MatchData类的应用
MatchData类在处理正则表达式匹配结果时非常有用,常用于字符串的解析和提取。
场景 :从一个字符串中提取日期信息。
操作步骤
:
1. 定义一个包含日期的字符串和对应的正则表达式:
text = "The event will happen on 2024-10-01."
regex = /(\d{4})-(\d{2})-(\d{2})/
- 使用正则表达式进行匹配并获取MatchData对象:
match = regex.match(text)
- 从MatchData对象中提取日期信息:
year = match[1]
month = match[2]
day = match[3]
puts "Year: #{year}, Month: #{month}, Day: #{day}"
6.3 Math模块的应用
Math模块提供了各种数学函数,在科学计算、图形处理等领域有广泛应用。
场景 :计算一个直角三角形的斜边长度。
操作步骤
:
1. 定义直角三角形的两条直角边:
side_a = 3
side_b = 4
- 使用Math.hypot函数计算斜边长度:
hypotenuse = Math.hypot(side_a, side_b)
puts "The length of the hypotenuse is: #{hypotenuse}"
6.4 Method类的应用
Method类可以动态调用对象的方法,在需要根据不同条件调用不同方法时非常有用。
场景 :根据用户输入调用不同的方法。
操作步骤
:
1. 定义一个包含多个方法的类:
class Calculator
def add(a, b)
a + b
end
def subtract(a, b)
a - b
end
end
- 创建对象并根据用户输入动态调用方法:
calc = Calculator.new
operation = gets.chomp
method = calc.method(operation)
result = method.call(5, 3)
puts "The result is: #{result}"
6.5 Module类的应用
Module类用于组织代码,实现代码的复用和模块化。
场景 :创建一个可复用的日志模块。
操作步骤
:
1. 定义日志模块:
module LoggerModule
def log(message)
puts "[LOG] #{message}"
end
end
- 在类中混入日志模块:
class MyClass
include LoggerModule
def do_something
log("Doing something...")
end
end
- 创建对象并调用方法:
obj = MyClass.new
obj.do_something
7. 各模块与类的关系和交互
这些模块和类之间存在着一定的关系和交互,形成了Ruby强大的编程生态。
7.1 Marshal与其他类的交互
Marshal模块可以对各种类的对象进行序列化和反序列化,只要这些对象不包含不可序列化的元素。例如,对于自定义的类对象,Marshal可以将其转换为字节流进行存储或传输,后续再恢复为原始对象。
7.2 MatchData与String、Regexp的交互
MatchData类是由Regexp对象的匹配操作产生的,它封装了正则表达式在字符串上的匹配结果。通过MatchData对象,可以方便地从字符串中提取所需的信息。
7.3 Math模块与数值类的交互
Math模块提供的数学函数主要作用于数值类对象,如Integer、Float等。这些函数可以对数值进行各种数学运算,扩展了Ruby的数值处理能力。
7.4 Method类与Object的交互
Method对象是由Object的method方法创建的,它与特定的对象相关联,可以在该对象的上下文中调用方法。通过Method对象,可以实现方法的动态调用和传递。
7.5 Module类与Class的交互
Module类是Class的父类,Module中的方法和常量可以通过include或extend的方式被类使用。通过这种方式,实现了代码的复用和模块化,提高了代码的可维护性和可扩展性。
8. 总结与展望
通过对Ruby中Marshal、MatchData、Math、Method和Module等核心模块与类的深入分析,我们了解到它们各自的功能、特性和应用场景。这些模块和类为Ruby开发者提供了丰富的工具和灵活的编程方式,使得开发者能够更高效地处理各种编程任务。
在未来的开发中,随着Ruby的不断发展,这些核心模块和类可能会进一步优化和扩展。例如,Marshal模块可能会支持更多类型的对象序列化,MatchData类可能会提供更便捷的方法来处理复杂的匹配结果。同时,开发者也可以基于这些核心模块和类,开发出更多实用的库和框架,推动Ruby生态的不断繁荣。
总之,深入学习和掌握这些核心模块和类的使用方法,对于提高Ruby编程水平和开发效率具有重要意义。希望开发者们能够充分利用这些资源,创造出更加优秀的Ruby应用程序。
以下是一个简单的mermaid流程图,展示了Marshal模块的序列化和反序列化过程:
graph LR
A[创建对象] --> B[Marshal.dump序列化对象]
B --> C[存储到文件或传输]
C --> D[读取数据]
D --> E[Marshal.load反序列化对象]
E --> F[恢复为原始对象]
通过这个流程图,可以更直观地理解Marshal模块的工作原理和应用流程。