[英]Ruby: Module, Mixins and Blocks confusing?
以下是我試圖從Ruby Programming
Book中運行的代碼http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
為什么product
方法沒有給出正確的輸出? 我用irb test.rb
運行它。 我正在運行Ruby 1.9.3p194
。
module Inject
def inject(n)
each do |value|
n = yield(n, value)
end
n
end
def sum(initial = 0)
inject(initial) { |n, value| n + value }
end
def product(initial = 1)
inject(initial) { |n, value| n * value }
end
end
class Array
include Inject
end
[1, 2, 3, 4, 5].sum ## 15
[1, 2, 3, 4, 5].product ## [[1], [2], [3], [4], [5]]
自編寫該代碼示例以來, Array
已經獲得了#product
方法,您將看到該特定方法的輸出。 將模塊的方法重命名為product_new
。
在代碼末尾添加以下行:
p Array.ancestors
你得到(在Ruby 1.9.3中):
[Array, Inject, Enumerable, Object, Kernel, BasicObject]
Array是Object的子類,並且有一個指向Object的超類指針。 由於Enumerable被Array包含在(包含)中,因此Array的超類指針指向Enumerable,並從那里指向Object。 當您包含Inject時,Array的超類指針指向Inject,並從那里指向Enumerable。 當你寫作
[1,2,3,4,5] .product
方法搜索機制從實例對象[1,2,3,4,5]開始,轉到它的類Array,並在那里找到產品(1.9中的新產品)。 如果你在Ruby 1.8中運行相同的代碼,方法搜索機制從實例對象[1,2,3,4,5]開始,轉到它的類Array,找不到產品,上升超類鏈,並找到Inject中的產品,您可以按預期獲得結果120。
您可以在Pickaxe中找到帶有圖形圖片的模塊和混合的很好的解釋http://pragprog.com/book/ruby3/programming-ruby-1-9
我知道我已經看到有些人要求prepend
方法在實例和它的類之間包含一個模塊,以便搜索機制在類之前找到包含的方法。 我在SO中用“[ruby] prepend模塊而不是包含”進行了搜索,並發現其中包括:
回應@zeronone“我們怎樣才能避免這種命名空間沖突?”
盡可能避免monkeypatching核心類是第一條規則。 更好的方法(IMO)將是子類Array:
class MyArray < Array
include Inject
# or you could just dispense with the module and define this directly.
end
xs = MyArray.new([1, 2, 3, 4, 5])
# => [1, 2, 3, 4, 5]
xs.sum
# => 15
xs.product
# => 120
[1, 2, 3, 4, 5].product
# => [[1], [2], [3], [4], [5]]
Ruby可能是一種OO語言,但因為它有時是動態的(我發現)子類化被遺忘為一種有用的做事方式,因此過度依賴於Array,Hash和String的基本數據結構,然后導致這些課程重新開放太多。
順便說一句:在Ruby 2.0中,有兩個功能可以幫助您解決問題。
Module#prepend
將 mixin添加到繼承鏈之前,因此mixin中定義的方法會覆蓋它所混合的模塊/類中定義的方法。
細化允許詞法范圍的monkeypatching。
在這里他們正在行動(您可以通過RVM或ruby-build輕松獲得當前版本的YARV 2.0):
module Sum
def sum(initial=0)
inject(initial, :+)
end
end
module ArrayWithSum
refine Array do
prepend Sum
end
end
class Foo
using ArrayWithSum
p [1, 2, 3].sum
# 6
end
p [1, 2, 3].sum
# NoMethodError: undefined method `sum' for [1, 2, 3]:Array
using ArrayWithSum
p [1, 2, 3].sum
# 6
以下代碼不是很詳細。 只是為了向您展示,今天您已經擁有了一些方法,比如Ruby在某些事件發生時調用的鈎子,以檢查將使用/不使用哪個方法(來自包含類或包含的模塊)。
module Inject
def self.append_features(p_host) # don't use included, it's too late
puts "#{self} included into #{p_host}"
methods_of_this_module = self.instance_methods(false).sort
print "methods of #{self} : "; p methods_of_this_module
first_letter = []
methods_of_this_module.each do |m|
first_letter << m[0, 2]
end
print 'selection to reduce the display : '; p first_letter
methods_of_host_class = p_host.instance_methods(true).sort
subset = methods_of_host_class.select { |m| m if first_letter.include?(m[0, 2]) }
print "methods of #{p_host} we are interested in: "; p subset
methods_of_this_module.each do |m|
puts "#{self.name}##{m} will not be used" if methods_of_host_class.include? m
end
super # <-- don't forget it !
end
在你的帖子中休息。 執行:
$ ruby -v
ruby 1.8.6 (2010-09-02 patchlevel 420) [i686-darwin12.2.0]
$ ruby -w tinject.rb
Inject included into Array
methods of Inject : ["inject", "product", "sum"]
selection to reduce the display : ["in", "pr", "su"]
methods of Array we are interested in: ["include?", "index",
..., "inject", "insert", ..., "instance_variables", "private_methods", "protected_methods"]
Inject#inject will not be used
$ rvm use 1.9.2
...
$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.0]
$ ruby -w tinject.rb
Inject included into Array
methods of Inject : [:inject, :product, :sum]
selection to reduce the display : ["in", "pr", "su"]
methods of Array we are interested in: [:include?, :index, ..., :inject, :insert,
..., :private_methods, :product, :protected_methods]
Inject#inject will not be used
Inject#product will not be used
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.