## 25.元编程
元编程是使程序编写程序的艺术。 也就是说,在运行时,程序可以根据情况进行自我修改。 让我们在本章中了解一下。
### 25.1。 发送
Ruby 有一个功能强大的方法`send`。 也就是说,如果对象`p`具有方法`name`,则在 ruby 中我们可以使用`p.name`对其进行调用,或者还有另一种方法来调用它。 我们用`p.send(:name)`或`p.send("name")`称呼它。 那么,这有什么用? 这样做的用途是,你可以从用户输入或收到的其他输入中确定要调用的函数。
让我们看一个基本的例子。 在文本编辑器中键入以下程序 [send.rb](code/send.rb) 并运行它。
```rb
class Person
attr_accessor :name
def speak
"Hello I am #{@name}"
end
end
p = Person.new
p.name = "Karthik"
puts p.send(:speak)
```
输出量
```rb
Hello I am Karthik
```
好的,正如你在突出显示的代码 p.send(:speak)中所看到的那样,我们正在使用 send 方法调用 Person 类的实例 p 的语音功能。 现在就发送。 兴奋起来!!! 告知你正在学习元编程,并开始向同事吹牛。
好吧,希望你吹牛。 现在让我们来看一下`send`函数的一些更实际的例子。 输入示例 [send_1.rb](code/send_1.rb) 并执行
```rb
# send_1.rb
class Student
attr_accessor :name, :math, :science, :other
end
s = Student.new
s.name = "Zigor"
s.math = 100
s.science = 100
s.other = 0
print "Enter the subject who's mark you want to know: "
subject = gets.chop
puts "The mark in #{subject} is #{s.send(subject)}"
```
Output
```rb
Enter the subject who's mark you want to know: math
The mark in math is 100
```
因此,在该计划中,我们有一个名为`Student`的类,我们创建了一个学生,该学生在数学,科学和其他科目上的分数分别为 100、100 和零。 我们要求用户在以下语句中输入需要知道其标记的主题,并将其放入名为主题的变量中
```rb
print "Enter the subject who's mark you want to know: "
subject = gets.chop
```
现在看到这一行:
```rb
puts "The mark in #{subject} is #{s.send(subject)}"
```
只需注意`s.send(subject)`部分,我们就在这里,而不是使用案例或其他 if 或条件来检查主题是什么,然后根据其调用适当的方法,我们只需将用户输入传递给`s.send`,然后它将调用适当的方法 并返回值。
你没看到魔法吗?
### 25.2。 方法缺失
假设你有一个仅包含某些方法的类,并且如果程序员调用了其他疯狂的方法,并且想要捕获它并查看它是否仍然可以使用,则可以使用`method_missing`方法捕获该方法 和其他东西。
让我们看一下有关方法缺失的程序。 在测试编辑器中键入程序 [method_missing.rb](code/method_missing.rb) 并执行
```rb
# method_missing.rb
class Something
def method_missing method, *args, &block
puts "You called: #{method}"
p args
block.call 7
end
end
s = Something.new
s.call_method "boo", 5 do |x|
x.times{ puts "in block" }
end
```
Output
```rb
You called: call_method
["boo", 5]
in block
in block
in block
in block
in block
in block
in block
```
让我们看看该程序的工作原理,在`s = Something.new`行中,我们创建了`Something`类的新实例变量`s`。 然后在下一行中执行`s.call_method "boo", 5`,在此行中,我们在`s`上调用一个名为`call_method`的方法,如果查看类`Something`,你会注意到在其中没有称为 call_method 的方法或函数 它,但程序不会抛出错误,异常或其他任何信息。
好吧,发生了什么事? 如果你注意到 Something 类,你将看到一个名为 method_missing 的方法,该方法已实现,如下所示
```rb
def method_missing method, *args, &block
puts "You called: #{method}"
p args
block.call 7
end
```
此方法接受三个参数,在我们的代码中,我们已将这些参数命名为`method`,`*args`和`&block`。 该方法接受方法名称,该名称是在对象`s`上调用的方法的名称,`*args`具有传递给该方法的属性,在本例中,其`call_method`和传递的属性为`“boo”` 和`5`。 `&block`接受已传递的任何块。 如果你看到下面的`s`,我们将其称为`call_method`
```rb
s.call_method "boo", 5 do |x|
x.times{ puts "in block" }
end
```
我们正在将一个块传递给`call_method`和`end`包围的`call_method`函数。 在该块内部,我们使用变量`x`并对其进行一些操作。 `&block`捕获了整个块。
最后,我们打印使用语句`p args`传递的参数(请注意,此处未使用`*args`),并使用`block.call 7`调用了该块(请注意,此处使用了块而不是&块)。 method_missing 定义。 值`7`被传递到块中的变量`x`。
现在让我们看看如何使用缺少方法。 假设我们有一个名为`Person`的类,它具有两个名为`name`和`age`的属性,请参见下面的代码并执行它
```rb
# method_missing_in_action.rb
class Person
attr_accessor :name, :age
def initialize name, age
@name, @age = name, age
end
def method_missing method_name
method_name.to_s.match(/get_(\w+)/)
eval("self.#{$1}")
end
end
person = Person.new "Zigor", "67893"
puts "#{person.get_name} is #{person.get_age} years old"
```
Output
```rb
Zigor is 67893 years old
```
在上面的代码中,看到突出显示的行`puts "#{person.get_name} is #{person.get_age} years old"`,我们称其属性与 person.name 和`person.age`不同,而是使用`person.get_name`和`person.get_age`。 现在`Person`类中没有`get_name`和`get_age`方法,而是代码在这里结束
```rb
def method_missing method_name
method_name.to_s.match(/get_(\w+)/)
eval("self.#{$1}")
end
```
在方法中缺少方法。 看一下代码,在`method_name.to_s.match(/get_(\w+)/)`这一行中,我们提取与`get_`关联的任何方法,然后在此语句中将提取的术语称为`eval("self.#{$1}")`。 如果你无法理解这些内容,则可能必须阅读[正则表达式](#_regular_expressions)一章。
现在如何实际使用它,例如,你可以拥有一个`get_db_<method name>`,你可以在其中从数据库中获取值,或者说`store_db_<method name>(values….)`,你可以在其中捕获它并将其存储在数据库中。
### 25.3。 定义方法
我们将在本节中了解如何在类中定义方法。 在下面输入程序并执行
```rb
# define_method.rb
class Square
define_method :area do |side|
side * side
end
end
s = Square.new
puts s.area 5
```
Output
```rb
25
```
好的,所以输出为 25。 如果你注意到程序 [define_method.rb](code/define_method.rb) ,你会注意到,在上面的几行中,我们正在使用此尴尬的语句定义名为 area 的方法,如下所示
```rb
define_method :area do |side|
side * side
end
```
你可能会想到为什么我们不这样做:
```rb
def area side
side * side
end
```
是的,但是让我们考虑一下可以动态定义方法的情况。
```rb
# define_method_dynamic.rb
Book_hash = {author: "Zigor", title: "I Love Ruby", page: 95}
class Book
Book_hash.each do |key, value|
define_method key do
value
end
end
end
b = Book.new
puts "#{b.author} has written a book named #{b.title}"
```
Output
```rb
Zigor has written a book named I Love Ruby
```
因此,在上面的程序中,我们有两个突出显示的部分,第一个是`Book_hash = {author: "Zigor", title: "I Love Ruby", page: 95}`,在这里是分配给哈希值的常量。 在现实世界中,它可能是一个从文件动态加载一些哈希值的变量。 在课堂书中,你会看到以下几行:
```rb
Book_hash.each do |key, value|
define_method key do
value
end
end
```
我们在每个哈希值中取值,并使用其键定义方法,然后该方法返回该值。 因此,当我们说`b = Book.new`时,我们现在已经有名为`author`,`title`和`page`的函数,它们分别返回`“Zigor”`,`“I Love Ruby”`和`95`。
对于此语句,`puts "#{b.author} has written a book named #{b.title}"`进行了解释。
### 25.4。 等级评估
假设你有一个实例对象,你想在其类中添加一些内容,可以使用一种名为`class_eval`的方法,下面举一个例子。 在文本编辑器中键入以下程序并执行。
```rb
# class_eval.rb
class Animal
end
dog = Animal.new
dog.class.class_eval do
def say_something
"I am a dog"
end
end
pig = Animal.new
puts pig.say_something
```
Output
```rb
I am a dog
```
查看下面显示的代码。 因此,你具有变量`dog`,它是`Animal`的实例。 可以说,在程序中突然我们决定在`dog`的类中添加`say_something`的方法,即`Animal`,我们要做的就是在`class_eval`块中编写该方法,如下所示
```rb
dog.class.class_eval do
def say_something
"I am a dog"
end
end
```
在上面的程序中,我们使用`dog.class`获得`dog`的类,并对其进行调用`class_eval`,其后是`do` `end`块,在其中定义要附加到[[ `dog`。 在其中定义方法`say_something`。
现在让我们说我们还有另一个名为`pig`的变量,它是`Animal`的实例,我们称之为`pig.say_something`,它也响应! 因此,我们修改了类`Animal`。
### 25.5。 实例评估
在`class_eval`中,我们看到可以将方法添加到可以由其实例访问的类中。 `instance_eval`与之相反。 不,我们不会删除方法:D,但这会将类方法添加到调用类中。 让我们来看一个例子
```rb
# instance_eval.rb
class Square
end
Square.instance_eval do
def who_am_i
puts "I am Square class"
end
end
Square.who_am_i
```
Output
```rb
I am Square class
```
在上面的示例中,请看以下代码:
```rb
Square.instance_eval do
def who_am_i
puts "I am Square class"
end
end
```
我们要做的就是在一个类上调用`instance_eval`,并且在一个块内,我们需要编写将成为类方法的代码。 我们定义了一个函数`who_am_i`,其安静程度等同于键入此
```rb
class Square
def self.who_am_i
puts "I am Square class"
end
end
```
当我们调用`Square.who_am_i`时,该方法将忠实地响应。
- 前言
- 红宝石
- 先决条件
- 1.安装 Ruby
- 2.在线资源
- 3.入门
- 4.比较与逻辑
- 5.循环
- 6.数组
- 7.哈希和符号
- 8.范围
- 9.功能
- 10.可变范围
- 11.类&对象
- 12.安全导航
- 13.打破大型程序
- 14.结构和 OpenStruct
- 15. Rdoc
- 16. Ruby 样式指南
- 17.模块和混入
- 18.日期和时间
- 19.文件
- 20. Proc,Lambda 和块
- 21.多线程
- 22.异常处理
- 23.正则表达式
- 24.宝石
- 25.元编程
- 26.基准
- 27.测试驱动开发
- 28.观察者模式
- 29.模板模式
- 30.工厂模式
- 31.装饰图案
- 32.适配器模式
- 33.单例模式
- 34.复合模式
- 35.建造者模式
- 36.策略模式
- 赞助商
- 捐
- 人们怎么说
- 版权
- 取得这本书