2008-10-14 32 views
16

Tôi muốn có thể viết một lambda/Proc trong mã Ruby của tôi, serialize nó để tôi có thể ghi nó vào đĩa, và sau đó thực hiện lambda sau này. Loại giống như ...Làm thế nào để bạn stringize/serialize mã Ruby?

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

Sau đó, trong một hoạt động riêng biệt của thông dịch viên Ruby, tôi muốn để có thể nói ...

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

Marshal.dump không làm việc cho Procs. Tôi biết Perl có Data::Dump::Streamer, và trong Lisp này là tầm thường. Nhưng có cách nào để làm điều đó trong Ruby? Nói cách khác, việc triển khai save_ cho _ sau là gì?

Chỉnh sửa: My answer below là tốt, nhưng không đóng các biến miễn phí (như x) và tuần tự hóa chúng cùng với lambda. Vì vậy, trong ví dụ của tôi ...

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

... đầu ra chuỗi không bao gồm định nghĩa cho x. Có một giải pháp mà đưa này vào tài khoản, có lẽ bằng cách serializing bảng biểu tượng? Bạn có thể truy cập nó trong Ruby không?

Chỉnh sửa 2: Tôi đã cập nhật câu trả lời của mình để kết hợp các biến cục bộ. Điều này có vẻ chấp nhận được.

Trả lời

11

Sử dụng 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 

này là rất tốt, nhưng nó không đóng qua các biến miễn phí (như x) và serialize họ cùng với lambda.

Để serialize variables cũng có, bạn có thể lặp lại qua số local_variables và tuần tự hóa chúng. Tuy nhiên, vấn đề là local_variables từ bên trong save_for_later chỉ truy cập cs trong mã ở trên - tức là các biến cục bộ với mã tuần tự hóa, không phải là người gọi. Thật không may, chúng ta phải đẩy lấy các biến cục bộ và các giá trị của chúng cho người gọi.

Có thể đây là một điều tốt, vì nói chung, việc tìm kiếm tất cả các biến miễn phí trong một đoạn mã Ruby là undecidable. Thêm vào đó, lý tưởng là chúng tôi cũng sẽ lưu global_variables và mọi lớp được tải và phương thức được ghi đè của chúng. Điều này có vẻ không thực tế.

Sử dụng phương pháp đơn giản này, bạn nhận được như sau:

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

Phiên bản Ruby2Ruby của tôi (1.2.4) dường như không có phương thức dịch. Có một api khác cho điều này trong các phiên bản mới hơn không? –

+1

Tôi không hiểu lắm, nhưng [chủ đề này] (http://stackoverflow.com/questions/1144906/error-running-heckle-currentcode-undefined-method-translate-for-ruby2ruby) khuyến nghị hạ cấp xuống Ruby2Ruby 1.2 .2. Tôi cũng tìm thấy [bản vá khỉ này] (http://gist.github.com/321038) mà yêu cầu thêm nó trở lại các phiên bản sau này. Đã không thử nó mặc dù. –

+1

Nó hút rằng nó không hoạt động với Ruby 1.9 ... – Kludge

11

Sử dụng sourcify

này sẽ làm việc trên Ruby 1,8 hoặc 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 

Xem my other answer để chụp các biến miễn phí.

Cập nhật: Bây giờ bạn cũng có thể sử dụng serializable_proc đá quý, trong đó sử dụng sourcify, và chụp địa phương, ví dụ, lớp, và các biến toàn cầu.

+0

Có ví dụ nào về việc sử dụng sourcify với các biến miễn phí không? – rogerdpack

+0

@rogerdpack, xem cập nhật của tôi. Bạn có thể chỉ muốn sử dụng gem gem serializable_proc nếu bạn quan tâm đến các biến tự do. –

+0

'sourcify' /' SerializableProc': không còn được duy trì (ít nhất là hôm nay) – ribamar

Các vấn đề liên quan