primeiro commit com exercicios de Exercism realizados até esta data

This commit is contained in:
2026-10-17 22:10:27 +01:00
commit 58fccbe27a
708 changed files with 35038 additions and 0 deletions
@@ -0,0 +1,23 @@
{
"authors": [
"J08K"
],
"contributors": [
"BethanyG",
"kytrinyx",
"meatball133"
],
"files": {
"solution": [
"generators.py"
],
"test": [
"generators_test.py"
],
"exemplar": [
".meta/exemplar.py"
]
},
"icon": "new-passport",
"blurb": "Learn about generators by assigning seats to passengers on Anaconda Airlines."
}
@@ -0,0 +1 @@
{"track":"python","exercise":"plane-tickets","id":"754d7ba9211d45619b389d7835720cbd","url":"https://exercism.org/tracks/python/exercises/plane-tickets","handle":"sanctushck","is_requester":true,"auto_approve":false}
+130
View File
@@ -0,0 +1,130 @@
# Help
## Running the tests
We use [pytest][pytest: Getting Started Guide] as our website test runner.
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
You should also install the following `pytest` plugins:
- [pytest-cache][pytest-cache]
- [pytest-subtests][pytest-subtests]
Extended information can be found in our website [Python testing guide][Python track tests page].
### Running Tests
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
Linux/MacOS
```bash
$ cd {path/to/exercise-folder-location}
```
Windows
```powershell
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
```
<br>
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
Linux/MacOS
```bash
$ python3 -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
Windows
```powershell
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
### Common options
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
- `-v` : enable verbose output.
- `-x` : stop running tests on first failure.
- `--ff` : run failures from previous test before running other test cases.
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
### Fixing warnings
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
```bash
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
```
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
You can also create your own `pytest.ini` file with the following content:
```ini
[pytest]
markers =
task: A concept exercise task.
```
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
### Extending your IDE or Code Editor
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
[pytest-cache]:http://pythonhosted.org/pytest-cache/
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
## Submitting your solution
You can submit your solution using the `exercism submit generators.py` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Python track's documentation](https://exercism.org/docs/tracks/python)
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
Below are some resources for getting help if you run into trouble:
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
- [The Exercism Community on Discord](https://exercism.org/r/discord)
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
- [Python Community Forums](https://discuss.python.org/)
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
+24
View File
@@ -0,0 +1,24 @@
# Hints
## 1. Generate seat letters
- The returned value should be of _type_ `generator`.
- You can have a sequence of letters from `A` to `D` and cycle through them.
- Use `yield` to return the next letter.
## 2. Generate seats
- The returned value should be of _type_ `generator`.
- Row `13` should be skipped, so go from `12` to `14`.
- Keep in mind that the returned values should be ordered from low to high. `1A, 1B, 2A, ...`
- Here it might be good to reuse or call functions you have already defined.
## 3. Assign seats to passengers
- Make sure your seat numbers do not have any spaces in them.
- Here it might be good to reuse or call functions you have already defined.
## 4. Ticket codes
- You can use `len()` to get the length of a string.
- You can use `"<string>" * <int>` to repeat a string.
+265
View File
@@ -0,0 +1,265 @@
# Plane Tickets
Welcome to Plane Tickets on Exercism's Python Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
A `generator` is a function or expression that returns a special type of [iterator][iterator] called [generator iterator][generator-iterator].
`Generator-iterators` are [lazy][lazy iterator]: they do not store their `values` in memory, but _generate_ their values when needed.
A generator function looks like any other function, but contains one or more [yield expressions][yield expression].
Each `yield` will suspend code execution, saving the current execution state (_including all local variables and try-statements_).
When the generator resumes, it picks up state from the suspension - unlike regular functions which reset with every call.
## Constructing a generator
Generators are constructed much like other looping or recursive functions, but require a [`yield` expression](#the-yield-expression), which we will explore in depth a bit later.
An example is a function that returns the _squares_ from a given list of numbers.
As currently written, all input must be processed before any values can be returned:
```python
>>> def squares(list_of_numbers):
... squares = []
... for number in list_of_numbers:
... squares.append(number ** 2)
... return squares
```
You can convert that function into a generator like this:
```python
>>> def squares_generator(list_of_numbers):
... for number in list_of_numbers:
... yield number ** 2
```
The rationale behind this is that you use a generator when you do not need to produce all the values _at once_.
This saves memory and processing power, since only the value you are _currently working on_ is calculated.
## Using a generator
Generators may be used in place of most `iterables` in Python.
This includes _functions_ or _objects_ that require an `iterable`/`iterator` as an argument.
To use the `squares_generator()` generator:
```python
>>> squared_numbers = squares_generator([1, 2, 3, 4])
>>> for square in squared_numbers:
... print(square)
...
1
4
9
16
```
Values within a `generator` can also be produced/accessed via the `next()` function.
`next()` calls the `__next__()` method of a generator-iterator object, "advancing" or evaluating the code up to its `yield` expression, which then "yields" or returns a value:
```python
>>> squared_numbers = squares_generator([1, 2])
>>> next(squared_numbers)
1
>>> next(squared_numbers)
4
```
When a `generator-iterator` is fully consumed and has no more values to return, it throws a `StopIteration` error.
```python
>>> next(squared_numbers)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
```
~~~~exercism/note
Generator-iterators are a special sub-set of [iterators][iterator].
`Iterators` are the mechanism/protocol that enables looping over _iterables_.
Generator-iterators and the iterators returned by common Python [`iterables`][iterables] act very similarly, but there are some important differences to note:
- They are _[lazily evaluated][lazy evaluation]_; iteration is _one-way_ and there is no "backing up" to a previous value.
- They are _consumed_ by iterating over the returned values; there is no resetting or saving in memory.
- They are not sortable and cannot be reversed.
- They are not sequence types, and _do not_ have `indexes`.
You cannot reference a previous or future value using addition or subtraction and you cannot use bracket (`[]`) notation or slicing.
- They cannot be used with the `len()` function, as they have no length.
- They can be _finite_ or _infinite_ - be careful when collecting all values from an _infinite_ `generator-iterator`!
[iterator]: https://docs.python.org/3.11/glossary.html#term-iterator
[iterables]: https://wiki.python.org/moin/Iterator
[lazy evaluation]: https://en.wikipedia.org/wiki/Lazy_evaluation
~~~~
## The yield expression
The [yield expression][yield expression] is very similar to the `return` expression.
_Unlike_ the `return` expression, `yield` gives up values to the caller at a _specific point_, suspending evaluation/return of any additional values until they are requested.
When `yield` is evaluated, it pauses the execution of the enclosing function and returns any values of the function _at that point in time_.
The function then _stays in scope_, and when `__next__()` is called, execution resumes until `yield` is encountered again.
~~~~exercism/note
Using `yield` expressions is prohibited outside of functions.
~~~~
```python
>>> 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
```
## Why Create a Generator?
Generators are useful in a lot of applications.
When working with a potentially large collection of values, you might not want to put all of them into memory.
A generator can be used to work on larger data piece-by-piece, saving memory and improving performance.
Generators are also very helpful when a process or calculation is _complex_, _expensive_, or _infinite_:
```python
>>> def infinite_sequence():
... current_number = 0
... while True:
... yield current_number
... current_number += 1
```
Now whenever `__next__()` is called on the `infinite_sequence` object, it will return the _previous number_ + 1.
[generator-iterator]: https://docs.python.org/3.11/glossary.html#term-generator-iterator
[iterables]: https://wiki.python.org/moin/Iterator
[iterator]: https://docs.python.org/3.11/glossary.html#term-iterator
[lazy evaluation]: https://en.wikipedia.org/wiki/Lazy_evaluation
[lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation
[yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions
## Instructions
Conda Airlines is the programming-world's biggest airline, with over 10,000 flights a day!
They are currently assigning all seats to passengers by hand; this will need to be automated.
They have asked _you_ to create software to automate passenger seat assignments.
They require your software to be memory efficient and performant.
## 1. Generate seat letters
Conda wants to generate seat letters for their airplanes.
An airplane is made of rows of seats.
Each row has _4 seats_.
The seats in each row are always named `A`, `B`, `C`, and `D`.
The first seat in the row is `A`, the second seat in the row is `B`, and so on.
After reaching `D`, it should start again with `A`.
Implement a function `generate_seat_letters(<number>)` that accepts an `int` that holds how many seat letters to be generated.
The function should then return an _iterable_ of seat letters.
```python
>>> letters = generate_seat_letters(4)
>>> next(letters)
"A"
>>> next(letters)
"B"
```
## 2. Generate seats
Conda wants a system that can generate a given number of seats for their airplanes.
Each airplane has _4 seats_ in each row.
The rows are defined using numbers, starting from `1` and going up.
The seats should be ordered, like: `1A`, `1B`, `1C`, `1D`, `2A`, `2B`, `2C`, `2D`, `3A`, `3B`, `3C`, `3D`, ...
Here is an example:
| x | 1 | 2 |
| :---------: | :-: | :-: |
| Row | 5 | 21 |
| Seat letter | A | D |
| Result | 5A | 21D |
Many airlines do not have _row_ number 13 on their flights, due to superstition amongst passengers.
Conda Airlines also follows this convention, so make sure you _don't_ generate seats for _row_ number 13.
Implement a function `generate_seats(<number>)` that accepts an `int` that holds how many seats to be generated.
The function should then return an _iterable_ of seats given.
```python
>>> seats = generate_seats(10)
>>> next(seats)
"1A"
>>> next(seats)
"1B"
```
## 3. Assign seats to passengers
Now that you have a function that generates seats, you can use it to assign seats to passengers.
Implement a function `assign_seats(<passengers>)` that accepts a `list` of passenger names.
The function should then return a _dictionary_ of `passenger` as _key_, and `seat_number` as _value_.
```python
>>> passengers = ['Jerimiah', 'Eric', 'Bethany', 'Byte', 'SqueekyBoots', 'Bob']
>>> assign_seats(passengers)
{'Jerimiah': '1A', 'Eric': '1B', 'Bethany': '1C', 'Byte': '1D', 'SqueekyBoots': '2A', 'Bob': '2B'}
```
## 4. Ticket codes
Conda Airlines would like to have a unique code for each ticket.
Since they are a big airline, they have a lot of flights.
This means that there are multiple flights with the same seat number.
They want you to create a system that creates a unique ticket that is _12_ characters long string code for identification.
This code begins with the `assigned_seat` followed by the `flight_id`.
The rest of the code is appended by `0s`.
Implement a function `generate_codes(<seat_numbers>, <flight_id>)` that accepts a `list` of `seat_numbers` and a `string` with the flight number.
The function should then return a `generator` that yields a `ticket_number`.
```python
>>> seat_numbers = ['1A', '17D']
>>> flight_id = 'CO1234'
>>> ticket_ids = generate_codes(seat_numbers, flight_id)
>>> next(ticket_ids)
'1ACO12340000'
>>> next(ticket_ids)
'17DCO1234000'
```
## Source
### Created by
- @J08K
### Contributed to by
- @BethanyG
- @kytrinyx
- @meatball133
+72
View File
@@ -0,0 +1,72 @@
"""Functions to automate Conda airlines ticketing system."""
def generate_seat_letters(number):
"""Generate a series of letters for airline seats.
:param number: int - total number of seat letters to be generated.
:return: generator - generator that yields seat letters.
Seat letters are generated from A to D.
After D it should start again with A.
Example: A, B, C, D
"""
seat_letters: list = ["A", "B", "C", "D"]
for seat_number in range(number):
yield seat_letters[seat_number % len(seat_letters)]
def generate_seats(number):
"""Generate a series of identifiers for airline seats.
:param number: int - total number of seats to be generated.
:return: generator - generator that yields seat numbers.
A seat number consists of the row number and the seat letter.
There is no row 13.
Each row has 4 seats.
Seats should be sorted from low to high.
Example: 3C, 3D, 4A, 4B
"""
seat_letters = generate_seat_letters(number)
row_number = 1
for seat_number in range(number):
if not seat_number % 4 and seat_number > 0:
row_number += 1
if row_number == 13:
row_number += 1
yield f"{row_number}{next(seat_letters)}"
def assign_seats(passengers):
"""Assign seats to passengers.
:param passengers: list[str] - a list of strings containing names of passengers.
:return: dict - with the names of the passengers as keys and seat numbers as values.
Example output: {"Adele": "1A", "Björk": "1B"}
"""
seats = generate_seats(len(passengers))
assign_seats_dict: dict = {}
for passenger in passengers:
assign_seats_dict[passenger] = next(seats)
return assign_seats_dict
def generate_codes(seat_numbers, flight_id):
"""Generate codes for a ticket.
:param seat_numbers: list[str] - list of seat numbers.
:param flight_id: str - string containing the flight identifier.
:return: generator - generator that yields 12 character long ticket codes.
"""
for seat in seat_numbers:
yield f"{seat}{flight_id}".ljust(12, "0")
+140
View File
@@ -0,0 +1,140 @@
import inspect
import unittest
import pytest
from generators import (
generate_seat_letters,
generate_seats,
assign_seats,
generate_codes
)
class PlaneTicketsTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_task1_returns_generator(self):
"""Test if generate_seat_letters() returns a generator type."""
number = 5
error_message = (f'Called generate_seat_letters({number}). '
f'The function returned a {type(generate_seat_letters(number))} type, '
f"but the tests expected the function to return a <class 'generator'> type.")
self.assertTrue(inspect.isgenerator(generate_seat_letters(number)), msg=error_message)
@pytest.mark.task(taskno=1)
def test_generate_seat_letters(self):
test_data = [1, 2, 3, 4, 5]
result_data = [["A"],
["A", "B"],
["A", "B", "C"],
["A", "B", "C", "D"],
["A", "B", "C", "D", "A"]]
for variant, (number, expected) in enumerate(zip(test_data, result_data), start=1):
with self.subTest(f"variation #{variant}", number=number, expected=expected):
actual_result = list(generate_seat_letters(number))
error_message = (f'Called generate_seat_letters({number}). '
f'The function returned {actual_result}, but the tests '
f'expected {expected} when generating {number} seat(s).')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_task2_returns_generator(self):
"""Test if generate_seats() returns a generator type."""
number = 7
error_message = (f'Called generate_seats({number}). '
f'The function returned a {type(generate_seats(number))} type, '
f"but the tests expected the function to return a <class 'generator'> type.")
self.assertTrue(inspect.isgenerator(generate_seats(number)), msg=error_message)
@pytest.mark.task(taskno=2)
def test_generate_seats(self):
test_data = [1, 2, 3, 4, 5]
result_data = [["1A"],
["1A", "1B"],
["1A", "1B", "1C"],
["1A", "1B", "1C", "1D"],
["1A", "1B", "1C", "1D", "2A"]]
for variant, (number, expected) in enumerate(zip(test_data, result_data), start=1):
with self.subTest(f"variation #{variant}", number=number, expected=expected):
actual_result = list(generate_seats(number))
error_message = (f'Called generate_seats({number}). '
f'The function returned {actual_result}, but the tests '
f'expected {expected} when generating {number} seat(s).')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_generate_seats_skips_row_13(self):
test_data = [14 * 4]
result_data = [["1A", "1B", "1C", "1D", "2A", "2B", "2C", "2D",
"3A", "3B", "3C", "3D", "4A", "4B", "4C", "4D",
"5A", "5B", "5C", "5D", "6A", "6B", "6C", "6D",
"7A", "7B", "7C", "7D", "8A", "8B", "8C", "8D",
"9A", "9B", "9C", "9D", "10A", "10B", "10C", "10D",
"11A", "11B", "11C", "11D", "12A", "12B", "12C", "12D",
"14A", "14B", "14C", "14D", "15A", "15B", "15C", "15D"]]
for variant, (number, expected) in enumerate(zip(test_data, result_data), start=1):
with self.subTest(f"variation #{variant}", number=number, expected=expected):
actual_result = list(generate_seats(number))
error_message = (f'Called generate_seats({number}). '
f'The function returned {actual_result}, but the tests '
f'expected: {expected}, when generating {number} seat(s).')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=3)
def test_assign_seats(self):
test_data = [["Passenger1", "Passenger2", "Passenger3", "Passenger4", "Passenger5"],
["TicketNo=5644", "TicketNo=2273", "TicketNo=493", "TicketNo=5411", "TicketNo=824"]]
result_data = [{"Passenger1": "1A", "Passenger2": "1B",
"Passenger3": "1C", "Passenger4": "1D", "Passenger5": "2A"},
{"TicketNo=5644": "1A", "TicketNo=2273": "1B",
"TicketNo=493": "1C", "TicketNo=5411": "1D", "TicketNo=824": "2A"}]
for variant, (passengers, expected) in enumerate(zip(test_data, result_data), start=1):
with self.subTest(f"variation #{variant}", passengers=passengers, expected=expected):
actual_result = assign_seats(passengers)
error_message = (f'Called assign_seats({passengers}). '
f'The function returned {actual_result}, but the tests '
f'expected {expected}, when assigning seats.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=4)
def test_task4_returns_generator(self):
"""Test if generate_codes() returns a generator type."""
seat_numbers, flight_id = "11B", "HA80085"
error_message = (f'Called generate_codes({seat_numbers}, {flight_id}). '
f'The function returned a {type(generate_codes(seat_numbers, flight_id))} type, '
f"but the tests expected the function to return a <class 'generator'> type.")
self.assertTrue(inspect.isgenerator(generate_codes(seat_numbers, flight_id)), msg=error_message)
@pytest.mark.task(taskno=4)
def test_generate_codes(self):
test_data = [(["12A", "38B", "69C", "102B"],"KL1022"),
(["22C", "88B", "33A", "44B"], "DL1002")]
result_data = [['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200'],
['22CDL1002000', '88BDL1002000', '33ADL1002000', '44BDL1002000']]
for variant, ((seat_numbers, flight_id), expected) in enumerate(zip(test_data, result_data), start=1):
with self.subTest(f"variation #{variant}", seat_numbbers=seat_numbers,
flight_id=flight_id, expected=expected):
actual_result = list(generate_codes(seat_numbers, flight_id))
error_message = (f'Called generate_codes({seat_numbers}, {flight_id}). '
f'The function returned {actual_result}, but the tests '
f'expected {expected} when generating ticket numbers.')
# Note: DO NOT call the function here again, in case the student is using list.pop()
# to process the input. If another call is done with that condition,
# the test will fail with a terrible error message.
self.assertEqual(actual_result, expected, msg=error_message)