메서드 내부에 메서드가 있습니다. 내부 방법은 실행중인 변수 루프에 따라 다릅니다. 그게 나쁜 생각인가요?
답변:
업데이트 :이 답변은 최근에 관심을 얻은 것 같기 때문에 여기에서 논의 된 기능 을 제거 하기 위해 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
). mlet
Common 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"