2008-10-14 19 views
16

Ruby kodumda bir lambda/Proc yazabilmek, serileştirebilmek için serileştirmek ve sonra lambda daha sonra çalıştırmak istiyorum. Ruby kodunu nasıl dizgeleştirir/serileştirirsiniz?

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

Daha sonra Yakut tercüman ayrı vadede, söyleyecek isterler benzeri sırala ... ... Marshal.dump procs için çalışmıyor

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

. Perl'in Data::Dump::Streamer olduğunu biliyorum ve Lisp'de bu önemsiz. Ama Ruby'de bunu yapmanın bir yolu var mı? Başka bir deyişle, _ için save_'un uygulanması ne olurdu?

Düzenleme: My answer below güzel, ama serbest değişkenleri (gibi x) üzerinden kapatın ve lambda birlikte bunları serileştirmez.

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

... benim örnekte Yani ... dize çıkış x tanımını içermez. Bunu dikkate alan bir çözüm var mı, belki de sembol tablosunu serileştirerek? Buna Ruby'de erişebilir misin?

Edit 2: Serileştiren yerel değişkenleri dahil etmek için yanıtımı güncelleştirdim. Bu kabul edilebilir görünüyor.

cevap

11

Kullanım 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 

Bu harika ama serbest değişkenler üzerinde kapatmak (gibi x) ve lambda birlikte bunları serileştirmez.

serialize variables için local_variables üzerinde yineleme yapabilir ve bunları da serileştirebilirsiniz. Buradaki problem, save_for_later numaralı telefondan local_variables'un yukarıdaki kodda yalnızca c ve s erişebilmeleridir - yani arayanın değil, serileştirme kodunun yerel değişkenleri. Maalesef, yerel değişkenlerin yakalanmasını ve değerlerini arayan kişiye iletmeliyiz.

Belki de bu iyi bir şeydir, çünkü genel olarak, tüm serbest değişkenleri bir Ruby kod parçasında bulmak undecidable'dur. Ayrıca, ideal olarak global_variables ve yüklü tüm sınıflar ile bunların geçersiz kılınan yöntemlerini de koruyacağız. Bu pratik değildir. Bu basit yaklaşım kullanılarak

, aşağıdaki olsun:

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) 'ün bir versiyonunun çeviri yöntemi yok gibi görünüyor. Daha yeni versiyonlarda bunun için farklı bir api var mı? –

+1

Oldukça anlamıyorum, ama [bu konu] (http://stackoverflow.com/questions/1144906/error-running-heckle-currentcode-undefined-method-translate-for-ruby2ruby) Ruby2Ruby 1.2'ye geçişi öneriyor 0,2. Ayrıca, daha sonraki sürümlerine eklediğini iddia eden [bu maymun yamaunu] (http://gist.github.com/321038) de buldum. Yine de denemedim. –

+1

Bunun yerine Ruby 1.9 ... – Kludge

11

kullanın sourcify

Bu Ruby 1.8 veya 1.9 üzerinde çalışacak.

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 

ücretsiz değişkenleri yakalamak için my other answer bakınız.

Güncelleme: Şimdi de sourcify kullanır ve yerel, örnek, sınıf ve küresel değişkenleri yakalar serializable_proc mücevher kullanabilirsiniz.

+0

Serbest değişkenlerin yakalanmasıyla birlikte sourcify'ın herhangi bir yerinde bir örnek var mı? – rogerdpack

+0

@rogerdpack, güncellemeye bakın. Serbest değişkenleri önemsiyorsanız sadece serializable_proc gemini kullanmak isteyebilirsiniz. –

+0

'sourcify' /' SerializableProc': artık devam etmiyor (en azından bugün) – ribamar