## 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`時,該方法將忠實地響應。
- 前言
- 紅寶石
- 先決條件
- 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.策略模式
- 贊助商
- 捐
- 人們怎么說
- 版權
- 取得這本書