2

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]]
4

5 に答える 5

4

そのコード例が書かれて以来、はメソッドArrayを取得しており#product、その特定のメソッドの出力が表示されています。モジュールのメソッドの名前を のような名前に変更しますproduct_new

于 2012-12-30T05:26:38.543 に答える
4

コードの最後に次の行を追加します。

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 に移動し、そこで product (1.9 の新機能) を見つけます。Ruby 1.8 で同じコードを実行すると、メソッド検索メカニズムはインスタンス オブジェクト [1, 2, 3, 4, 5] から開始し、そのクラス Array に移動し、product を検出せず、スーパークラス チェーンを上って検索します。製品を注入すると、期待どおりの結果 120 が得られます。

つるはしhttp://pragprog.com/book/ruby3/programming-ruby-1-9で、モジュールとミックスインのグラフィック画像付きの適切な説明を見つけることができます

prepend検索メカニズムがクラスのメソッドの前に含まれているメソッドを見つけるように、インスタンスとそのクラスの間の前にモジュールを含めるメソッドを求めている人がいることを知っていました。「インクルードの代わりに[ruby]モジュールを追加する」を使用してSOで検索したところ、とりわけ次のことがわかりました。

このモジュールをインクルードしても、動的に生成されたメソッドがオーバーライドされないのはなぜですか?

于 2012-12-30T11:04:13.140 に答える
2

@zeronone への応答として、「このような名前空間の競合を回避するにはどうすればよいですか?」

最初のルールは、可能な限りコア クラスにモンキー パッチを適用しないことです。これを行うより良い方法 (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 言語かもしれませんが、非常に動的であるため (私が見つけたのですが)、サブクラス化は何かを行うための便利な方法として忘れられてしまいます。これらのクラスの再開があまりにも多くなります。

于 2012-12-30T12:00:56.893 に答える
2

ところで、Ruby 2.0 には、両方の問題を解決するのに役立つ 2 つの機能があります。

Module#prepend mixin を継承チェーンの先頭に追加して、mixin で定義されたメソッドが、mixinが混在するモジュール/クラスで定義されたメソッドをオーバーライドするようにします。

改良により、レキシカルスコープのモンキーパッチが可能になります。

ここでそれらが動作しています (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
于 2012-12-30T17:27:12.743 に答える
1

次のコードはあまり詳しくありません。今日、特定のイベントが発生したときに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
于 2012-12-30T20:44:25.540 に答える