[python] Deferred output

Petr Messner petr.messner na gmail.com
Čtvrtek Únor 11 03:22:47 CET 2010


(odpověď je dole)

On 11.2.2010, at 2:35, matesfila wrote:
> 
> Zdravím, 
> 
> mám taký trochu algoritmický problém! 
> 
> Na adrese http://code.activestate.com/recipes/576961/ som sa dopátral k pre mňa dosť záhadnej algoritmickej konštrukcii, ktorú tam autor nazýva zhruba ako deferred output.  
> 
> Ide o tento algoritmus: 
> 
> def fibonacci(): 
>     """ Funkcia na počítanie Fibonachiho čísiel """ 
>     def deferred_output(): 
>         for i in output: 
>             yield i 
> 
>     result, c1, c2 = tee(deferred_output(), 3) 
>     paired = map(add, c1, islice(c2, 1, None)) 
>     output = chain([0, 1], paired) 
>     return result 
> 
> #príklad použitia funkcie: 
> print(list(islice(fibonacci(), 50))) 
> 
> Sú mi jasné metódy ako list, islice, chain a myslím, že aj map, tee a využitie generátora. 
> 
> Ale ako je možné, že v generátore sa využíva premenná, ktoré ešte nebola nikde definovaná? !!! Pravdepodobne sa tam nejako využíva vlastnosť generátora, ktorý premennnú 'output' využije vlastne až keď sa vyvolá next() na generátore a v tej dobe vlastne premenná už asi existuje... ale neviem, jaksik tie myšlienky tam využité neviem definovať a uchopiť :-) 
> 


Ano, výraz deferred_output() nespustí kód uvnitř funkce deferred_output; protože je to generátor (to se pozná už při parsování kódu), vrátí se iterátor a teprve při procházení iterátoru se spustí kód uvnitř deferred_output. A v tom okamžiku už "volná proměnná" output existuje.

Takovéto techniky bych rozhodně neoznačil za běžně používané v Pythonu.

Zrovna tento kód mi nefungoval, ale asi tuším, co to dělá - deferred_output() představuje nějaký iterátor; vytvoří se "nad ním" tři nezávislé iterátory, jeden z nich se vrátí, další dva slouží ke sčítání (posledního a předposledního prvku), které je vyjádřeno iterátorem paired, a ten se (spolu s počátečními prvky - 0 a 1) vloží do deferred_output.

Takže co se děje, když máš iterátor fibonacci() a zavoláš na něj next(): to, co máš, je vlastně iterátor z tee, to tee uchovává potřebnou historii (předposlední a poslední prvek, diky tomu, že další dva iterátory vzniklé z tee() právě na tyto prvky ukazují). Když chceš další prvek z result, vezme se další prvek z iterátoru vzniklého z map(), který je zadán jako součet hodnoty toho druhého a třetího iterátoru z tee(). Tedy součet předposledního a posledního čísla. Tento součet se zároveň uloží (funkcionalita tee). Asi jsem to popsal trochu zmateně, je prostě nutné se do toho kódu delší chvíli dívat :) 

PM



Další informace o konferenci Python