diff --git a/concepts/sequences/.meta/config.json b/concepts/sequences/.meta/config.json index 9b9e8da5a9..0399a1c47c 100644 --- a/concepts/sequences/.meta/config.json +++ b/concepts/sequences/.meta/config.json @@ -1,5 +1,5 @@ { - "blurb": "TODO: add blurb for this concept", - "authors": ["bethanyg", "cmccandless"], + "blurb": "A sequence is an ordered, indexable collection of items. All sequence types support a common set of operations, with lists supporting additional mutable operations. All sequences can be indexed into with bracket notation, copied in whole or in part using slice notation, and iterated over using the \"for item in \".", + "authors": ["bethanyg", "meatball133"], "contributors": [] } diff --git a/concepts/sequences/about.md b/concepts/sequences/about.md index c628150d56..14a8744ca8 100644 --- a/concepts/sequences/about.md +++ b/concepts/sequences/about.md @@ -1,2 +1,174 @@ -#TODO: Add about for this concept. +# Sequences +A sequence is an ordered, indexable collection of items. +All sequence types support a common set of operations that include `in`/`not in`, `min()`/`max()`, `.index`, `.count()` and `.len()`. +`lists` and `bytearray` support additional mutable operations such as [slice assignment][], `.append()`, `.extend()`, `.reverse()`, and `.copy()`. + +All sequences can be indexed into using `[]`, copied in whole or in part using `[::]`(\_a full copy can be made with `[:]`), and iterated over using the `for item in ` construct. +`for index, item in enumerate()` can be used when both the element index and the element value are needed. + +Pythons `list`, `tuple`, `str`, `bytes`, `bytearray` and `range` types all belong to this wider sequence type. +In the case of `str`, the “collection” is made up of unicode code points. +In the case of `bytes`, bytes. +Ranges are “collections” of numbers conforming to a `start:stop:step` rule. + +## Common Sequence operations + +### In operator + +`in` checks if a sequence contains a value. +It returns `True` if the value is found, and `False` otherwise. + +```python +>>> 1 in [1, 2, 3] +True + +>>> 4 in [1, 2, 3] +False + +>>> "a" in "abc" +True + +>>> "d" in "abc" +False +``` + +It does not check if a value is in a sequence within a sequence. + +```python +>>> "a" in ["abc", "def"] +False + +>>> 1 in [[1, 2, 3], [4, 5, 6]] +False +``` + +### Not in operator + +`Not in` checks if a sequence does not contain a value. +It does the reverse of `in`. + +```python +>>> 1 not in [1, 2, 3] +False + +>>> 4 not in [1, 2, 3] +True +``` + +### Get the length of a sequence + +`len()` gives the length of a sequence. + +```python +>>> len([1, 2, 3]) +3 + +>>> len((1, 2, 3)) +3 + +>>> len("abc") +3 +``` + +!!! Add that a nested sequence will not return the count of elements within sequences + +### min() and max() + +```exercism/caution +Using `max()/max()` on an `string` is tricky since it compares via unicode code point value. +Therefore when dealing with characters outside of the English alphabet, the result may not be what you expect. +``` + +`min()` gives the minimum value in a sequence. + +```python +>>> min([1, 2, 3]) +1 + +>>> min("abc") +'a' +``` + +`max()` gives the maximum value in a sequence. + +```python +>>> max([1, 2, 3]) +3 + +>>> max("abc") +'c' +``` + +Using `max()/min()` on an empty sequence will raise a `ValueError`. + +```python +>>> max([]) + +Traceback (most recent call last): + File "c:\sequence.py", line 3, in + print(max([])) + ^^^^^^^ +ValueError: max() arg is an empty sequence +``` + +Using `min()/max()` on a sequence that includes a number and a `string` will raise a `TypeError`. + +```python +>>> max([1, 2, 3, "a"]) + +Traceback (most recent call last): + File "c:\sequence.py", line 3, in + print(max([1, 2, 3, "a"])) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: '>' not supported between instances of 'str' and 'int' +``` + +### Reverse a sequence + +Using slicing with steps allows reversing a sequence. +That is because we can use a negative step to start at the end of the sequence and walk backwards. +This is a very common operation. + +```python +>>> numbers = [1, 2, 3, 4, 5] +>>> numbers[::-1] +[5, 4, 3, 2, 1] + +>>> name = "Delizald" +>>> name[::-1] +'dlazileD' +``` + +NOTES: + +Time table, tickets, ads. +Models of trams + +```python + +def time_table(table_of_time, week_day, start, stop): + return table_of_time[week_day][start:stop] + +print(time_table(("8:00", "9:00"),["8:00", "9:00"] )) +``` + +```python +def time_table(table_of_time, week_day, start, stop): + return table_of_time[week_day][start:stop] + +print(time_table(("8:00", "9:00"),["8:00", "9:00"] )) +``` + +Fastest route (last exercise) + +Fewest stops or transfers + +Ad function + +`min()`/`max()`, yes +`.index`, yes +`.count()`, maybe +`.len()`, maybe +`slicing`, yes +`slice assignment`, yes diff --git a/concepts/sequences/introduction.md b/concepts/sequences/introduction.md index fcde74642c..6c090cbde2 100644 --- a/concepts/sequences/introduction.md +++ b/concepts/sequences/introduction.md @@ -1,2 +1,16 @@ -#TODO: Add introduction for this concept. +# Sequences + +A sequence is an ordered, indexable collection of items. +All sequence types support a common set of operations that include `in`/`not in`, `min()`/`max()`, `.index`, `.count()` and `.len()`. +`lists` support additional mutable operations such as [slice assignment][], `.append()`, `.extend()`, `.reverse()`, and `.copy()`. + +All sequences can be indexed into using `[]`, copied in whole or in part using `[::]`(_a full copy can be made with `[:]`), and iterated over using the `for item in ` construct. + `for index, item in enumerate()` can be used when both the element index and the element value are needed. + + +Pythons `list`, `tuple`, `str`, `byte`, and `range` types all belong to this wider sequence type. +In the case of `str`, the “collection” is made up of unicode code points. +In the case of `byte`, bytes. +Ranges are “collections” of numbers conforming to a `start:stop:step` rule. + diff --git a/config.json b/config.json index 61cc1285f0..009aede5c3 100644 --- a/config.json +++ b/config.json @@ -141,6 +141,14 @@ "name": "Locomotive Engineer", "uuid": "e1b8b9c9-21c3-47b1-b645-5938b3110c78", "concepts": ["unpacking-and-multiple-assignment"], + "prerequisites": ["loops", "lists", "tuples", "dicts", "unpacking-and-multiple-assignment"], + "status": "wip" + }, + { + "slug": "thallias-tram-troubles", + "name": "Thallias Tram Troubles", + "uuid": "d1cf6301-d8fe-47a8-95a8-5ec0765aef7b", + "concepts": ["sequences"], "prerequisites": ["loops", "lists", "tuples", "dicts"], "status": "wip" }, diff --git a/exercises/concept/thallias-tram-troubles/.docs/hints.md b/exercises/concept/thallias-tram-troubles/.docs/hints.md new file mode 100644 index 0000000000..b5296c3664 --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/.docs/hints.md @@ -0,0 +1 @@ +# Hints diff --git a/exercises/concept/thallias-tram-troubles/.docs/instructions.md b/exercises/concept/thallias-tram-troubles/.docs/instructions.md new file mode 100644 index 0000000000..bcc118306c --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/.docs/instructions.md @@ -0,0 +1,53 @@ +# Instructions + +Your classmate Thallia has just won an internship with the city's premier tram company. +Her goal for the term of the internship is to improve communications with the ridership and promote special services. +She's decided to focus on writing several apps that can run on station platforms, the tram website, and rider's phones. +Since writing all these apps is a big task, she's asked that the tram company recruit you to help with the project. + +1. Schedules by Weekday Number + +First on the agenda is an app that can help commuters navigate the various schedules so they can more easily get to and from work. +Thallia's got the lines and locations mostly figured out, but would like you to write a function that will filter the timetable sequence for a given weekday, so riders can clearly see all the departure times. +The aim will be to return tram departure times from the timetable based on a weekday number (_0 being Monday, 6 being Sunday_). +Write the `time_table_for_a_weekday` function that takes a nested sequence of timetables and weekday number. +It should return the schedule sequence corresponding to the weekday number. + +```python +>>> time_table_for_a_weekday([["8:00", "17:00"], ("9:00", "16:00"), ("8:30", "15:00"), ["10:00", "19:00", "21:00"], ["12:00", "20:00"], ("9:00", "19:00"), ("9:30", "15:00", "20:00")], 3) +["10:00", "19:00", "21:00"] +``` + +2. Departures by Commute Window + +Now that you have the weekday filtering figured out, Thallia has asked you to drill down into specific commute times. +This part of the app is designed to allow a commuter to input their commute day plus a "commute window" and receive a sequence of departure times. +With the `time_table_for_a_specific_range` function that takes a sequence of daily timetables, a weekday number, and `start`/`stop` indexes for the "commute window". +It should return a sequence of departure times for the commuter that fits their travel day and "commute window". + +```python +>>> time_table_for_a_specific_range([["8:00", "17:00"], ("9:00", "16:00"), ("8:30", "15:00"), ["10:00", "19:00", "21:00"], ["12:00", "20:00"], ("9:00", "19:00"), ("9:30", "15:00", "20:00", "21:00")], 6, 1, 3) +("15:00", "20:00") +``` + +3. Calculate Route with Fewest Transfers + +The tram system has many routes and many different ways for commuters to reach their destinations. +However, transferring adds time and complexity to the process. +Thallia is trying to tackle this in-app by asking you to add a feature that will calculate routes with the fewest transfers. + +4. Calculate Fastest Route + +To up the tram company's visibility, Thallia has decided to add functionality similar to Googles "plan a trip" functionality. +But instead of providing all routes and times, she'd like you to provide commuters only with the fastest available routes listed in the timetables. + +5. Update Station Status Displays + +Having built up commuter features, Thallia now wants to focus a bit on weekend "family activity" services. +She'd like to update the station display software to better promote different activities (concerts, museums, amusement parks, zoos, sports matches, theater) that can be reached via different tram lines. +She's asked you to write a function that will insert a short blurb about an activity into the arrival and departure displays. + +6. Update Weekend Schedule Information + +Thallia would also like to update the app so that weekend riders can know when an event is scheduled and what train departures will get them to the activities on time. +She'd like you to write a function that will insert an activity description and start time into a given schedule display, so that riders are reminded that they can take the tram to the activity. diff --git a/exercises/concept/thallias-tram-troubles/.docs/introduction.md b/exercises/concept/thallias-tram-troubles/.docs/introduction.md new file mode 100644 index 0000000000..b15fe77524 --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/.docs/introduction.md @@ -0,0 +1 @@ +#todo diff --git a/exercises/concept/thallias-tram-troubles/.meta/config.json b/exercises/concept/thallias-tram-troubles/.meta/config.json new file mode 100644 index 0000000000..e5bd8afcac --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/.meta/config.json @@ -0,0 +1,10 @@ +{ + "blurb": "todo", + "icon": "tracks-on-tracks-on-tracks", + "authors": ["meatball133","BethanyG"], + "files": { + "solution": ["sequences.py"], + "test": ["sequences_test.py"], + "exemplar": [".meta/exemplar.py"] + } +} diff --git a/exercises/concept/thallias-tram-troubles/.meta/design.md b/exercises/concept/thallias-tram-troubles/.meta/design.md new file mode 100644 index 0000000000..af4109a9b0 --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/.meta/design.md @@ -0,0 +1,69 @@ +# Design + +## TODO: Add introduction for this concept. +## Goal + +This concept exercise is meant to teach an understanding/use of `unpacking` and the `*` (splat) and `**` (double splat) operators in Python. + +
+ +## Learning objectives + +- Understand/use `unpacking` through the use of `*` and `**` _prefix_ operators in various scenarios + - `*` and `**` as _prefixes_ ..... not to be confused with `*` (_multiply_) and `**` (_exponentiation_) as _infix_, or mathematical operators (**consider a link in the links doc or a mention in dig deeper.**) + - ~~what happens in the process of "unpacking" - form, ordering, & iteration~~ (this will go in a **dig deeper** or the link docs.) + - use in arguments to `functions` + - use in argument _capture_ for `functions` (_aka passing an arbitrary number of arguments -- *args * & **kwargs_) + - ~~use in defining `keyword only arguments`~~ (_topic moved to arguments exercise_). + - use in iterable (_mainly `tuple` and `list`_) unpacking & packing + - use in `dict` unpacking & packing +- Understand/use `unpacking` via `multiple assignment` + - using `multiple assignment ` in place of `indexing` + - using `multiple assigment` + `*` in place of `slicing` + - ~~using "list-like" syntax & "tuple-like" syntax~~ + - unpacking plus "leftovers" via `*` +- Differences between straight `multiple assignment` and `*` & `**` +- Deep unpacking + + +## Concepts + +- `unpacking` +- `unpacking generalizations` +- `multiple assignment` + + +## Topics that are Out of scope + +- `classes` +- `comprehensions` +- `comprehensions` in `lambdas` +- `map()`, `filter()` or `functools.reduce()` in a `comprehension` +- `function-arguments` beyond explaining briefly how `*`, `**` work in function arguments. +- `functools` beyond `functools.reduce()`(_this will get its own exercise_) +- `generators` +- using an `assignment expression` or "walrus" operator (`:=`) alone or in a `lambda` + + +## Prerequisites + +- `basics` +- `bools` +- `comparisons` +- `dicts` +- `lists` +- `numbers` +- `strings` +- `tuples` + + +## Representer + +This exercise does not require any specific logic to be added to the [representer][representer] + +## Analyzer + +This exercise does not require any specific logic to be added to the [analyzer][analyzer]. + +[analyzer]: https://github.com/exercism/python-analyzer +[representer]: https://github.com/exercism/python-representer \ No newline at end of file diff --git a/exercises/concept/thallias-tram-troubles/.meta/exemplar.py b/exercises/concept/thallias-tram-troubles/.meta/exemplar.py new file mode 100644 index 0000000000..89f9782d06 --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/.meta/exemplar.py @@ -0,0 +1,21 @@ +def time_table_for_a_weekday(table_of_time, week_day): + """Get the time table for a weekday. + + :parm table_of_time: sequence[sequence] - sequence of time tables. + :parm week_day: integer - the number of the weekday. + :return: sequence - sequence of timetables within a weekday . + """ + return table_of_time[week_day] + + +def time_table_for_a_specific_range(table_of_time, week_day, start, stop): + """Get the a slice of time table for a weekday. + + :parm table_of_time: sequence[sequence] - sequence of time tables. + :parm week_day: integer - the number of the weekday. + :parm start: integer - the start of the slice. + :parm stop: integer - the end of the slice. + :return: sequence - slice of timetables within a weekday . + """ + return table_of_time[week_day][start:stop] + diff --git a/exercises/concept/thallias-tram-troubles/thallias_tram_troubles.py b/exercises/concept/thallias-tram-troubles/thallias_tram_troubles.py new file mode 100644 index 0000000000..aeed8013b3 --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/thallias_tram_troubles.py @@ -0,0 +1,46 @@ +def time_table_for_a_weekday(table_of_time, week_day): + """Get the time table for a weekday. + + :parm table_of_time: sequence[sequence[sequence]] - sequence of time tables. + :parm week_day: integer - the number of the weekday. + :return: sequence - sequence of timetables within a weekday . + """ + pass + + +def time_table_for_a_specific_range(table_of_time, week_day, start, stop): + """Get the a slice of time table for a weekday. + + :parm table_of_time: sequence[sequence[sequence]] - sequence of time tables. + :parm week_day: integer - the number of the weekday. + :parm start: integer - the start of the slice. + :parm stop: integer - the end of the slice. + :return: sequence - slice of timetables within a weekday . + """ + pass + + +def fewest_transfers(routes): + """Find the fewest transfers between two stops. + + :parm route: sequence[sequence] - sequence of time tables. + :return: sequence - the fewest transfers. + """ + pass + + +def fastest_route(): + """Find the fastest route between two stops. + + :parm ... + :return: sequence - the fastest route. + """ + pass + +def update___(): + """Update the ... . + + :parm ... + :return: ... + """ + pass diff --git a/exercises/concept/thallias-tram-troubles/thallias_tram_troubles_test.py b/exercises/concept/thallias-tram-troubles/thallias_tram_troubles_test.py new file mode 100644 index 0000000000..dd00ceda9b --- /dev/null +++ b/exercises/concept/thallias-tram-troubles/thallias_tram_troubles_test.py @@ -0,0 +1,31 @@ +import unittest +import pytest + +from thallias_tram_troubles import time_table_for_a_weekday, time_table_for_a_specific_range, fewest_transfers, fastest_route, update___ + +class ThalliasTramTroublesTest(unittest.TestCase): + + @pytest.mark.task(taskno=1) + def test_time_table_for_a_weekday(self): + input_data = (([['7:00', '12:00', '19:00'], ('7:00', '13:00', '17:00'), ['7:00', '12:00', '17:00'], ('10:00', '12:00', '17:00'), ('7:00', '15:00', '17:00'), ['7:00', '12:00'], ['7:00', '12:00']],2), + ((('5:00', '7:00', '12:00', '15:00', '18:00'),('6:00', '8:00', '13:00', '14:00', '17:00'),('4:00', '6:00', '9:00', '12:00', '18:00'),('6:00', '7:30', '16:00', '18:00', '19:00'),('6:00', '9:00', '12:00', '13:00', '18:00'), ('7:00', '8:00', '10:00', '12:00', '19:00'),('7:00', '10:00', '12:00', '15:00', '18:00')), 6), + ([['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00'], ['4:00', '6:00', '8:00', '9:00', '11:00', '14:00', '17:00', '19:00', '21:00']],4) + ) + output_data = (['7:00', '12:00', '17:00'], ('7:00', '10:00', '12:00', '15:00', '18:00'), ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30']) + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data): + self.assertEqual(time_table_for_a_weekday(*input_data), output_data) + + @pytest.mark.task(taskno=2) + def test_time_table_for_a_specific_range(self): + input_data = ( + ([['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00', '23:30'], ['4:00', '6:00', '7:00', '8:00', '9:00', '11:00', '12:00', '14:00', '17:00', '19:00', '21:00'], ['4:00', '6:00', '8:00', '9:00', '11:00', '14:00', '17:00', '19:00', '21:00']],4, 2,5), + ((['4:00' ,'7:00', '8:00', '9:00', '15:00'], ['7:00', '8:00', '9:00', '18:00'], ['6:00', '7:00', '8:00', '9:00', '15:00'], ['6:00', '7:00', '8:00', '9:00', '18:00'], ['5:00', '7:00', '8:00', '9:00', '15:00'], ['5:00', '7:00', '8:00', '9:00', '18:00'], ['8:00', '9:00']), 1, 0, 1), + ((('9:00', '10:00', '14:00', '15:00', '18:00'),('9:00', '10:00', '14:00', '15:00', '18:00'),('9:00', '10:00', '14:00', '15:00', '18:00'),('9:00', '10:00', '14:00', '15:00', '18:00'),('9:00', '10:00', '14:00', '15:00', '18:00'), ('9:00', '10:00', '14:00', '15:00', '18:00'),('9:00', '10:00', '14:00', '15:00', '18:00')), 1, 3, 5), + ) + output_data = (['7:00', '8:00', '9:00'],['7:00'], ('15:00', '18:00')) + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data): + self.assertEqual(time_table_for_a_specific_range(*input_data), output_data)