Domanda

Sto solo imparando ruby ??e sto cercando di capire l'ambito del codice eseguito in blocchi. Ad esempio, voglio essere in grado di creare un blocco che influisce sul metodo a cui è collegato, in questo modo:

def test(&block)
  block.call() if block_given?
  puts "in test, foo is #{foo}"
  puts "in test, bar is #{bar}"
end

test() {
  foo="this is foo"
  bar="this is bar"
}

In questo caso non voglio affatto modificare il blocco: voglio poterlo scrivere usando semplici riferimenti a variabili e nessun parametro. Solo modificando il metodo "test" nell'esempio precedente , è possibile accedere alle variabili definite nel blocco?

Ancora una volta, l'obiettivo è lasciare il blocco non modificato, ma essere in grado di accedere alle variabili create da 'test' dopo l'esecuzione del blocco.

È stato utile?

Soluzione

Innanzitutto, block.call () viene eseguito con yield e non è necessario il parametro & amp; block che modo.

Normalmente non puoi fare quello che vuoi, i blocchi vengono associati quando vengono creati e all'interno del blocco puoi vedere le variabili locali definite in quel momento; il modo più semplice per fare quello che vuoi, che non è come userai normalmente i blocchi, è questo:

def test()
  foo = yield if block_given?
  puts "in test, foo is #{foo}"
end

test() {
  foo="this is foo"
}

Ma questo è solo un effetto collaterale perché foo è " reso " dal blocco. Se invece lo fai:

def test()
  foo = yield if block_given?
  puts "in test, foo is #{foo}"
end

test() {
  foo="this is foo"
  "ha ha, no foo for you"
}

Noterai che fa qualcosa di diverso.

Ecco più magia:

def test(&block)
   foo = eval "foo", block.binding
   puts foo
   block.call
   foo = eval "foo", block.binding
   puts foo
end

foo = "before test"
test() {
  foo = "after test"
  "ha ha, no foo for you"
}

Funzionerebbe in qualche modo, ma si interrompe se si rimuove foo = " prima del test " perché foo diventa una variabile locale nel blocco e non esiste in l'associazione.

Riepilogo: non è possibile accedere alle variabili locali da un blocco, solo i locali in cui è stato definito il blocco e il valore di ritorno del blocco.

Anche questo non funzionerà:

def test(&block)
   eval "foo = 'go fish'", block.binding
   block.call
   bar = eval "foo", block.binding
   puts bar
end

perché il foo nell'associazione è diverso da quello locale nel blocco (non lo sapevo, grazie).

Altri suggerimenti

No, un blocco non può influenzare le variabili locali nel luogo in cui viene chiamato.

I blocchi in Ruby sono chiusure , il che significa che catturano l'ambito attorno a loro quando vengono creati. Le variabili che sono visibili quando si crea il blocco sono quelle che vede. Se ci fosse un foo e bar nella parte superiore del tuo codice, al di fuori di qualsiasi metodo, quel blocco cambierebbe quelli quando veniva chiamato.

Puoi fare quello che vuoi essendo un po 'più prolisso:

class Test
  def foo(t)
    @foo = t
  end
  def bar(t)
    @bar = t
  end
  def test(&block)
    self.instance_eval &block if block_given?
    puts "in test, foo is #{@foo}"
    puts "in test, bar is #{@bar}"
  end
end

Test.new.test() {
  foo "this is foo"
  bar "this is bar"
}

Puoi creare metodi come attr_accessor che genereranno setter appropriati (i metodi foo e bar ).

def test(&block)
  foo = yield
  puts "in test, foo is #{foo}"
end

test { "this is foo" }

stampa in prova, foo è questo è foo

Il valore di rendimento è il valore del blocco.

È anche possibile passare parametri per produrre, a cui è possibile accedere dal blocco usando | param, un altro | all'inizio del blocco.

Inoltre, controlla procs.

foo = "this is foo"
p = Proc.new { "foo is #{foo}" }
p.call

Stampa " foo è questo è foo "

def test(p) 
  p.call
end

test p

Stampa " foo è questo è foo "

def test2(p)
  foo = "monkey"
  p.call
end

test2 p

Stampa " foo è questo è foo "

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