簡體   English   中英

Ruby:模塊,Mixins和Block令人困惑?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM