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


89

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


2
코드 샘플 또는 수행하려는 작업과 적어도 논리적으로 동등한 것을 공유 할 수 있습니까?
Aaron Scruggs

답변:


165

업데이트 :이 답변은 최근에 관심을 얻은 것 같기 때문에 여기에서 논의 된 기능 제거 하기 위해 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가 중첩 된 메서드에 크게 반대한다는 것을 알 수 있습니다 (하지만 여전히 중첩 된 메서드 정의를 제거하기위한 것임).


6
그러나 DRYness 메서드에서 클로저 람다를 생성하거나 재귀를 실행하는 방법을 보여줄 수도 있습니다.
Phrogz

119
Ruby에 중첩 메서드가 없을 수도 있다는 느낌이 듭니다.
Mark Thomas

16
@Mark Thomas : Ruby에 중첩 된 메서드가 없다는 사실을 언급하는 것을 잊었습니까? :-) 진지하게 : 내가이 답변을 썼을 때 이미 세 개의 답변이 있었는데, 각각의 답변은 루비 중첩 된 메소드를 가지고 있다고 주장했습니다 . 그 답변 중 일부는 노골적으로 틀렸음에도 불구하고 찬성도있었습니다. 하나는 틀렸음에도 불구하고 OP에 의해 다시 받아 들여졌습니다. 대답이 루비가 중첩 된 메서드를 지원한다는 것을 증명하기 위해 사용하는 코드 스 니펫은 실제로 그 반대를 증명하지만, 분명히 upvoters도 OP도 실제로 확인하지 않았습니다. 그래서 나는 모든 잘못된 것에 대해 하나의 정답을주었습니다. :-)
Jörg W Mittag

10
이 모든 것이 테이블을 수정하는 커널에 대한 명령 일 뿐이며 메서드와 클래스 및 모듈이 모두 테이블의 항목 일뿐 실제가 아니라는 사실을 알게되면 매트릭스가 어떻게 생겼는지 볼 때 Neo처럼됩니다. 그러면 정말로 철학적이되어 중첩 된 방법 외에는 방법조차 없다고 말할 수 있습니다. 요원조차 없습니다. 그것들은 매트릭스의 프로그램입니다. 당신이 먹는 육즙이 많은 스테이크조차도 테이블의 항목에 불과합니다.
mydoghasworms dec.

3
방법이 없습니다. 코드는 The Matrix의 시뮬레이션
일뿐입니다

13

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

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

2
당신이 틀린 것은 아니지만 당신의 대답은 중첩 된 방법을 달성하기위한 해결책으로 표현되어 있습니다. 실제로는 메소드가 아닌 procs를 사용하고 있습니다. 그것은 "중첩 방법"해결하기 위해 주장의 벌금 응답 외부의
브랜든 벅

5

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

9
inner_method는 메소드가 아니라 함수 / 람다 / 프로 시저입니다. 클래스의 연결된 인스턴스가 없으므로 메서드가 아닙니다.
Sami Samhuri

2

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

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


2

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!작동 방식을 알아 내면 그런 끔찍한 혐오감을 만들 수있는 길을 멀게 만들 수 있습니다.


-3

:-디

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

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" 

4
이것은 중첩 방법이 아닙니다 ... 명확한 이해를 위해 Jörg W Mittag 의 답변을 참조하십시오 .
Hardik 2014-08-05
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.