Devo creare un semplice dsl, uno dei metodi è un iterazione alla quale
però vorrei utilizzare un parametro come condizione di uscita ma c'è
solo un problema, non riesco a farlo. In pratica dovrei inserire del
codice nel blocco (o proc se serve), esempio del mio approccio poco
utile ma per rendere l'idea:
class Fixnum
def tentativi options={}
self.times do |n|
tentativo = n + 1
eval("break if #{options[:termina]}") if options[:termina]
yield tentativo
end
end
end
irb(main):058:0> 5.tentativi(:termina => "tentativo > 2"){|t| p
"tentativo " + t.to_s}
"tentativo 1"
"tentativo 2"
=> nil
Ma dovrei fare questo:
5.tentativi(:termina => "t > 2"){|t| p "tentativo " + t.to_s}
on 2012-06-06 12:08
on 2012-06-06 12:41
Direi che eval non è adatto in quel contesto. Si dovrebbe poter ottenere un comportamento simile con una lambda. http://www.skorks.com/2010/05/ruby-procs-and-lambd... Luigi
on 2012-06-06 14:41
Non so cosa possa servire, ma con le lambda si può fare una cosa del
genere.
class Fixnum
def tentativi(options={})
self.times do |n|
tentativo = n + 1
break if options[:termina].call(tentativo) if options[:termina]
yield tentativo
end
end
end
5.tentativi({:termina => lambda {|t| t == 3 }}) {|t| p "tentativo #{t}"}
# "tentativo 1"
# "tentativo 2"
Luigi
on 2012-06-06 17:18
Grazie per il link, l'autore più che un programmatore mi sembra un filosofo. Comunque l'esempio non va bene, lo scopo è poter accedere allo scope del blocco, non posso prevedere quale sia la condizione di uscita o più genericamente il codice da eseguire.
on 2012-06-06 17:59
Senti io ti spato una cosa così al volo.
class Fixnum
def tentativi
self.times do |n|
tentativo = n + 1
continue = yield tentativo
break unless continue
end
end
end
5.tentativi { |t| t <= 2 ? (p "tentativo " + t.to_s; true) : false }
un più leggibile
5.tentativi do |t|
if t > 2
false
else
p "tentativo " + t.to_s
true
end
end
Non sono il massimo, e forse avrei usato un simbolo al posto di true
false...
tipo
class Fixnum
def tentativi
self.times do |n|
tentativo = n + 1
continue = yield tentativo
break if continue == :stop
end
end
end
5.tentativi { |t| t <= 2 ? (p "tentativo " + t.to_s) : :stop }
Scappo che sono in ritardo ;)
nolith
on 2012-06-06 18:46
2012/6/6 Marco Mastrodonato <m.mastrodonato@gmail.com>:
> 5.tentativi(:termina => "t > 2"){|t| p "tentativo " + t.to_s}
domanda: perch non passi una proc come :termina ?
--
twitter: @riffraff
blog (en, it): www.riffraff.info riffraff.blogsome.com
work: circleme.com
on 2012-06-07 09:18
@Alessio
Così il blocco contiene una parte della logica di controllo invece deve
avere solo il contenuto.
@Gabriele
Intendi qualcosa di analogo a quello che ha proposto Luigi? Le proc
agiscono in un proprio contesto, non saprei come utilizzarle in questo
caso.
L'unico modo per avvicinarmi alla mia idea originale è con eval che mi
permette di agire nello scope di "tentativi", per farla più semplice:
class Fixnum
def tentativi cmd=nil, &block
self.times do |n|
tentativo = n + 1
eval cmd if cmd
block.call tentativo
end
end
end
5.tentativi("break if tentativo>2"){|t| puts "tentativo #{t}"}
ma io vorrei andare oltre ed agire nello scope del blocco. Se fosse
possibile in qualche modo poter manipolare la variabile block anzichè
poterla solo richiamare. Non so se ora è più chiaro, grazie comunque a
tutti
on 2012-06-07 10:07
Altra idea che ancora non funziona ma forse mi potete aiutare a
completarla:
module Tentativi
end
class Fixnum
def tentativi options={}, &block
block.extend Tentativi
self.times do |n|
tentativo = n + 1
if options[:termina]
Tentativi.class_eval "def break; break if #{options[:termina]}
end" if options[:termina]
block.break
end
block.call tentativo
end
block
end
end
5.tentativi(:termina => "t>2"){|t| puts "tentativo #{t}"}
NameError: undefined local variable or method `t' for
#<Proc:0x047cba58@(irb):33
>
from (eval):1:in `break'
from (irb):26:in `tentativi'
from (irb):22:in `times'
from (irb):22:in `tentativi'
from (irb):33
from ♥:0
on 2012-06-07 12:55
2012/6/7 Marco Mastrodonato <m.mastrodonato@gmail.com>: > @Alessio > Cos il blocco contiene una parte della logica di controllo invece deve > avere solo il contenuto. > > @Gabriele > Intendi qualcosa di analogo a quello che ha proposto Luigi? si, intendevo "cosa ha l'approccio di luigi che non va bene?" > Le proc > agiscono in un proprio contesto, non saprei come utilizzarle in questo > caso. si, ma nell'esempio che fai tu sta facendo la stessa cosa, cio il cliente del metodo "tentativi" sta facendo il lavoro di specificare quando il blocco deve terminare, e a quel punto tanto vale che lo faccia con una proc invece che con una stringa. Forse conviene che fai un esempio con quello che vuoi fare veramente cos riusciamo ad esserti pi utili. > end > end > 5.tentativi("break if tentativo>2"){|t| puts "tentativo #{t}"} qua lo stesso, " eval cmd if cmd" diventa cmd.call if cmd e usi throw/raise al posto di break. se vuoi passargli lo scope visto all'interno di tentativi fai cmd.call(binding) if cmd ma non ha senso che _chi chiama_ il metodo "tentativi" abbia accesso allo scope all'interno del metodo, sarebbe l'antitesi della programmazione strutturata. > ma io vorrei andare oltre ed agire nello scope del blocco. Se fosse > possibile in qualche modo poter manipolare la variabile block anzich > poterla solo richiamare. Non so se ora pi chiaro, grazie comunque a > tutti puoi farlo con porcate varie tra cui: parse_tree, ripper e a occhio con set_trace_func. Ma una cosa _terribile_ e io non penso che vuoi fare quello, e che se ritorniamo al problema originale riusciamo a fare una coda migliore. -- twitter: @riffraff blog (en, it): www.riffraff.info riffraff.blogsome.com work: circleme.com
on 2012-06-07 15:29
Ma ho scritto cosa voglio fare accedere al binding del blocco e tu mi
hai dato uno spunto incredibile:
a="main"
def prova main_bind=nil, &block
a = "main::prova"
p eval("a", main_bind) if main_bind
p eval("a", binding)
p eval("a", block.call) if block_given?
end
prova binding do
a="main::prova::block"
binding
end
dalla funzione prova ho accesso al bind del blocco!
sono ad un passo dal risultato, devo solo capire ora come accedere al
bind senza che sia il blocco a restituirlo
on 2012-06-07 16:59
Questo è un pò più chiaro:
main_a="main"
def prova main_bind=nil, &block
local_a="main::prova"
block_bind = block.call
p eval("main_a", main_bind) if main_bind
p eval("local_a", binding)
p eval("block_a", block_bind) if block_given?
end
prova binding do
block_a="main::prova::block"
binding
end
"main"
"main::prova"
"main::prova::block"
=> nil
Quello che vorrei ottenere è un blocco pulito e poter accedere
esternamente al bind del blocco, semplicemente richiamandolo ma
block.bindind si riferisce al bind dove viene eseguito il blocco e non
all'interno del blocco:
main_a="main"
def prova main_bind=nil, &block
local_a="main::prova"
block.call
p eval("main_a", main_bind) if main_bind
p eval("local_a", binding)
p eval("block_a", block.binding) if block_given?
end
prova binding do
block_a="main::prova::block"
end
"main"
"main::prova"
NameError: undefined local variable or method `block_a' for main:Object
from (irb):47
from (irb):47
from ♥:0
Credo manchi poco
on 2012-06-07 17:34
Tornando all'esempio originale:
class Fixnum
def tentativi options={}, &block
self.times do |n|
tentativo = n + 1
block_bind = block.call tentativo
break if options[:termina] && eval(options[:termina], block_bind)
end
end
end
time=Time.now+2
10_000.tentativi :termina => "exit_variable > time" do |t|
puts "tentativo #{t}"
#questa variabile appartiene allo scope del blocco
exit_variable = Time.now
binding
end
...
tentativo 4633
=> nil
on 2012-06-07 18:01
Quante cose che ho scoperto oggi:
se un blocco è bindato dove viene eseguito significa che condivide le
stesse variabili:
definiamo la funzione nel main scope
def prova
a="main::prova"
yield if block_given?
a
end
=> nil
sempre nel main scope definiamo a
irb(main):006:0> a="main"
=> "main"
eseguiamo la funzione
irb(main):007:0> prova
=> "main::prova"
irb(main):008:0> a
=> "main"
la funzione ha un bind diverso infatti crea una copia di a che non
interferisce col main scope
ora eseguiamo un blocco
prova do
a="main::prova::block"
b="main::prova::block"
end
=> "main::prova"
controlliamo a del main scope
irb(main):013:0> a
=> "main::prova::block"
il blocco utilizza lo stesso bind del main ed infatti a è stata
modificata, b invece non esisteva ed è stata eliminata
irb(main):014:0> b
NameError: undefined local variable or method `b' for main:Object
from (irb):14
on 2012-06-07 23:03
il motivo per cui ti chiedevo un esempio di come vuoi che sia il dsl
che poi se scrivi cos
On Thu, Jun 7, 2012 at 5:34 PM, Marco Mastrodonato
<m.mastrodonato@gmail.com> wrote:
> => nil
...significa che forse per te accettabile invocare un metodo ad hoc
nel blocco e a quel punto le cose sono _molto_ diverse.
In particolare, una cosa tipo:
def tentativi str, &b
#definisci temporaneamente il metodo in questo oggetto
def maybe_exit!
# trova la gemma che definisce questo, o usa set_trace_func per
tracciare quando inizia il blocco e prendi il binding da l
b=binding.of_caller
eval str in b
end
#esegui il blocco qua
instance_eval(&b)
end
x.tentativi :termina => "exit_variable > time" do |t|
puts "tentativo #{t}"
exit_variable = Time.now
maybe_exit!
en
Ma son contento per te che hai capito come funzionano le closure :D
(per quanto non capisco perch on scrivi
x.tentativi do |t|
puts "tentativo #{t}"
exit_variable = Time.now
break if exit_variable > time
end
)
--
twitter: @riffraff
blog (en, it): www.riffraff.info riffraff.blogsome.com
work: circleme.com
on 2012-06-12 17:07
Grazie Gabriele, in effetti ben presto mi sono accorto di aver scoperto l'acqua calda, troppi test tutti insieme evidentemente non mi hanno fatto bene :) Inserire il codice :termina nel blocco è troppo semplice, io voglio una vita spericolata! A parte gli scherzi, l'intento è separare il codice del blocco da quello di controllo o per altri scopi generato in modi diversi. Poi magari non riuscirò ad applicarlo però volevo capire bene il funzionamento. A proposito del tuo ultimo consiglio: non riesco a farlo funzionare! Prendendo spunto da quà: http://rubychallenger.blogspot.it/2011/07/caller-b... ho fatto una pezza scimmiesca: class Binding def of_caller cc = nil # must be present to work within lambda count = 0 # counter of returns set_trace_func lambda { |event, file, lineno, id, binding, klass| # First return gets to the caller of this method # (which already know its own binding). # Second return gets to the caller of the caller. # That's we want! if count == 2 set_trace_func nil # Will return the binding to the callcc below. cc.call binding elsif event == "return" count += 1 end } # First time it'll set the cc and return nil to the caller. # So it's important to the caller to return again # if it gets nil, then we get the second return. # Second time it'll return the binding. return callcc { |cc| } end end ma viene impostato il bind sulla classe fixnum. Non ho fatto molti tentativi anche perchè la soluzione non mi piace: 1. devo comunque sporcare il blocco 2. è complesso: dovrei far vedere a maybe_exit! due scope diversi: dove prelevare il codice di uscita e dove eseguirlo 3. of_caller è un metodo che io giudico "spericolato" e temo un giorno si possa ribellare imprevedibilmente Non perderci altro tempo, ti ringrazio tanto comunque
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.