[ruby] 메소드 내부에 메소드를 가질 수 있습니까?

메서드 내부에 메서드가 있습니다. 내부 방법은 실행중인 변수 루프에 따라 다릅니다. 그게 나쁜 생각인가요?



답변

업데이트 :이 답변은 최근에 관심을 얻은 것 같기 때문에 여기에서 논의 된 기능 제거 하기 위해 Ruby 문제 추적기에 대한 논의 가 있음을 지적하고 싶었 습니다. 즉 , 메서드 본문 내부에 메서드 정의를 갖는 것을 금지 합니다.


아니요, Ruby에는 중첩 된 메서드가 없습니다.

다음과 같이 할 수 있습니다.

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

Test1.new.meth1

그러나 그것은 중첩 된 방법 이 아닙니다 . 반복합니다 : 루비 에는 중첩 된 메서드 가 없습니다 .

이것이 바로 동적 메서드 정의입니다. 를 실행 meth1하면의 본문 meth1이 실행됩니다. 본문은 이름이라는 메서드를 정의하기 meth2때문에 한 meth1번 실행 한 후을 호출 할 수 있습니다 meth2.

그러나 어디에 meth2정의되어 있습니까? 음, Ruby 에는 중첩 된 메서드 가 없기 때문에 분명히 중첩 된 메서드로 정의되어 있지 않습니다 . 다음의 인스턴스 메소드로 정의됩니다 .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에서 메서드 본문은 클로저가 될 수 없으며 블록 본문 만 가능합니다. 이 거의을 제거해도 있기 때문에 중첩 방법의 주요 사용 사례, 경우에 루비가 중첩 방법을 지원, 당신은 중첩 된 방법으로 외부 메소드의 변수를 사용할 수 없습니다.


UPDATE CONTINUED : 이후 단계에서이 구문은 Ruby에 중첩 된 메서드를 추가하는 데 재사용 될 수 있습니다.이 구문은 제가 설명한 방식대로 동작합니다. 즉, 포함하는 메서드로 범위가 지정됩니다. 신체. 그리고 아마도 그들은 포함하는 메서드의 어휘 범위에 액세스 할 수 있습니다. 그러나 위에서 링크 한 토론을 읽으면 matz가 중첩 된 메서드에 크게 반대한다는 것을 알 수 있습니다 (하지만 여전히 중첩 된 메서드 정의를 제거하기위한 것임).


답변

실제로 가능합니다. 이를 위해 procs / lambda를 사용할 수 있습니다.

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


답변

아니요, 아니요, 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"


답변

다음과 같이 할 수 있습니다.

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. 이 동작을 원하고 때로는 그럴 수도 있다면 이것이 좋은 방법입니다.


답변

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선택한 이름의 메서드가있는 척하는 데 사용 합니다. 제공해야하는 람다를 호출하여 메서드를 “구현”합니다. 한 글자로 된 이름으로 객체의 이름을 지정하면 필요한 추가 입력의 양을 최소화 할 수 있습니다 (Rails가에서 수행하는 것과 동일 함 schema.rb). mletCommon Lisp 형식의 이름을 따서 명명되었습니다. flet단, f“기능” m은 “방법”을 의미합니다.

다음과 같이 사용합니다.

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

추가 중첩없이 여러 내부 함수를 정의 할 수있는 유사한 장치를 만들 수 있지만,이를 위해서는 Rake 또는 Rspec 구현에서 찾을 수있는 훨씬 더 추악한 해킹이 필요합니다. Rspec의 let!작동 방식을 알아 내면 그런 끔찍한 혐오감을 만들 수있는 길을 멀게 만들 수 있습니다.


답변

:-디

루비에는 중첩 된 메서드가 있지만 예상대로 수행하지 않습니다.

1.9.3p484 :001 > def kme; 'kme'; def foo; 'foo'; end; end
 => nil
1.9.3p484 :003 >   self.methods.include? :kme
 => true
1.9.3p484 :004 > self.methods.include? :foo
 => false
1.9.3p484 :005 > kme
 => nil
1.9.3p484 :006 > self.methods.include? :foo
 => true
1.9.3p484 :007 > foo
 => "foo"


답변