2012-03-21 2 views
2

Ruby에서 가능하면 확실하지 않지만 누군가가 좋은 해결책을 알고있는 경우에 대비할 수 있습니다.Ruby proc/block의 구조 변경

블록의 구조를 변경하고 특정 노드를 다른 코드 구조로 바꾸고 싶습니다. 매크로와 비슷합니다. 예를 들어

, 나는
some_method do 
    foo 
    bar 
end 

가 그럼 난 some_method에서

def some_method(&block) 
    ... 
end 

처럼 some_method 정의

평가되지 않은 코드 블록을 가지고 있다고, 정말 뭔가 다른 블록에 "바"를 교체하고 싶습니다 , 예. 바즈와.

블록을 평가하는 교체가 필요합니다. 궁극적으로 블록을 다른 곳으로 돌리고 있기 때문입니다.

Doable? 또는 아니오?

상당히 복잡한 답변을 생각할 수 있습니다. 예 : 블록을 대체 할 클로저를 추가하여 블록을 전달할 수 있으며 bar가 평가 될 때 method_missing과 bar를 바스로 바꿀 수 있습니다. 그러나 더 간단한 방법이 있습니까?

감사합니다.

+0

당신이 원하는 것을하기 위해'bar'를 단순히 원숭이 패치하는 대신에 이것을하기에 정말로 좋은 이유가 있기를 바랍니다. 이러한 종류의 무거운 메타 프로그래밍은 외부 개발자의 관점에서 예측할 수 없을 정도로 행동하는 응용 프로그램으로 이어질 수 있습니다. – tadman

+1

@ tadman : 원숭이 패치는 외부 개발자에게 친숙한가요? 나는 그것이 규범의 바깥에 있다는 것을 알았지 만, 일단 블록이 변경되면 다른 루비 코드 블록과 다르지 않으므로 실제로는 매우 잘 추상화되어 있습니다. 매크로는 유용한 개념입니다. – Overclocked

+2

[매크로, 위생 및 Ruby에서 이름 별 호출] (http://weblog.raganwald.com/2008/06/macros-hygiene-and-call-by-name-in-ruby.html) 및 Reg의 [rewrite] (http://rewrite.rubyforge.org/)와 [ick] (http://ick.rubyforge.org/)의 보석을 보았습니까? 1.8st 해석기에 묶여있는 sexp/parse-tree에 의존하기 때문에 1.9에서 작동한다고 생각하지 않습니다. 그러나 루비를 좀 더 유능한 리스프와보다 유용한 함수형 언어로 구부리기위한 꽤 흥미로운 작업. – dbenhur

답변

0

Ruby에는 동적 범위가없고 매크로가 없으므로, bar을 매개 변수로 사용하는 함수에서 블록을 래핑하고 그 함수를 전달하지 않는 한 그런 코드를 대체 할 수는 없다고 생각합니다. .

class Base 
    def some_method(&block) 
    self.instance_eval(&block) 
    end 
    def foo; puts 'foo'; end 
    def bar; puts 'bar'; end 
end 

class Replacement < Base 
    def foo; puts 'baz'; end 
end 

Base.new.some_method do 
    foo 
    bar 
end 

Replacement.new.some_method do 
    foo 
    bar 
end 

출력 :

foo 
bar 
baz 
bar 
0

) = 당신은 물론 eval 사용할 수 있습니다,하지만 난 그것을 권하고 싶지 않다 별칭 및 Procs 도움이?

def foo; p 'foo'; end 
def bar; p 'bar'; end 
def sm(&block) 
    @@Block = Proc.new { 
     alias :oldbar :bar 
     def bar; p 'baz'; end #redefine 
     block.call 
     alias :bar :oldbar #restore 
    } 
    yield #prints foo,bar 
end 


sm do 
    foo 
    bar 
end 


def later(&block) 
    yield 
end 
def delayedEx 
    later { @@Block.call} 
end 

delayedEx #prints foo,baz 
bar #prints bar (unchanged) 

이 인쇄 "foo는 바 foo는 바즈 바", 즉 : bar이 블록에서 다른 무언가를하지만, 외부의 원래 동작을 유지합니다.

+0

이 다소 작동하지만 instance_eval 변경 자체가 블록에 의해 참조 된 인스턴스 vars 작성된 때 범위에서 자체가 있지만 바꾸기 인스턴스를 만든. 풋총!또한 그는 즉시 블록 평가를하지 않길 원했지만이를 통과시키기 위해 변경 사항은 나중에 궁극적으로 실행될 때 효과적이었습니다. – dbenhur

+0

그래서'Replacement'를'Base'의 하위 클래스로 만들었습니다. 그렇지 않으면 이처럼 작동하지 않을 것입니다. 나중에 블록을 실행할 수도 있습니다. 차이점은 무엇입니까? 그러나 나는 @Overclocked가 요구하는 것이 거의 불가능하다고 생각한다. –

+0

하지만 서브 클래 싱은 메소드 디스패치에만 도움이됩니다. ivars는 여전히 특정 인스턴스 – dbenhur

-1
def some_method(a_proc=Proc.new{puts "Bar"}, &block) 
    a_proc.call 
    yield 
end 

p1 = Proc.new{puts "Baz"} 

some_method{puts "a block"} 
some_method(p1){puts "a block"} 
+0

이 방법은 어떤 방식으로 OP를 처리합니까? 당신의 proc 호출은 블록의 바인딩이나 실행에 영향을주지 않습니다. – dbenhur

0

할 일이 내가 생각할 수있는 가장 간단한 방법입니다