多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 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(:spe​​ak)中所看到的那样,我们正在使用 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`时,该方法将忠实地响应。