Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: Ħαℓ₸℮ℵ ␏⫢ ⦚ » 21. 08. 2019, 00:44:59
-
Zdravím do programátorského koutku, přidám se něco z Ruby. Je to problém, zda je možné nějak ohnout bloky a yield naruby , případě jak dostat blok do vnitřního bloku(proc).
Mám takový to příklad (důležité je, že proměnná generator je daná a že je to typ Proc nebo Lambda.)
generator = proc {a,b=0,1 ; while(true) do a,b=b,a+b ; yield ; break if b>120 ; end}
generator.call{|v|p v}
LocalJumpError: no block given (yield)
from (irb):3019:in `block in irb_binding'
from (irb):3018:in `call'
from (irb):3018
Jak předám blok (například {|res|puts res}, aby výsledek bylo vypsané 1\n2\n3,5,8...) tomuto Proc generator? Vím, že bloky jsou dělané na metody původně.
Samozřejmě to jde triviálně opravit(že předám blok jako argument, například pomocí &). Ale já právě chci zachovat původní formát
generator = proc {|&c|a,b=0,1 ; while(true) do a,b=b,a+b ; c.( b) ; break if b>120 ; end}
generator.call{|v|p v}
(v ruby pro Proc objekty jsou , prc.call a prc.yield , prc.() synonyma, a začíná v tom trochu bordel: .yield metody je něco jiného než yield klíčové slovo a prc() je chyba narozdíl od prc.() )
Pod čarou:
Inspirace vychází z konstrukce objektu Enumerator.new() (srozumitelnější pod pojmem Iterátor možná), který v konstruktoru přijímá blok (s argumentem Enumerator::Yielder, přes který vrací hodnoty). Tělo bloku (Enumerator::Generator) je generující funkce. A aby Enumerátor fungoval, tělo funkce hodnoty vracet přes Enumerator::Yielder pomocí metody .<< (alias .yield, jde ale o třetí odlišnou yield konstrukci!)
it=Enumerator.new{|ýíldr| a=0 ; loop do a+=1 ; ýíldr << a ; end}
it.next , it.take(40), it.first ...
# Proč to nejde takto? Resp. jak to udělat
it=Enumerator.new{ a=0 ; loop do a+=1 ; yield a ; end}
# Ono i když generátor si deklaruji jako funkci (nikoli proc), tak i tak je s tím dost ohýbání
def gen() ; a=1; loop { a+=10 ; yield a ; sleep 0.1 } end
e=Enumerator.new {|yi|gen {|w|yi<<w} }.take(8)
def Enum_method(m) ; return Enumerator.new{|y|method(m).call{|v|y<<v}} end
Enum_method(:gen).take(4)
Zkoušeno:
- Musím proc obalit do funkce, což nechci , chci mít proc samostatně (https://stackoverflow.com/questions/17353045/why-cant-i-pass-a-block-to-the-proc-in-ruby)
- https://stackoverflow.com/questions/19127645/can-i-pass-a-block-to-a-proc?noredirect=1& – Trvám vyloženě na použití yield
-
blok je lexikálně scopovaný, jestli proc vytváříte uvnitř funkce, použije se blok předaný funkci. Přes call to asi nejde, jestli to nějak jde, také bych to rád věděl.
K čemu to chcete používat moc nechápu.
it=Enumerator.new{ a=0 ; loop do a+=1 ; yield a ; end}
kde chcete předávat blok?
-
Kde předávat blok
To je narážka na to, že uvnitř bloku je yield a tedy že nějak by se do výrazu musel přidat další blok(aby ho mohl volat), ale to možné(syntaxe dovoluje jeden blok) není?
myslel jsem obecnou konstrukci
Libovolna_funkce_co_vrati_hotový_Enumerator(){ a=0 ; loop do a+=1 ; yield a ; end}
#případně
tmp=lambda{ a=0 ; loop do a+=1 ; yield a ; end}
Libovolna_funkce_co__vrati_hotovy_Enumerator(tmp)
-
vy chcete použít yield jako Yielder yield? To podle mě nejde, yield je klíčové slovo a chování je zadrátované natvrdo, zavolá blok. Nemůžete ani zavolat funkci, která se jmenuje yield. Jestli jde nějak dynamicky nastavit blok, nebo nějak jinak předat blok do proc v čase volání, rád bych věděl jak.
-
už o tom rozjímám několik dní
Blok samozřejmě předat jde
def funkce(proc-v-promenne, &blok-jako-promenna)
yield # blok-jako-prom hned zavolá, nelze ho uložit
blok-jako-promenna.call() #totéž jako výše, ale údajně výkonnostně horší, neboť su musí vytvořit closure či co
jinafunkce(blok-jako-promenna) # & z bloku vytvori proc
jinafunkce(proc-v-promenne) # jde s nim pracovat stejne
Proc.new # také vytvoří proc z bloku za předaného funkci
funkce(){print 4}
Prostě ruby je magický a mám ho rád. Jen je vtipné, že se opakuje furt principle of least astonishment. Ono to platí, když chce člověk, programovat přirozeně , ale když chce zkoumat jak ruby funguje, je udiven...
Pokud tě zajímá čtení:
https://stackoverflow.com/questions/2306731/using-yield-inside-define-method-in-ruby (celé)
https://banisterfiend.wordpress.com/2010/11/06/behavior-of-yield-in-define_method/ - Zde bych chtěl polopaticky vysvětlit That’s right, the yield in the define_method block is actually causing the hello method to close over the block passed to make_method.
a
Everyone knows that Ruby’s blocks close over local variables and constants but it appears they close over blocks too.
Asi pořádně nerozumím, co je closure(uzávěra) a tím spíš sloveso close(over)-"uzávěrovot"
-
Blok samozřejmě předat jde
implicitní blok do proc v čase volání podle mě jednoduše předat nejde. Jen v čase vytvoření.
def proc1
proc {yield}
end
p1 = proc1 {puts "proc1 called"}
p1.call # "proc1 called"
ve starém Ruby bylo možné získat kód pomocí sourcify a znovu vyevalovat v jiném kontextu, ale sourcify v novém Ruby nefunguje, alternativy neznám
Nejjednodušší je používat explicitní blok