Trilhas
/
Python
Python
/
Exercícios
/
Passagens aéreas
Passagens aéreas

Passagens aéreas

Exercício de aprendizagem

Introdução

Um generator é uma função ou expressão que retorna um tipo especial de iterador chamado generator iterator. Os generator-iterators são preguiçosos: eles não armazenam seus values na memória, mas geram seus valores quando necessário.

Uma função geradora se parece com qualquer outra função, mas contém uma ou mais expressões yield. Cada yield suspende a execução do código e salva o estado de execução atual (incluindo todas as variáveis locais e instruções try). Quando o gerador é retomado, ele recupera o estado a partir do ponto de suspensão, ao contrário das funções comuns, que são reiniciadas a cada chamada.

Construindo um gerador

Os geradores são construídos de forma muito parecida com outras funções de laço ou recursivas, mas exigem uma expressão yield, que veremos em detalhes mais adiante.

Um exemplo é uma função que retorna os quadrados de uma determinada lista de números. Do jeito que está escrita, toda a entrada precisa ser processada antes que qualquer valor possa ser retornado:

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

Você pode converter essa função em um gerador assim:

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

A razão disso é que você usa um gerador quando não precisa produzir todos os valores de uma vez. Isso economiza memória e poder de processamento, já que só o valor com que você está trabalhando no momento é calculado.

Usando um gerador

Os geradores podem ser usados no lugar da maioria dos iterables em Python. Isso inclui funções ou objetos que exigem um iterable/iterator como argumento.

Para usar o gerador squares_generator():

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

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

Os valores dentro de um generator também podem ser produzidos/acessados por meio da função next(). A função next() chama o método __next__() de um objeto gerador-iterador, "avançando" ou avaliando o código até a sua expressão yield, que então "produz" ou retorna um valor:

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

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

Quando um generator-iterator é totalmente consumido e não tem mais valores para retornar, ele lança um erro StopIteration.

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

Os geradores-iteradores são um subconjunto especial dos iteradores. Os Iterators são o mecanismo/protocolo que permite iterar sobre iteráveis. Os geradores-iteradores e os iteradores retornados pelos iterables comuns do Python se comportam de forma muito parecida, mas há algumas diferenças importantes a observar:

  • Eles são avaliados de forma preguiçosa; a iteração é unidirecional e não há como "voltar" para um valor anterior.
  • Eles são consumidos ao iterar sobre os valores retornados; não há reinicialização nem armazenamento na memória.
  • Eles não são ordenáveis e não podem ser invertidos.
  • Eles não são tipos de sequência e não têm indexes. Você não pode referenciar um valor anterior ou futuro usando adição ou subtração, e não pode usar a notação de colchetes ([]) nem o fatiamento.
  • Eles não podem ser usados com a função len(), pois não têm comprimento.
  • Eles podem ser finitos ou infinitos. Tome cuidado ao coletar todos os valores de um Generator-iterator infinito!

A expressão yield

A expressão yield é muito parecida com a expressão return. Ao contrário da expressão return, o yield entrega valores ao chamador em um ponto específico, suspendendo a avaliação/retorno de quaisquer valores adicionais até que eles sejam solicitados. Quando o yield é avaliado, ele pausa a execução da função que o contém e retorna quaisquer valores da função naquele momento. A função então permanece no escopo, e quando __next__() é chamado, a execução continua até que o yield seja encontrado novamente.

Note

O uso de expressões yield é proibido fora de funções.

>>> 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 que criar um gerador?

Os geradores são úteis em muitas aplicações.

Ao trabalhar com uma coleção potencialmente grande de valores, talvez você não queira colocá-los todos na memória. Um gerador pode ser usado para processar dados maiores peça por peça, economizando memória e melhorando o desempenho.

Os geradores também são muito úteis quando um processo ou cálculo é complexo, custoso ou infinito:

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

Agora, sempre que __next__() for chamado no objeto infinite_sequence, ele retornará o número anterior + 1.

Instruções

A Conda Airlines é a maior companhia aérea do mundo da programação, com mais de 10.000 voos por dia!

No momento, eles atribuem todos os assentos aos passageiros à mão; isso vai precisar ser automatizado.

Eles pediram para você criar um software para automatizar a atribuição de assentos aos passageiros. Eles exigem que seu software seja eficiente em memória e tenha bom desempenho.

1. Gerar as letras dos assentos

A Conda quer gerar as letras dos assentos para seus aviões. Um avião é feito de fileiras de assentos. Cada fileira tem 4 assentos. Os assentos de cada fileira sempre se chamam A, B, C e D. O primeiro assento da fileira é A, o segundo assento da fileira é B, e assim por diante. Depois de chegar em D, deve começar de novo com A.

Implemente uma função generate_seat_letters(<number>) que aceita um int com a quantidade de letras de assento a serem geradas. A função deve então retornar um iterável de letras de assento.

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

2. Gerar assentos

A Conda quer um sistema que consiga gerar um determinado número de assentos para seus aviões. Cada avião tem 4 assentos em cada fileira. As fileiras são definidas por números, começando em 1 e subindo. Os assentos devem estar ordenados, assim: 1A, 1B, 1C, 1D, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, ...

Aqui está um exemplo:

x 1 2
Row 5 21
Seat letter A D
Result 5A 21D

Muitas companhias aéreas não têm a fileira número 13 em seus voos, por causa da superstição entre os passageiros. A Conda Airlines também segue essa convenção, então certifique-se de não gerar assentos para a fileira número 13.

Implemente uma função generate_seats(<number>) que aceita um int com a quantidade de assentos a serem gerados. A função deve então retornar um iterável com os assentos gerados.

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

3. Atribuir assentos aos passageiros

Agora que você tem uma função que gera assentos, pode usá-la para atribuir assentos aos passageiros.

Implemente uma função assign_seats(<passengers>) que aceita uma list de nomes de passageiros. A função deve então retornar um dicionário com passenger como chave e 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 bilhete

A Conda Airlines gostaria de ter um código único para cada bilhete. Como são uma companhia aérea grande, eles têm muitos voos. Isso significa que há vários voos com o mesmo número de assento. Eles querem que você crie um sistema que formule um bilhete único que seja uma string de 12 caracteres.

Esse código começa com o assigned_seat seguido do flight_id. O restante do código é preenchido com 0s.

Implemente uma função generate_codes(<seat_numbers>, <flight_id>) que aceita uma list de seat_numbers e uma string com o número do voo. A função deve então retornar um generator que gera um 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 via GitHub O link abre em uma nova janela ou aba
Python Exercism

Tudo pronto para começar Passagens aéreas?

Crie sua conta no Exercism para aprender e dominar Python com 17 conceitos146 exercícios e mentoria humana de verdade, tudo de graça.