web-dev-qa-db-ja.com

メソッド内にメソッドを含めることは可能ですか?

メソッドの中にメソッドがあります。内部メソッドは、実行されている変数ループに依存します。それは悪い考えですか?

83
Trip

更新:この答えは最近興味を持っているように見えるので、 Discussion on the Ruby issue tracker to removeここで説明した機能、つまりforbidメソッド本体内にメソッド定義を含む


いいえ、Rubyにはネストされたメソッドはありません。

次のようなことができます:

class Test1
  def meth1
    def meth2
      puts "Yay"
    end
    meth2
  end
end

Test1.new.meth1

しかし、それはnotネストされたメソッドです。繰り返します:Rubydoes not havenested method。

これは、動的なメソッド定義です。 meth1を実行すると、meth1の本体が実行されます。本文はたまたまmeth2という名前のメソッドを定義しているため、meth1を1回実行した後、meth2を呼び出すことができます。

しかし、meth2はどこで定義されていますか?さて、それは明らかにnotnot(-===-)です。なぜなら、noRubyのネストされたメソッド。 Test1のインスタンスメソッドとして定義されます。

Test1.new.meth2
# Yay

また、明らかにmeth1を実行するたびに再定義されます:

Test1.new.meth1
# Yay

Test1.new.meth1
# test1.rb:3: warning: method redefined; discarding old meth2
# test1.rb:3: warning: previous definition of meth2 was here
# Yay

要するに、いいえ、Rubyはネストされたメソッドをサポートしません

また、Rubyでは、メソッド本体はクロージャーにはできず、ブロック本体のみがクロージャーになることに注意してください。 ifRubyサポートされているネストされたメソッド、 tネストされたメソッドで外部メソッドの変数を使用します。


UPDATE CONTINUED:later段階で、この構文はネストされたメソッドをRubyに追加するために再利用される可能性があり、これは私が説明した方法で動作します:それらは包含メソッドにスコープされます。つまり、包含メソッド本体の外側では見えず、アクセスできません。そしておそらく、それらは含まれるメソッドのレキシカルスコープにアクセスできるでしょう。ただし、上記でリンクした説明を読むと、matzがネストされたメソッドに大きく反していることがわかります(ただし、ネストされたメソッド定義を削除するために)。

157
Jörg W Mittag

実際には可能です。これにはprocs/lambdaを使用できます。

def test(value)
  inner = ->() {
    value * value
  }
  inner.call()
end
11
SuperManEver

いいえ、いいえ、Rubyにはネストされたメソッドがあります。これをチェックしてください。

def outer_method(arg)
    outer_variable = "y"
    inner_method = lambda {
      puts arg
      puts outer_variable
    }
    inner_method[]
end

outer_method "x" # prints "x", "y"
5
iirekm

このようなことができます

module Methods
  define_method :outer do 
    outer_var = 1
    define_method :inner do
      puts "defining inner"
      inner_var = outer_var +1
    end
    outer_var
  end
  extend self
end

Methods.outer 
#=> defining inner
#=> 1
Methods.inner 
#=> 2

これは、メソッド間でスコープを共有する必要があるDSLの作成などを行う場合に便利です。しかし、そうでない場合は、他の回答で述べたように、innerが呼び出されるたびにouterが再定義されるため、他のことを行う方がはるかに適切です。この動作が必要な場合、および場合によっては、これを取得するのに適した方法です。

1
mdmoskwa

Rubyの方法は、「これはどのように機能するのでしょうか?」 RakeまたはRailsを使用したことがある場合は、この種のものを見たことがあるでしょう。

ここにそのようなハックがあります:

def mlet(name,func)
  my_class = (Class.new do
                def initialize(name,func)
                  @name=name
                  @func=func
                end
                def method_missing(methname, *args)
                  puts "method_missing called on #{methname}"
                  if methname == @name
                    puts "Calling function #{@func}"
                    @func.call(*args)
                  else
                    raise NoMethodError.new "Undefined method `#{methname}' in mlet"
                  end
                end
              end)
  yield my_class.new(name,func)
end

それは、クラスを作成してブロックに渡すトップレベルのメソッドを定義することです。クラスはmethod_missingを使用して、選択した名前のメソッドを持っているふりをします。提供する必要があるラムダを呼び出すことにより、メソッドを「実装」します。オブジェクトに1文字の名前を付けることで、必要な余分な入力の量を最小限に抑えることができます(これはRailsがschema.rbで行うことと同じです)。mletは、fletが「関数」を表し、fが「メソッド」を表すことを除いて、Common LISP形式mにちなんで命名されます。

次のように使用します。

def outer
   mlet :inner, ->(x) { x*2 } do |c|
     c.inner 12
   end
end

ネストを追加せずに複数の内部関数を定義できる同様の仕掛けを作成することもできますが、RakeまたはRspecの実装で見られるようなsortいハックが必要になります。 Rspecのlet!がどのように機能するかを理解することで、このような恐ろしい憎悪を生み出すことができるようになります。

0