Funcions generadores ==================== Què són? -------- - Són funcions tals que en el cos de la funció apareix una o més sentències :ref:`yield ` en comptes de sentències :ref:`return `. - La crida a una funció generadora retorna un iterador (anomenat també :term:`generador `). - L'execució de la funció comença en cridar :func:`next` sobre l'iterador i se suspèn en executar la primera sentència :code:`yield`. Els valors de les variables de la funció es conserven. - :code:`next` retorna l'objecte al que s'avalua l'expressió de la sentència :code:`yield`. - L'execució de la funció es reprèn en cridar altre cop :code:`next` sobre l'iterador. Les variables de la funció conserven el valor que tenien quan es va suspendre. Com transformar funcions que calculen llistes? ---------------------------------------------- Partim d'una funció que calcula una llista començant per la llista buida i afegint al final de la llista cada un dels elements que va calculant. Per exemple, la funció :func:`~digits.digits_llista` que donat un nombre natural, calcula la llista amb els dígits del nombre: .. literalinclude:: digits.py :language: python3 :linenos: :start-after: # digits_llista # :end-before: # fi digits_llista # .. literalinclude:: digits.txt :language: pycon :start-after: # digits_llista # :end-before: # fi digits_llista # Per tal de transformar-la en una funció generadora cal fer el següent: #. Esborrem la inicialització de la llista i la sentència :code:`return`. #. Substituïm la crida al mètode :meth:`~list.append` per la sentència :ref:`yield `. Seguint l'exemple, la funció :func:`~digits.digits` l'hem obtingut de :func:`~digits.digits_llista` aplicant els canvis anteriors, és a dir, esborrant les línies 2 i 7, i substituint l\':code:`append` de a línia 5 per :code:`yield`. .. literalinclude:: digits.py :language: python3 :start-after: # digits # :end-before: # fi digits # La crida a la funció :func:`~digits.digits` retorna un iterador. El recorregut d'aquest iterador obtindrà els mateixos elements i en el mateix ordre en què estaven a la llista que retornava la funció :func:`~digits.digits_llista` .. literalinclude:: digits.txt :language: pycon :start-after: # digits # :end-before: # fi digits # Vegeu l'execució de la funció generadora `digits al Python Tutor`_. Per què usar funcions generadores? ---------------------------------- - Una funció generadora calcula els elements d'una seqüència, successió o sèrie. Diguem-ne :math:`\mathcal{S}`. - El problema de calcular :math:`\mathcal{S}` està resolt a la funció generadora. - Sempre que calguin els elements de la seqüencia :math:`\mathcal{S}` per resoldre un problema, podrem cridar la funció generadora en comptes de tornar a resoldre el problema de calcular els elements d\':math:`\mathcal{S}`. Per exemple, a l'exercici :doc:`fi:temes/iteracions/Dígits_d_un_nombre_enter/index` cal implementar una funció que calculi la suma dels dígits d'un enter i una altra que digui si un nobre enter té el dígit donat. Totes dues fan un tractament sobre la seqüència de dígits d'un nombre enter i poden aprofitar el càlcul que fa la funció generadora :func:`~digits.digits`: .. literalinclude:: digits.py :language: python3 :start-after: # suma_digits # :end-before: # fi suma_digits # .. literalinclude:: digits.py :language: python3 :start-after: # conte_digit # :end-before: # fi conte_digit # Exemples -------- - `Successió de Fibonacci `__: :download:`fib.py`, :download:`fib.txt`. .. literalinclude:: fib.py :language: python3 .. literalinclude:: fib.txt :language: pycon - Progressions `aritmètica `__ i `geomètrica `__: :download:`prog.py`, :download:`prog.txt`. .. literalinclude:: prog.py :language: python3 .. important:: Observeu que les progressions aritmètica i geomètrica són successions infinites. Una funció generadora que calcula un iterador infinit normalment s'implementa amb una sentència :code:`while` amb l'expressió :code:`True`. .. danger:: Una sentència :code:`while` amb l'expressió :code:`True` només té sentit en una funció generadora, **mai** en una funció. .. literalinclude:: prog.txt :language: pycon .. danger:: Cal anar en compte al recòrrer un iterador infinit. Cal recòrrer només alguns elements com en els exemples anteriors. Si els intentem recòrrer tots, el programa no acabarà. Per exemple, el fragment següent no acabarà mai: .. code-block:: python3 it = aritmetica(3, 2) for e in it: print(e) - :doc:`fi:temes/iteracions/Nombres_triangulars/index`: :download:`triangulars.py`. .. literalinclude:: triangulars.py :language: python3 - Iterador ordenat? :download:`ord.py`, :download:`ord.txt`. .. literalinclude:: ord.py :language: python3 .. literalinclude:: ord.txt :language: pycon .. - `Divisors d’un enter `_, `Propietats dels nombres `_. - `Nombres primers i triangulars `_. - `Sèrie alternada pel logaritme `_. .. _digits al Python Tutor: http://pythontutor.com/visualize.html#code=def%20digits(n%29%3A%0A%20%20%20%20while%20n%20!%3D%200%3A%0A%20%20%20%20%20%20%20%20d%20%3D%20n%20%25%2010%0A%20%20%20%20%20%20%20%20yield%20d%0A%20%20%20%20%20%20%20%20n%20%3D%20n%20//%2010%0A%0Ait1%20%3D%20digits(315%29%0Ad1%20%3D%20next(it1%29%0Ad2%20%3D%20next(it1%29%0Ad3%20%3D%20next(it1%29%0A%23%20L'iterador%20s'ha%20esgotat%0Afor%20d%20in%20it1%3A%0A%20%20%20%20print(d%29%0A%23%20Calculem%20un%20nou%20iterador%0Ait2%20%3D%20digits(315%29%0Afor%20d%20in%20it2%3A%0A%20%20%20%20print(d%29%0A%20%20%20%20&cumulative=false&curInstr=0&heapPrimitives=false&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false