Domanda

Voglio essere in grado di scrivere un lambda / Proc nel mio codice Ruby, serializzarlo in modo da poterlo scrivere su disco e quindi eseguire il lambda in seguito. Un po 'come ...

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

Più tardi, in una corsa separata dell'interprete Ruby, voglio poter dire ...

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

Marshal.dump non funziona per Procs. So che Perl ha Data :: Dump :: Streamer , e in Lisp questo è banale. Ma c'è un modo per farlo in Ruby? In altre parole, quale sarebbe l'implementazione di save _ per _ in seguito ?

Modifica : La mia risposta in basso è carino, ma non si chiude su variabili libere (come x ) e le serializza insieme a lambda. Quindi nel mio esempio ...

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

... l'output della stringa non include una definizione per x . Esiste una soluzione che ne tenga conto, magari serializzando la tabella dei simboli? Puoi accedervi in ??Ruby?

Modifica 2 : ho aggiornato la mia risposta per incorporare la serializzazione delle variabili locali. Questo sembra accettabile.

È stato utile?

Soluzione

Usa 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:0x4037bb2c@(eval):1>
g.call(2) 
# => 42

È fantastico, ma non si chiude su variabili libere (come x ) e le serializza insieme a lambda.

Per serializzare le variabili , puoi anche scorrere su local_variables e serializzare anche loro. Il problema, tuttavia, è che local_variables dall'interno di save_for_later accede solo a c e s nel codice sopra - cioè variabili locali al codice di serializzazione, non al chiamante. Quindi, sfortunatamente, dobbiamo spingere l'acquirente delle variabili locali e dei loro valori.

Forse questa è una buona cosa, anche se, in generale, trovare tutte le variabili libere in un pezzo di codice Ruby è indecidibile . Inoltre, idealmente vorremmo anche salvare global_variables e tutte le classi caricate e i loro metodi sostituiti. Questo sembra poco pratico.

Usando questo semplice approccio, ottieni quanto segue:

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:0xb7cfe9c0@(eval):1>
g.call(2)
# => 42

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

Altri suggerimenti

Utilizza sourcify

Funzionerà su Ruby 1.8 o 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:0x00000100e88450@(eval):1>
g.call(2) 
# => 42

Vedi la mia altra risposta per catturare gratuitamente variabili.

Aggiorna : Ora puoi anche usare la gemma serializable_proc , che utilizza sourcify e acquisisce locale, istanza, classe e variabili globali.

Controlla le risposte a questa domanda .

Ruby ha la classe Marshal che ha un metodo di dump che puoi chiamare.

Dai un'occhiata qui:

http://rubylearning.com/satishtalim/object_serialization.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top