2008-10-14 4 views
16

Ruby 코드에 람다/Proc을 작성하고 직렬화하여 디스크에 기록한 다음 나중에 람다를 실행할 수 있기를 원합니다. 어떻게 루비 코드를 문자열 화/직렬화합니까?

x = 40 
f = lambda { |y| x + y } 
save_for_later(f) 

나중에, 루비 인터프리터의 별도의 실행에, 내가 말할 수 있도록하려면 같은 종류의 ... ... Marshal.dump은 프로세서 수에 대해 작동하지 않습니다

f = load_from_before 
z = f.call(2) 
z.should == 42 

. Perl은 Data::Dump::Streamer이고, Lisp에서는 이것이 사소하다는 것을 알고 있습니다. 하지만 루비에서 할 수있는 방법이 있습니까? 즉, _에 대한 save_의 구현은 나중에일까요?

편집 : My answer below 좋은이지만, 그것은 자유 변수 (같은이 x)를 통해 닫고 람다와 함께 그들을 직렬화하지 않습니다.

x = 40 
s = save_for_later { |y| x + y } 
# => "lambda { |y|\n (x + y)\n}" 

... 내 예제에서 그래서 ... 문자열 출력은 x에 대한 정의가 포함되어 있지 않습니다. 심볼 테이블을 직렬화하여이를 고려한 솔루션이 있습니까? 루비에서 액세스 할 수 있습니까?

편집 2 : 로컬 변수 직렬화를 통합하여 답변을 업데이트했습니다. 이것은 받아 들일 만하다.

답변

11

사용 Ruby2Ruby

def save_for_later(&block) 
    return nil unless block_given? 

    c = Class.new 
    c.class_eval do 
    define_method :serializable, &block 
    end 
    s = Ruby2Ruby.translate(c, :serializable) 
    s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1|').sub(/end$/, '}') 
end 

x = 40 
s = save_for_later { |y| x + y } 
# => "lambda { |y|\n (x + y)\n}" 
g = eval(s) 
# => #<Proc:[email protected](eval):1> 
g.call(2) 
# => 42 

이 매우 중요하지만, 무료로 변수를 통해 닫습니다 (같은 x)와 람다와 함께 그들을 직렬화하지 않습니다.

serialize variables으로도 local_variables 이상으로 반복하고 일련 번호를 지정할 수 있습니다. 문제는 위의 코드에서 local_variables ~ save_for_later은 액세스가 cs입니다. 즉, 호출자가 아닌 일련 화 코드의 로컬 변수입니다. 불행히도 우리는 지역 변수와 그 값을 호출자에게 가져와야합니다.

일반적으로 루비 코드의 모든 자유 변수를 찾는 것이 undecidable이기 때문에 이것이 좋은 방법 일 것입니다. 또한 global_variables 및로드 된 클래스와 재정의 된 메서드를 저장하는 것이 이상적입니다. 이것은 비실용적 인 것처럼 보인다. 이 간단한 방법을 사용

, 다음을 얻을 :

def save_for_later(local_vars, &block) 
    return nil unless block_given? 

    c = Class.new 
    c.class_eval do 
    define_method :serializable, &block 
    end 
    s = Ruby2Ruby.translate(c, :serializable) 
    locals = local_vars.map { |var,val| "#{var} = #{val.inspect}; " }.join 
    s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1| ' + locals).sub(/end$/, '}') 
end 

x = 40 
s = save_for_later(local_variables.map{ |v| [v,eval(v)] }) { |y| x + y } 
# => "lambda { |y| _ = 40; x = 40;\n (x + y)\n}" 

# In a separate run of Ruby, where x is not defined... 
g = eval("lambda { |y| _ = 40; x = 40;\n (x + y)\n}") 
# => #<Proc:[email protected](eval):1> 
g.call(2) 
# => 42 

# Changing x does not affect it. 
x = 7 
g.call(3) 
# => 43 
+1

내 Ruby2Ruby 버전 (1.2.4)에 번역 방법이없는 것 같습니다. 최신 버전에서 다른 API가 있습니까? –

+1

나는 그것을 이해하지 못한다. 그러나 [this thread] (http://stackoverflow.com/questions/1144906/error-running-check-currentcode-undefined-method-translate-for-ruby2ruby)는 Ruby2Ruby 1.2로 다운 그레이드 할 것을 권장한다. .2. 나는 또한 [이 원숭이 패치] (http://gist.github.com/321038)가 최신 버전으로 다시 추가한다고 주장했다. 비록 그것을 시도하지 않았습니다. –

+1

Ruby 1.9에서는 작동하지 않습니다. – Kludge

11

사용 sourcify

을이 루비 1.8 또는 1.9에서 작동합니다.

def save_for_later(&block) 
    block.to_source 
end 

x = 40 
s = save_for_later {|y| x + y } 
# => "proc { |y| (x + y) }" 
g = eval(s) 
# => #<Proc:[email protected](eval):1> 
g.call(2) 
# => 42 

무료 변수를 캡처 my other answer를 참조하십시오.

업데이트는 : 지금 당신은 또한 sourcify 사용하고, 현지 예, 클래스 및 전역 변수를 캡처 serializable_proc 보석을 사용할 수 있습니다.

+0

무료 변수를 캡처 할 때 sourcify를 사용하는 예가 있습니까? – rogerdpack

+0

@rogerdpack, 내 업데이트 참조. 자유 변수에 관심이 있다면 serializable_proc 젬을 사용하고 싶을지도 모릅니다. –

+0

'sourcify' /'SerializableProc' : 더 이상 유지 관리되지 않습니다 (적어도 오늘) – ribamar