Tracks
/
Python
Python
/
Ejercicios
/
Boletos de avión
Boletos de avión

Boletos de avión

Ejercicio de aprendizaje

Introducción

Un generator es una función o expresión que devuelve un tipo especial de iterador llamado generator iterator. Los Generator-iterators son perezosos: no almacenan sus values en memoria, sino que generan sus valores cuando se necesitan.

Una función generadora se ve como cualquier otra función, pero contiene una o más expresiones yield. Cada yield suspende la ejecución del código y guarda el estado de ejecución actual (incluidas todas las variables locales y las sentencias try). Cuando el generador se reanuda, retoma el estado desde donde se suspendió, a diferencia de las funciones normales, que se reinician con cada llamada.

Construir un generador

Los generadores se construyen de forma muy parecida a otras funciones con bucles o recursivas, pero requieren una expresión yield, que exploraremos con más detalle un poco más adelante.

Un ejemplo es una función que devuelve los cuadrados de un array de números dado. Tal como está escrita, se deben procesar todos los valores antes de poder devolver alguno:

>>> def squares(list_of_numbers):
...     squares = []
...     for number in list_of_numbers:
...         squares.append(number ** 2)
...     return squares

Puedes convertir esa función en un generador así:

>>> def squares_generator(list_of_numbers):
...     for number in list_of_numbers:
...         yield number ** 2

La razón de esto es que usas un generador cuando no necesitas producir todos los valores a la vez. Esto ahorra memoria y poder de procesamiento, ya que solo se calcula el valor en el que estás trabajando en ese momento.

Usar un generador

Los generadores pueden usarse en lugar de la mayoría de los iterables en Python. Esto incluye funciones u objetos que requieren un iterable/iterator como argumento.

Para usar el generador squares_generator():

>>> squared_numbers = squares_generator([1, 2, 3, 4])

>>> for square in squared_numbers:
...     print(square)
...
1
4
9
16

Los valores dentro de un generator también se pueden producir u obtener mediante la función next(). next() llama al método __next__() de un objeto iterador generador, «avanzando» o evaluando el código hasta su expresión yield, que luego «cede» o devuelve un valor:

>>> squared_numbers = squares_generator([1, 2])

>>> next(squared_numbers)
1
>>> next(squared_numbers)
4

Cuando un generator-iterator se consume por completo y no le quedan más valores por devolver, lanza un error StopIteration.

>>> next(squared_numbers)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Note

Los iteradores generadores son un subconjunto especial de los iteradores. Los Iterators son el mecanismo/protocolo que permite recorrer iterables. Los iteradores generadores y los iteradores que devuelven los iterables comunes de Python actúan de forma muy similar, pero hay algunas diferencias importantes que conviene señalar:

  • Se evalúan de forma perezosa; la iteración es unidireccional y no hay forma de «retroceder» a un valor anterior.
  • Se consumen al iterar sobre los valores devueltos; no se reinician ni se guardan en memoria.
  • No se pueden ordenar ni invertir.
  • No son tipos de secuencia y no tienen indexes. No puedes referirte a un valor anterior o futuro usando suma o resta, y no puedes usar la notación de corchetes ([]) ni el rebanado.
  • No se pueden usar con la función len(), ya que no tienen longitud.
  • Pueden ser finitos o infinitos. ¡Ten cuidado al recopilar todos los valores de un generator-iterator infinito!

La expresión yield

La expresión yield es muy similar a la expresión return. A diferencia de la expresión return, yield entrega valores a quien llama en un punto específico, suspendiendo la evaluación o el retorno de cualquier valor adicional hasta que se soliciten. Cuando se evalúa yield, pausa la ejecución de la función que lo contiene y devuelve los valores de la función en ese momento. Luego la función se mantiene en el ámbito y, cuando se llama a __next__(), la ejecución se reanuda hasta que se vuelve a encontrar yield.

Note

Usar expresiones yield está prohibido fuera de las funciones.

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

>>> lets_try = infinite_sequence()
>>> lets_try.__next__()
0
>>> lets_try.__next__()
1

¿Por qué crear un generador?

Los generadores son útiles en muchas aplicaciones.

Cuando trabajas con una colección de valores potencialmente grande, quizá no quieras ponerlos todos en memoria. Puedes usar un generador para trabajar con datos más grandes poco a poco, lo que ahorra memoria y mejora el rendimiento.

Los generadores también son muy útiles cuando un proceso o cálculo es complejo, costoso o infinito:

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

Ahora, cada vez que se llama a __next__() en el objeto infinite_sequence, devolverá el número anterior + 1.

Instrucciones

Conda Airlines es la aerolínea más grande del mundo de la programación, ¡con más de 10,000 vuelos al día!

Actualmente asignan todos los asientos a los pasajeros a mano; esto tendrá que automatizarse.

Te han pedido a ti que crees software para automatizar la asignación de asientos a los pasajeros. Requieren que tu software sea eficiente en memoria y con buen rendimiento.

1. Genera las letras de los asientos

Conda quiere generar las letras de los asientos de sus aviones. Un avión está hecho de filas de asientos. Cada fila tiene 4 asientos. Los asientos de cada fila siempre se llaman A, B, C y D. El primer asiento de la fila es A, el segundo asiento de la fila es B, y así sucesivamente. Después de llegar a D, debe empezar de nuevo con A.

Implementa una función generate_seat_letters(<number>) que acepte un int que indique cuántas letras de asiento se van a generar. La función debe devolver un iterable de letras de asiento.

>>> letters = generate_seat_letters(4)
>>> next(letters)
"A"
>>> next(letters)
"B"

2. Genera los asientos

Conda quiere un sistema que pueda generar una cantidad determinada de asientos para sus aviones. Cada avión tiene 4 asientos en cada fila. Las filas se definen con números, empezando desde 1 y subiendo. Los asientos deben ordenarse así: 1A, 1B, 1C, 1D, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, ...

Aquí tienes un ejemplo:

x 1 2
Fila 5 21
Letra del asiento A D
Resultado 5A 21D

Muchas aerolíneas no tienen la fila número 13 en sus vuelos, debido a la superstición de los pasajeros. Conda Airlines también sigue esta convención, así que asegúrate de no generar asientos para la fila número 13.

Implementa una función generate_seats(<number>) que acepte un int que indique cuántos asientos se van a generar. La función debe devolver un iterable con los asientos generados.

>>> seats = generate_seats(10)
>>> next(seats)
"1A"
>>> next(seats)
"1B"

3. Asigna los asientos a los pasajeros

Ahora que tienes una función que genera asientos, puedes usarla para asignar asientos a los pasajeros.

Implementa una función assign_seats(<passengers>) que acepte una list con los nombres de los pasajeros. La función debe devolver un diccionario con passenger como clave y seat_number como valor.

>>> passengers = ['Jerimiah', 'Eric', 'Bethany', 'Byte', 'SqueekyBoots', 'Bob']

>>> assign_seats(passengers)
{'Jerimiah': '1A', 'Eric': '1B', 'Bethany': '1C', 'Byte': '1D', 'SqueekyBoots': '2A', 'Bob': '2B'}

4. Códigos de los boletos

A Conda Airlines le gustaría tener un código único para cada boleto. Como son una aerolínea grande, tienen muchos vuelos. Esto significa que hay varios vuelos con el mismo número de asiento. Quieren que crees un sistema que formule un boleto único, que sea un string de 12 caracteres de longitud.

Este código empieza con el assigned_seat seguido del flight_id. El resto del código se rellena con 0s.

Implementa una función generate_codes(<seat_numbers>, <flight_id>) que acepte una list de seat_numbers y un string con el número de vuelo. La función debe devolver un generator que produzca un ticket_number.

>>> seat_numbers = ['1A', '17D']
>>> flight_id = 'CO1234'
>>> ticket_ids = generate_codes(seat_numbers, flight_id)

>>> next(ticket_ids)
'1ACO12340000'
>>> next(ticket_ids)
'17DCO1234000'
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Python Exercism

¿Todo listo para empezar Boletos de avión?

Regístrate en Exercism para aprender y dominar Python con 17 conceptos146 ejercicios y mentoría humana real, todo gratis.