Zastanawiam się, jak przekazać blok do metody, która uczyni metodę return
na yield
.Zrozumienie powracania z proców w Rubim
Naiwny aproach nie działa:
def run(&block)
block.call
end
run { return :foo } # => LocalJumpError
owijania w innym proc ma ten sam efekt:
def run(&block)
proc { block.call }.call
end
run { return :bar } # => LocalJumpError
więc pomyślałem, że oświadczenie return
jest związany z receiver
prądu binding
. Jednakże, próbując go z instance_eval
okazał mi źle:
class ProcTest
def run(&block)
puts "run: #{[binding.local_variables, binding.receiver]}"
instance_eval(&block)
end
end
pt = ProcTest.new
binding_inspector = proc { puts "proc: #{[binding.local_variables, binding.receiver]}" }
puts "main: #{[binding.local_variables, binding.receiver]}"
# => main: [[:pt, :binding_inspector], main]
binding_inspector.call
# => proc: [[:pt, :binding_inspector], main]
pt.run(&binding_inspector)
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => proc: [[:pt, :binding_inspector], #<ProcTest:0x007f4987b06508>]
pt.run { return :baz }
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => LocalJumpError
więc pytania są:
- Jak można to zrobić?
- W jaki sposób kontekst powrotu jest powiązany z instrukcją
return
. Czy to połączenie jest dostępne za pośrednictwem interfejsu API języka? - Czy zostało to celowo wdrożone w taki sposób? Jeśli tak, dlaczego? Jeśli nie - jakie są przeszkody, aby to naprawić?
Nie odbierzesz jedno pytanie. Pierwszy punkt - Wiem, że otrzymuję wyjątek, ponieważ próbuję "zwrócić" z zakresu * głównego * i że 'return' jest związany z zakresem, który go zdefiniował. O drugiej części - wiem * lambdas * mają różne semantyki 'return' z regularnych * procs *, pytanie nie dotyczyło ich. Wiem też o 'throw' -' catch', chodziło o zrozumienie 'return'. To tak, jakby zapytać * dlaczego trawa jest zielona? * I uzyskać odpowiedź * patrząc na tę trawę, widać, że jest zielona, woda nie jest zielona i można pomalować sztuczną trawę w kolorze innym niż zielony *. – ndn
Nie ma możliwości, aby Ruby przekazała blok do metody, która może powrócić z bloku poza metodę, do której został przekazany blok, ponieważ takie zachowanie powrotne wykluczałoby się wzajemnie z istniejącymi zwrotami. Nie wiem * dlaczego * został zaprojektowany w ten sposób, ale odpowiedź "dlaczego Ruby tego nie ma", a tym samym odpowiedź na pytanie Q1 (nie możesz), Q3 jest zatem nieistotny (nie jest to błąd być ustalonym, to celowy wybór wzajemnie wykluczających się alternatyw), Q2 będzie wymagało uzyskania odpowiedzi na parser i kod VM, ale odpowiedź brzmi: "kontekst nie jest odsłonięty". –
Co rozumiesz przez *, ponieważ takie zachowanie powrotne wykluczałoby się wzajemnie z istniejącymi zwrotnymi zachowaniami * i * celowym wyborem wzajemnie wykluczających się alternatyw *? Jakie pożądane zachowanie zostałoby złamane, gdyby zostało zaimplementowane, aby powrócić z metody wywołującej. Czy wykonanie procesu nie jest dodawane do stosu, gdy jest wywoływane? * dlaczego Ruby nie ma tego * oznacza, że jest to dodatkowa funkcja, która nie jest. – ndn