13

イディオムの提示

受け入れられた答えに代わる、興味深いが説明のつかない代替案を見つけました。コードはREPLで明確に機能します。例えば:

module Foo
  class Bar
    def baz
    end
  end
end
Foo.constants.map(&Foo.method(:const_get)).grep(Class)
=> [Foo::Bar]

ただし、ここで使用されているイディオムを完全には理解していません。&Foo特に、ある種のクロージャのように見えるの使用、またはこの特定の#grepの呼び出しが結果にどのように作用するかを理解していません。

イディオムの解析

これまでのところ、私はこれの断片を解析することができましたが、すべてがどのように組み合わされているかは実際にはわかりません。これが私がサンプルコードについて理解していると思うことです。

  1. Foo.constantsモジュール定数の配列をシンボルとして返します。

  2. method(:const_get)Object#methodを使用してメソッドルックアップを実行し、クロージャを返します。

  3. Foo.method(:const_get).call :Barクラス内の定数への修飾パスを返すクロージャです。

  4. &Fooある種の特別なラムダのようです。ドキュメントによると:

    Procオブジェクトが&引数で指定されている場合、&引数はトリックを保持します。

    この特定の文脈でも、それが何を意味するのかを完全に理解しているのかわかりません。なぜProc?どのような「トリック」があり、なぜここで必要なのですか?

  5. grep(Class)#mapメソッドの値で動作していますが、その機能は明らかではありません。

    • この#mapコンストラクトが列挙子ではなくgreppable配列を返すのはなぜですか?

      Foo.constants.map(&Foo.method(:const_get)).class
      => Array
      
    • Classという名前のクラスのgrepは実際にどのように機能し、なぜここでその特定の構築が必要なのですか?

      [Foo::Bar].grep Class
      => [Foo::Bar]
      

質問、言い換え

このイディオム全体を本当に理解したいと思います。誰かがここのギャップを埋めて、すべてのピースがどのように組み合わされているかを説明できますか?

4

4 に答える 4

9

&Foo.method(:const_get)オブジェクトのメソッドconst_getです。Foo別の例を次に示します。

m = 1.method(:+)
#=> #<Method: Fixnum#+>
m.call(1)
#=> 2
(1..3).map(&m)
#=> [2, 3, 4]

つまり、これは無意味な言い方Foo.constants.map { |c| Foo.const_get(c) }です。grepを使用===して要素を選択するため、他の値ではなく、クラスを参照する定数のみを取得します。Fooこれは、たとえば、 pedBaz = 1されない別の定数を追加することで確認できます。grep

さらに質問がある場合は、コメントとして追加してください。明確にするよう努めます。

于 2012-07-16T14:09:20.257 に答える
4

あなたの慣用句の解析はかなり正確ですが、私はそれを調べて、あなたが言及した質問を解決しようとします.

1.Foo.constants

おっしゃったように、これはモジュール定数名の配列をシンボルとして返します。

2.Array#map

あなたは明らかにこれが何をするか知っていますが、完全を期すためにそれを含めたいと思います。Map はブロックを取り、各要素を引数としてそのブロックを呼び出します。Arrayこれらのブロック呼び出しの結果の を返します。

3.Object#method

また、あなたが述べたように、これはメソッド検索を行います。これは重要です。なぜなら、Ruby の括弧のないメソッドは、そのメソッドの引数なしのメソッド呼び出しだからです。

4.&

この演算子は、ものをブロックに変換するためのものです。Ruby ではブロックは第一級オブジェクトではないため、これが必要です。この二流のステータスのため、スタンドアロンのブロックを作成する方法はありませんが、Procsブロックに変換することはできます (ただし、ブロックを関数に渡す場合のみ)。&演算子は、この変換を行う方法です。Procオブジェクトをブロックであるかのように渡したいときはいつでも、&演算子を先頭に追加して、関数の最後の引数として渡すことができます。しかし、&実際にはProcオブジェクトだけでなく、to_procメソッドを持つあらゆるものを変換できます。

この場合、メソッドを持つMethodオブジェクトがあります。オブジェクトとオブジェクトto_procの違いは、そのコンテキストにあります。オブジェクトはクラス インスタンスにバインドされ、そのクラスに属する変数にアクセスできます。Aは、それが作成されたコンテキストにバインドされます。つまり、作成されたスコープにアクセスできます。結果が同じ変数にアクセスできるように、メソッドのコンテキストをバンドルします。オペレーターの詳細については、こちらを参照してください。ProcMethodMethodProcMethod#to_procProc&

5.grep(Class)

動作する方法は、列挙可能なすべての x に対してEnumerable#grep実行されることです。この場合、ではなくを呼び出しているためargument === x、 への引数の順序は非常に重要です。次のコマンドを実行すると、これら 2 つの違いを確認できます。===Class.===Foo::Bar.===

    irb(main):043:0> Class === Foo::Bar
    => true
    irb(main):044:0> Foo::Bar === Class
    => false

Module#===(からメソッドをClass継承) は、引数が のインスタンスまたはその子孫の 1 つ (! など) の場合に戻ります。これにより、 or型ではない定数が除外されます。ドキュメントはこちらにあります。===MethodTrueModuleClassModuleClassModule#===

于 2012-07-31T00:48:51.033 に答える
3

最初に知っておくべきことは次のとおりです。

&to_proc後続のオブジェクトを呼び出し、メソッドのブロックとして生成された proc を使用します。

ここで、メソッドが特定のクラスでどの程度正確にto_proc実装されているかをドリルダウンする必要があります。

1. 記号

class Symbol
  def to_proc
    Proc.new do |obj, *args|
      obj.send self, *args
    end
  end
end

またはこのようなもの。上記のコードから、生成された proc がオブジェクトのメソッド (name == シンボル) を呼び出し、メソッドに引数を渡すことが明確にわかります。簡単な例:

[1,2,3].reduce(&:+)
#=> 6

まさにそれを行います。次のように実行します。

  1. :+.to_procproc オブジェクトを呼び出して取得します=> #<Proc:0x007fea74028238>
  2. proc を受け取り、それをブロックとしてreduceメソッドに渡します。したがって、呼び出す代わりに、を呼び出し[1,2,3].reduce { |el1, el2| el1 + el2 }ます
    [1,2,3].reduce { |el1, el2| el1.send(:+, el2) }

2.方法

 class Method
   def to_proc
     Proc.new do |*args|
       self.call(*args)
     end
   end
 end

ご覧のとおり、 の実装が異なりますSymbol#to_proc。これを説明するために、reduce例をもう一度考えてみますが、代わりにメソッドを使用する方法を見てみましょう。

def add(x, y); x + y end
my_proc = method(:add)
[1,2,3].reduce(&my_proc)
#=> 6

上記の例では、 を呼び出して[1,2,3].reduce { |el1, el2| my_proc(el1, el2) }います。

mapメソッドが Enumerator ではなく Array を返す理由は、 block を渡しているためです。代わりにこれを試してください。

[1,2,3].map.class
#=> Enumerator

最後になりましたが、Array で重要なことは、その引数にgrepある要素を選択することです。===これであなたの懸念が明確になることを願っています。

于 2012-07-30T10:50:05.593 に答える
2

あなたのシーケンスは次と同等です:

c_names = Foo.constants #=> ["Bar"]
cs = c_names.map { |c_name| Foo.__send__(:const_get, c_name) } #=> [Foo::Bar]
cs.select{ |c| Class === c } #=> [Foo::Bar]

あなたはObject#method(大まかに)次のように考えることができます:

class Object
  def method(m)
    lambda{ |*args| self.__send__(m, *args) }
  end
end

grepここで説明されていますhttp://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-grep

===for Class(のサブクラスModule) については、こちらで説明していますhttp://ruby-doc.org/core-1.9.3/Module.html#method-i-3D-3D-3D

UPDATEgrep :他の定数が存在する可能性があるため、必要があります:

module Foo
  PI = 3.14
  ...
end

おそらくそれらは必要ありません。

于 2012-07-16T14:13:47.073 に答える