From 1772b0794f8ad10cbdefae5e14aa321933811c3f Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Tue, 14 Mar 2023 16:26:34 +0100
Subject: [PATCH 001/126] Sync sum-of-multiples docs with
problem-specifications
The sum-of-multiples exercise has been improved upstream in the
problem-specifications repository.
For more context, please see the pull request that updated the exercise:
- https://github.com/exercism/problem-specifications/pull/2231
---
.../sum-of-multiples/.docs/instructions.md | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/sum-of-multiples/.docs/instructions.md b/exercises/practice/sum-of-multiples/.docs/instructions.md
index ff7fdffd86a..7b7ec006e2c 100644
--- a/exercises/practice/sum-of-multiples/.docs/instructions.md
+++ b/exercises/practice/sum-of-multiples/.docs/instructions.md
@@ -1,7 +1,18 @@
# Instructions
-Given a number, find the sum of all the unique multiples of particular numbers up to but not including that number.
+Given a list of factors and a limit, add up all the unique multiples of the factors that are less than the limit.
+All inputs will be greater than or equal to zero.
-If we list all the natural numbers below 20 that are multiples of 3 or 5, we get 3, 5, 6, 9, 10, 12, 15, and 18.
+## Example
-The sum of these multiples is 78.
+Suppose the limit is 20 and the list of factors is [3, 5].
+We need to find the sum of all unique multiples of 3 and 5 that are less than 20.
+
+Multiples of 3 less than 20: 3, 6, 9, 12, 15, 18
+Multiples of 5 less than 20: 5, 10, 15
+
+The unique multiples are: 3, 5, 6, 9, 10, 12, 15, 18
+
+The sum of the unique multiples is: 3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78
+
+So, the answer is 78.
From c4d8546f37c741d46d39a978a663b615f371c3d8 Mon Sep 17 00:00:00 2001
From: Hoai-Thu Vuong
Date: Fri, 10 Mar 2023 14:18:29 +0700
Subject: [PATCH 002/126] fix typo in the instruction of guidos-gorgeous
- remove double in
---
exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
index 106e30a392f..2d61cec837d 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
@@ -57,7 +57,7 @@ def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time):
:param number_of_layers: int - the number of layers in the lasagna.
:param elapsed_bake_time: int - elapsed cooking time.
- :return: int - total time elapsed (in in minutes) preparing and cooking.
+ :return: int - total time elapsed (in minutes) preparing and cooking.
This function takes two integers representing the number of lasagna layers and the
time already spent baking and calculates the total elapsed minutes spent cooking the
From 8e5c58583b195ebc57cb0b5a866ad3ced9e5d872 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Thu, 16 Mar 2023 12:38:59 +0100
Subject: [PATCH 003/126] Sync binary-search docs with problem-specifications
There were a few follow-on tweaks to binary-search.
For context, this is part of the project to overhaul all the practice exercises.
You can read about that here:
https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
----
If you approve this pull request, I will eventually merge it. However, if you are happy with this change **please merge the pull request**, as it will get the changes into the hands of the students much more quickly.
If this pull request contradicts the exercise on your track, **please add a review with _request changes_**. This will block the pull request from getting merged.
Otherwise we aim to take an optimistic merging approach.
If you wish to suggest tweaks to these changes, please open a pull request to the exercism/problem-specifications repository to discuss, so that everyone who has an interest in the shared exercise descriptions can participate.
---
.../practice/binary-search/.docs/instructions.md | 15 ++++++++-------
.../practice/binary-search/.docs/introduction.md | 6 +++++-
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/binary-search/.docs/instructions.md b/exercises/practice/binary-search/.docs/instructions.md
index 175c4c4ba8f..d7f1c899229 100644
--- a/exercises/practice/binary-search/.docs/instructions.md
+++ b/exercises/practice/binary-search/.docs/instructions.md
@@ -5,17 +5,18 @@ Your task is to implement a binary search algorithm.
A binary search algorithm finds an item in a list by repeatedly splitting it in half, only keeping the half which contains the item we're looking for.
It allows us to quickly narrow down the possible locations of our item until we find it, or until we've eliminated all possible locations.
-```exercism/caution
+~~~~exercism/caution
Binary search only works when a list has been sorted.
-```
+~~~~
The algorithm looks like this:
-- Divide the sorted list in half and compare the middle element with the item we're looking for.
-- If the middle element is our item, then we're done.
-- If the middle element is greater than our item, we can eliminate that number and all the numbers **after** it.
-- If the middle element is less than our item, we can eliminate that number and all the numbers **before** it.
-- Repeat the process on the part of the list that we kept.
+- Find the middle element of a sorted list and compare it with the item we're looking for.
+- If the middle element is our item, then we're done!
+- If the middle element is greater than our item, we can eliminate that element and all the elements **after** it.
+- If the middle element is less than our item, we can eliminate that element and all the elements **before** it.
+- If every element of the list has been eliminated then the item is not in the list.
+- Otherwise, repeat the process on the part of the list that has not been eliminated.
Here's an example:
diff --git a/exercises/practice/binary-search/.docs/introduction.md b/exercises/practice/binary-search/.docs/introduction.md
index 66c4b8a45cb..03496599e75 100644
--- a/exercises/practice/binary-search/.docs/introduction.md
+++ b/exercises/practice/binary-search/.docs/introduction.md
@@ -1,9 +1,13 @@
# Introduction
You have stumbled upon a group of mathematicians who are also singer-songwriters.
-They have written a song for each of their favorite numbers, and, as you can imagine, they have a lot of favorite numbers.
+They have written a song for each of their favorite numbers, and, as you can imagine, they have a lot of favorite numbers (like [0][zero] or [73][seventy-three] or [6174][kaprekars-constant]).
You are curious to hear the song for your favorite number, but with so many songs to wade through, finding the right song could take a while.
Fortunately, they have organized their songs in a playlist sorted by the title — which is simply the number that the song is about.
You realize that you can use a binary search algorithm to quickly find a song given the title.
+
+[zero]: https://en.wikipedia.org/wiki/0
+[seventy-three]: https://en.wikipedia.org/wiki/73_(number)
+[kaprekars-constant]: https://en.wikipedia.org/wiki/6174_(number)
From d783bd143271a8e1db36d655adfd8039754a9268 Mon Sep 17 00:00:00 2001
From: Erik Schierboom
Date: Sat, 18 Mar 2023 11:22:17 +0100
Subject: [PATCH 004/126] Update INSTALLATION.md
---
docs/INSTALLATION.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md
index 9481ce512e3..ad3463291c0 100644
--- a/docs/INSTALLATION.md
+++ b/docs/INSTALLATION.md
@@ -6,7 +6,7 @@ Real Python also offers a [nice guide][helpful guide] to installation on various
Finally, these posts by Brett Cannon [A quick-and-dirty guide][quick-and-dirty] and [Why you should use `python -m pip`][python-m-pip], give very helpful advice on how to manage Python installations and packages.
**Note for MacOS users:** prior to MacOS Monterey (12.3), `Python 2.7` came pre-installed with the operating system.
-Using `Python 2.7` with exercsim or most other programs is not recommended.
+Using `Python 2.7` with Exercism or most other programs is not recommended.
You should instead install Python 3 via one of the methods detailed below.
As of MacOS Monterey (12.3), no version of Python will be pre-installed via MacOS.
From 5b1ef82c4e2ffd6ad16e3d5b79f2fba047c88f2b Mon Sep 17 00:00:00 2001
From: Erik Schierboom
Date: Sat, 18 Mar 2023 11:54:00 +0100
Subject: [PATCH 005/126] basics: fix markdown
---
concepts/basics/about.md | 2 +-
concepts/basics/introduction.md | 2 +-
exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/concepts/basics/about.md b/concepts/basics/about.md
index 929582357c8..30ea083022f 100644
--- a/concepts/basics/about.md
+++ b/concepts/basics/about.md
@@ -41,7 +41,7 @@ On the Python track, [variables][variables] are always written in [`snake_case`]
## Name Assignment (Variables & Constants)
In Python, there are no keywords used in creating variables or constants.
-Instead, programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables__) to any type of object using the assignment `=` operator: ` = `.
+Instead, programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables_) to any type of object using the assignment `=` operator: ` = `.
A name can be reassigned (or re-bound) to different values (different object types) over its lifetime.
For example, `my_first_variable` can be re-assigned many times using `=`, and can refer to different object types with each re-assignment:
diff --git a/concepts/basics/introduction.md b/concepts/basics/introduction.md
index e48e6726515..34e2a8804d7 100644
--- a/concepts/basics/introduction.md
+++ b/concepts/basics/introduction.md
@@ -12,7 +12,7 @@ Python was created by Guido van Rossum and first released in 1991.
## Name Assignment (Variables & Constants)
-Programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables__) to any type of object using the assignment `=` operator: ` = `.
+Programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables_) to any type of object using the assignment `=` operator: ` = `.
A name can be reassigned (or re-bound) to different values (different object types) over its lifetime.
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
index edb9f0fe10e..f9be66c3fe9 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
@@ -24,7 +24,7 @@ On the Python track, [variables][variables] are always written in [`snake_case`]
## Name Assignment (Variables & Constants)
-Programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables__) to any type of object using the assignment `=` operator: ` = `.
+Programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables_) to any type of object using the assignment `=` operator: ` = `.
A name can be reassigned (or re-bound) to different values (different object types) over its lifetime.
From 9f6787133a863290b59d71a2a0032943ca5303cf Mon Sep 17 00:00:00 2001
From: Erik Schierboom
Date: Wed, 22 Mar 2023 20:29:25 +0100
Subject: [PATCH 006/126] string-methods: fix typo
---
concepts/string-methods/about.md | 4 ++--
exercises/concept/little-sisters-essay/.docs/introduction.md | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/concepts/string-methods/about.md b/concepts/string-methods/about.md
index 81eaffb0a7a..e01cbaf8908 100644
--- a/concepts/string-methods/about.md
+++ b/concepts/string-methods/about.md
@@ -44,7 +44,7 @@ There may also be [locale][locale] rules in place for a language or character se
>>> man_in_hat_th = 'ู้ชายในหมวก'
>>> man_in_hat_ru = 'mужчина в шляпе'
>>> man_in_hat_ko = '모자를 쓴 남자'
->>> main_in_hat_en = 'the man in the hat.'
+>>> man_in_hat_en = 'the man in the hat.'
>>> man_in_hat_th.title()
'ผู้ชายในหมวก'
@@ -55,7 +55,7 @@ There may also be [locale][locale] rules in place for a language or character se
>>> man_in_hat_ko.title()
'모자를 쓴 남자'
->> main_in_hat_en.title()
+>> man_in_hat_en.title()
'The Man In The Hat.'
```
diff --git a/exercises/concept/little-sisters-essay/.docs/introduction.md b/exercises/concept/little-sisters-essay/.docs/introduction.md
index 1b0e9531123..24358f2fc28 100644
--- a/exercises/concept/little-sisters-essay/.docs/introduction.md
+++ b/exercises/concept/little-sisters-essay/.docs/introduction.md
@@ -24,7 +24,7 @@ There may also be [locale][locale] rules in place for a language or character se
man_in_hat_th = 'ู้ชายในหมวก'
man_in_hat_ru = 'mужчина в шляпе'
man_in_hat_ko = '모자를 쓴 남자'
-main_in_hat_en = 'the man in the hat.'
+man_in_hat_en = 'the man in the hat.'
>>> man_in_hat_th.title()
'ผู้ชายในหมวก'
@@ -35,7 +35,7 @@ main_in_hat_en = 'the man in the hat.'
>>> man_in_hat_ko.title()
'모자를 쓴 남자'
->> main_in_hat_en.title()
+>> man_in_hat_en.title()
'The Man In The Hat.'
```
From d8e8cb6ca70423c3b9db43b0ff64e16e4050bf2f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 23 Mar 2023 23:56:56 +0000
Subject: [PATCH 007/126] Bump actions/stale from 7 to 8
Bumps [actions/stale](https://github.com/actions/stale) from 7 to 8.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v7...v8)
---
updated-dependencies:
- dependency-name: actions/stale
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/stale.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 4742a0db0a0..41156435d82 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -8,7 +8,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@v7
+ - uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
From 84c35bac97da78c09f9044f85f6a6335589124b6 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 30 Mar 2023 20:50:14 -0700
Subject: [PATCH 008/126] Removed numbers concept exercise from Palindrome
Products practices array.
---
config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config.json b/config.json
index dbafb1ef987..1d118f79c88 100644
--- a/config.json
+++ b/config.json
@@ -1441,7 +1441,7 @@
"slug": "palindrome-products",
"name": "Palindrome Products",
"uuid": "fa795dcc-d390-4e98-880c-6e8e638485e3",
- "practices": ["functions", "function-arguments", "numbers"],
+ "practices": ["functions", "function-arguments"],
"prerequisites": [
"basics",
"bools",
From 9bbd540c5c752226296e586162cd76ffe4b6dde8 Mon Sep 17 00:00:00 2001
From: Hanson Char
Date: Thu, 6 Apr 2023 07:47:02 -0700
Subject: [PATCH 009/126] Minor simplification to is_pangram in the Bit field
approach
---
exercises/practice/pangram/.approaches/bitfield/content.md | 4 ++--
.../practice/pangram/.articles/performance/code/Benchmark.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/pangram/.approaches/bitfield/content.md b/exercises/practice/pangram/.approaches/bitfield/content.md
index 20e460fea0d..b292540567e 100644
--- a/exercises/practice/pangram/.approaches/bitfield/content.md
+++ b/exercises/practice/pangram/.approaches/bitfield/content.md
@@ -9,9 +9,9 @@ ALL_26_BITS_SET = 67108863
def is_pangram(sentence):
letter_flags = 0
for letter in sentence:
- if letter >= 'a' and letter <= 'z':
+ if 'a' <= letter <= 'z':
letter_flags |= 1 << ord(letter) - A_LCASE
- elif letter >= 'A' and letter <= 'Z':
+ elif 'A' <= letter <= 'Z':
letter_flags |= 1 << ord(letter) - A_UCASE
return letter_flags == ALL_26_BITS_SET
diff --git a/exercises/practice/pangram/.articles/performance/code/Benchmark.py b/exercises/practice/pangram/.articles/performance/code/Benchmark.py
index 729cc967dc9..1b423744479 100644
--- a/exercises/practice/pangram/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/pangram/.articles/performance/code/Benchmark.py
@@ -43,9 +43,9 @@ def is_pangram(sentence):
def is_pangram(sentence):
letter_flags = 0
for letter in sentence:
- if letter >= 'a' and letter <= 'z':
+ if 'a' <= letter <= 'z':
letter_flags |= 1 << (ord(letter) - A_LCASE)
- elif letter >= 'A' and letter <= 'Z':
+ elif 'A' <= letter <= 'Z':
letter_flags |= 1 << (ord(letter) - A_UCASE)
return letter_flags == ALL_26_BITS_SET
From c865759d7d45449eabbcfa73b9f08dc7adb4975f Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 7 Apr 2023 04:19:47 -0700
Subject: [PATCH 010/126] Updated refrences to Python 3.10 --> 3.11. (#3384)
---
CONTRIBUTING.md | 13 +++++++------
README.md | 23 ++++++++++++++---------
docs/ABOUT.md | 12 ++++++++----
docs/GENERATOR.md | 17 +++++++++++++----
docs/INSTALLATION.md | 18 ++++++++++++------
docs/LEARNING.md | 5 +++--
docs/TESTS.md | 2 +-
exercises/shared/.docs/help.md | 1 +
8 files changed, 59 insertions(+), 32 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 691ca9f92fc..7f082a56322 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,14 +28,13 @@ Please read this [community blog post](https://exercism.org/blog/freeing-our-mai
**`exercism/Python`** is one of many programming language tracks on [exercism(dot)org][exercism-website].
This repo holds all the instructions, tests, code, & support files for Python _exercises_ currently under development or implemented & available for students.
-🌟 Track exercises support Python `3.7` - `3.10.6`.
+🌟 Track exercises support Python `3.7` - `3.11.2`.
Exceptions to this support are noted where they occur.
- 🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.10.6`.
+🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.11.2`.
-Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree 🌴 .
-Concept exercises are constrained to a small set of language or syntax features.
-Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_.
-These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
+Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree 🌴 .
+Concept exercises are constrained to a small set of language or syntax features.
+Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
@@ -430,3 +429,5 @@ configlet generate --spec-path path/to/problem/specifications
[version-tagged-language-features]: https://docs.python.org/3/library/stdtypes.html#dict.popitem
[website-contributing-section]: https://exercism.org/docs/building
[yapf]: https://github.com/google/yapf
+
+[config-json]: https://github.com/exercism/python/blob/main/config.json
diff --git a/README.md b/README.md
index 64930816362..1c82f33b9d0 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
Exercism Python Track
[![Discourse topics](https://img.shields.io/discourse/topics?color=8A08E6&label=Connect%20&labelColor=FFDF58&logo=Discourse&logoColor=8A08E6&server=https%3A%2F%2Fforum.exercism.org&style=social)](https://forum.exercism.org)
- [![Exercism_II](https://img.shields.io/badge/Exercism--Built-9101FF?logo=python&logoColor=FFDF58&labelColor=3D7AAB&label=Python%203.10%20Powered)](https://exercism.org)
+ [![Exercism_II](https://img.shields.io/badge/Exercism--Built-9101FF?logo=python&logoColor=FFDF58&labelColor=3D7AAB&label=Python%203.11%20Powered)](https://exercism.org)
[![Exercism_III](https://img.shields.io/badge/PAUSED-C73D4E?labelColor=3D454D&label=Contributions)](https://exercism.org/blog/freeing-our-maintainers)
[![Build Status](https://github.com/exercism/python/workflows/Exercises%20check/badge.svg)](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
@@ -17,11 +17,13 @@ Hi. 👋🏽 👋 **We are happy you are here.** 🎉&nb
**`exercism/Python`** is one of many programming language tracks on [exercism(dot)org][exercism-website].
This repo holds all the instructions, tests, code, & support files for Python _exercises_ currently under development or implemented & available for students.
-🌟 Track exercises support Python `3.7` - `3.10.6`.
+🌟 Track exercises support Python `3.7` - `3.11.2`.
Exceptions to this support are noted where they occur.
- 🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.10.6`.
+🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.11.2`.
-Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree 🌴 . Concept exercises are constrained to a small set of language or syntax features. Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
+Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree 🌴 .
+Concept exercises are constrained to a small set of language or syntax features.
+Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
@@ -43,24 +45,25 @@ It might also be helpful to look at [Being a Good Community Member][being-a-good
We 💛 💙 our community.
**`But our maintainers are not accepting community contributions at this time.`**
-Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for details.
+Please read this [community blog post][freeing-maintainers] for details.
-
+
Here to suggest a new feature or new exercise?? **Hooray!** 🎉
-We'd love if you did that via our [Exercism Community Forum](https://forum.exercism.org/). Please keep in mind [Chesterton's Fence][chestertons-fence].
+We'd love if you did that via our [Exercism Community Forum](https://forum.exercism.org/).
+Please read [Suggesting Exercise Improvements][suggesting-improvements] & [Chesterton's Fence][chestertons-fence].
_Thoughtful suggestions will likely result faster & more enthusiastic responses from volunteers._
-
✨ 🦄 _**Want to jump directly into Exercism specifications & detail?**_
[Structure][exercism-track-structure] **|** [Tasks][exercism-tasks] **|** [Concepts][exercism-concepts] **|** [Concept Exercises][concept-exercises] **|** [Practice Exercises][practice-exercises] **|** [Presentation][exercise-presentation]
[Writing Style Guide][exercism-writing-style] **|** [Markdown Specification][exercism-markdown-specification] (_✨ version in [contributing][website-contributing-section] on exercism.org_)
-
+
+
## Python Software and Documentation
@@ -94,10 +97,12 @@ This repository uses the [MIT License](/LICENSE).
[exercism-track-structure]: https://github.com/exercism/docs/tree/main/building/tracks
[exercism-website]: https://exercism.org/
[exercism-writing-style]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
+[freeing-maintainers]: https://exercism.org/blog/freeing-our-maintainers
[practice-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md
[prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md
[psf-license]: https://docs.python.org/3/license.html#psf-license
[python-syllabus]: https://exercism.org/tracks/python/concepts
+[suggesting-improvements]: https://github.com/exercism/docs/blob/main/community/good-member/suggesting-exercise-improvements.md
[the-words-that-we-use]: https://github.com/exercism/docs/blob/main/community/good-member/words.md
[website-contributing-section]: https://exercism.org/docs/building
[zero-clause-bsd]: https://docs.python.org/3/license.html#zero-clause-bsd-license-for-code-in-the-python-release-documentation
diff --git a/docs/ABOUT.md b/docs/ABOUT.md
index 9b53653fd17..05c56799ac6 100644
--- a/docs/ABOUT.md
+++ b/docs/ABOUT.md
@@ -20,13 +20,14 @@ Code can be written and executed from the command line, in an interactive interp
The [zen of Python (PEP 20)][the zen of python] and [What is Pythonic?][what is pythonic] lay out additional philosophies and perspectives on the language.
-This track currently uses `Python 3.9.0`.
+Tests and tooling for this track currently support `3.7` - `3.10.6` (_tests_) and [`Python 3.11`][311-new-features] (_tooling_).
It is highly recommended that students upgrade to at least `Python 3.8`, as some features used by this track may not be supported in earlier versions.
-That being said, most exercises can be completed using Python 3.7, and many can be worked in Python 3.6.
-We will note when a feature is only available in a certain version.
+That being said, most of the exercises will work with `Python 3.6+`, and many are compatible with `Python 2.7+`.
+But we don't guarantee support for versions not listed under [Active Python Releases][active-python-releases].
+We will try to note when a feature is only available in a certain version.
-Complete documentation for the current release of Python (3.9.7) can be found at [docs.python.org][python docs].
+Complete documentation for the current release of Python (3.11.2) can be found at [docs.python.org][python docs].
- [Python Tutorial][python tutorial]
- [Python Library Reference][python library reference]
@@ -36,6 +37,9 @@ Complete documentation for the current release of Python (3.9.7) can be found at
- [Python Glossary of Terms][python glossary of terms]
+
+[311-new-features]: https://docs.python.org/3/whatsnew/3.11.html
+[active-python-releases]: https://www.python.org/downloads/
[duck typing]: https://en.wikipedia.org/wiki/Duck_typing
[dynamic typing in python]: https://stackoverflow.com/questions/11328920/is-python-strongly-typed
[editors for python]: https://djangostars.com/blog/python-ide/
diff --git a/docs/GENERATOR.md b/docs/GENERATOR.md
index d1a8a1da495..271c03d71f1 100644
--- a/docs/GENERATOR.md
+++ b/docs/GENERATOR.md
@@ -1,7 +1,8 @@
# Exercism Python Track Test Generator
-The Python track uses a generator script and [Jinja2] templates for
-creating test files from the canonical data.
+The Python track uses a generator script and [Jinja2][Jinja2] templates for
+creating test files from Exercism's [canonical data][canonical data].
+
## Table of Contents
@@ -15,14 +16,16 @@ creating test files from the canonical data.
* [Learning Jinja](#learning-jinja)
* [Creating a templates](#creating-a-templates)
+
## Script Usage
-Test generation requires a local copy of the [problem-specifications] repository.
+Test generation requires a local copy of the [problem-specifications][problem-specifications] repository.
The script should be run from the root `python` directory, in order to correctly
access the exercises.
Run `bin/generate_tests.py --help` for usage information.
+
## Test Templates
Test templates support [Jinja2] syntax, and have the following context
@@ -42,6 +45,7 @@ Additionally, the following template filters are added for convenience:
- `error_case`: Checks if a test case expects an error to be thrown (ex: `{% for case in cases%}{% if case is error_case}`)
- `regex_replace`: Regex string replacement (ex: `{{ "abc123" | regex_replace("\\d", "D") }}` -> `abcDDD`)
+
### Conventions
- General-use macros for highly repeated template structures are defined in `config/generator_macros.j2`.
@@ -63,6 +67,7 @@ files.
# Additional tests for this track
```
+
### Layout
Most templates will look something like this:
@@ -83,6 +88,7 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{{ macros.footer() }}
```
+
### Overriding Imports
The names imported in `macros.header()` may be overridden by adding
@@ -92,6 +98,7 @@ a list of alternate names to import (ex:`clock`):
{{ macros.header(["Clock"])}}
```
+
### Ignoring Properties
On rare occasion, it may be necessary to filter out properties that
@@ -102,6 +109,7 @@ are not tested in this track. The `header` macro also accepts an
{{ macros.header(ignore=["scores"]) }}
```
+
## Learning Jinja
Starting with the [Jinja Documentation] is highly recommended, but a complete reading is not strictly necessary.
@@ -127,8 +135,9 @@ Additional Resources:
+[Jinja Documentation]: https://jinja.palletsprojects.com/en/3.1.x/
[Jinja2]: https://palletsprojects.com/p/jinja/
-[Jinja Documentation]: https://jinja.palletsprojects.com/en/2.10.x/
[Primer on Jinja Templating]: https://realpython.com/primer-on-jinja-templating/
[Python Jinja tutorial]: http://zetcode.com/python/jinja/
+[canonical data]: https://github.com/exercism/problem-specifications/tree/main/exercises
[problem-specifications]: https://github.com/exercism/problem-specifications
diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md
index ad3463291c0..8bf42a04529 100644
--- a/docs/INSTALLATION.md
+++ b/docs/INSTALLATION.md
@@ -7,7 +7,7 @@ Finally, these posts by Brett Cannon [A quick-and-dirty guide][quick-and-dirty]
**Note for MacOS users:** prior to MacOS Monterey (12.3), `Python 2.7` came pre-installed with the operating system.
Using `Python 2.7` with Exercism or most other programs is not recommended.
-You should instead install Python 3 via one of the methods detailed below.
+You should instead install [Python 3][Python-three downloads] via one of the methods detailed below.
As of MacOS Monterey (12.3), no version of Python will be pre-installed via MacOS.
Some quick links into the documentation by operating system:
@@ -18,12 +18,18 @@ Some quick links into the documentation by operating system:
We recommend reviewing some of the methods outlined in the Real Python article [Installing Python][installing-python] or the articles by Brett Cannon linked above.
-Exercism tests and tooling currently support `Python 3.8` (_tests_) and `Python 3.9` (_tooling_).
-This means that the [newest features of Python `3.10`][310-new-features] are **not** currently supported.
-Please refer to the [Python 3.9.x documentation][3.9 docs] for what is currently supported.
+Exercism tests and tooling currently support `3.7` - `3.10.6` (_tests_) and [`Python 3.11`][311-new-features] (_tooling_).
+Exceptions to this support are noted where they occur.
+Most of the exercises will work with `Python 3.6+`, and many are compatible with `Python 2.7+`.
+But we don't guarantee support for versions not listed under [Active Python Releases][active-python-releases].
-[3.9 docs]: https://docs.python.org/3.9/
-[310-new-features]: https://docs.python.org/3/whatsnew/3.10.html
+
+Please refer to the [Python 3.11.x documentation][3.11 docs] for what is currently supported.
+
+[3.11 docs]: https://docs.python.org/3.11/
+[311-new-features]: https://docs.python.org/3/whatsnew/3.11.html
+[Python-three downloads]: https://www.python.org/downloads/
+[active-python-releases]: https://www.python.org/downloads/
[helpful guide]: https://realpython.com/installing-python/
[installing-python]: https://realpython.com/installing-python/#what-your-options-are_1
[macos]: https://docs.python.org/3/using/mac.html
diff --git a/docs/LEARNING.md b/docs/LEARNING.md
index 15e4fd80215..f9cb1b22051 100644
--- a/docs/LEARNING.md
+++ b/docs/LEARNING.md
@@ -21,12 +21,13 @@ Below you will find some additional jumping-off places to start your learning jo
- [Think Python][Think Python]
- [Python for Non-Programmers][python-for-non-programmers]
- [Python 4 Everyone][python4everyone]
-- [Introduction to Computer Science and Programming in Python (MIT)][mitocw600]
- [Googles Python Class][googles python class]
- [Microsoft's Python Learning Path][MS Python]
-- [PyCharm EDU **IDE** and **Courses**][pycharm edu] (_paid_)
+- [Introduction to Computer Science and Programming in Python (MIT)][mitocw600]
+- [Harvard CS50P][CS50P]
+[CS50P]: https://pll.harvard.edu/course/cs50s-introduction-programming-python?delta=0
[Learn X in Y minutes]: https://learnxinyminutes.com/docs/python3/
[MS Python]: https://docs.microsoft.com/en-us/learn/paths/python-language/
[Python Documentation Tutorial]: https://docs.python.org/3/tutorial/index.html
diff --git a/docs/TESTS.md b/docs/TESTS.md
index 9e522b12d57..00ff924cd73 100644
--- a/docs/TESTS.md
+++ b/docs/TESTS.md
@@ -272,5 +272,5 @@ export PATH=”$PATH:{python_directory}}”
[python command line]: https://docs.python.org/3/using/cmdline.html
[python-m-pip]: https://snarky.ca/why-you-should-use-python-m-pip/
[quick-and-dirty]: https://snarky.ca/a-quick-and-dirty-guide-on-how-to-install-packages-for-python/
-[tutorial from pycqa.org]: https://pylint.pycqa.org/en/latest/tutorial.html
+[tutorial from pycqa.org]: https://pylint.pycqa.org/en/v2.17.2/tutorial.html
[working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
diff --git a/exercises/shared/.docs/help.md b/exercises/shared/.docs/help.md
index 0e105b02d94..ef95bd6193b 100644
--- a/exercises/shared/.docs/help.md
+++ b/exercises/shared/.docs/help.md
@@ -3,6 +3,7 @@
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.
From 6cc4b919ec04e6e2e777e58ff926e77054b4c26c Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 7 Apr 2023 04:20:05 -0700
Subject: [PATCH 011/126] [Track CI] Upgrade Workflow to use Python `3.11.2` &
Pytest `7.2.2` (#3382)
* Upgraded CI workflow to use Python 3.11.2 and Pytest 7.2.2
* Pinned Pylint version to ~=2.17.1
* Added tomli to test_exercises.py imports.
* Adding in tomli for dependancies.
* Need to use latest build of Black due to Python 3.11 and packaging problems with tomli
* Regenerated test files for exercises failing CI.
* Try removing tomli to see if it fixes CI for Py3.11
* And more version fishing for black, which is the culprit here.
* Regeneraed test files with black 22.3.0, since black 23.3.0 is incompatible with Python 3.11 due to tomli conflicts.
* Trying a different strategy. Using only 3.11 for the main container.
* Must import tomllib, part of exteded standard lib not core.
* Wrapped tomllib in a try-catch and aliased importing tomli. Updated requirements to only install tmli on 3.10 and below.
* Forgot to update the imports in the generator.
* Added paasio test changes here, since this changes the CI checker to use Python 3.11.
* Trying a msg= approach.
---
.github/workflows/ci-workflow.yml | 6 +++---
bin/data.py | 11 +++++++++--
bin/generate_tests.py | 8 +++++++-
exercises/practice/paasio/paasio_test.py | 20 ++++++++------------
requirements-generator.txt | 4 ++--
requirements.txt | 5 +++--
6 files changed, 32 insertions(+), 22 deletions(-)
diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml
index fe8ff089a67..01ddab0cac5 100644
--- a/.github/workflows/ci-workflow.yml
+++ b/.github/workflows/ci-workflow.yml
@@ -19,7 +19,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
- python-version: 3.10.6
+ python-version: 3.11.2
- name: Download & Install dependencies
run: |
@@ -53,7 +53,7 @@ jobs:
needs: housekeeping
strategy:
matrix:
- python-version: [3.7, 3.8, 3.9, 3.10.6]
+ python-version: [3.7, 3.8, 3.9, 3.10.6, 3.11.2]
steps:
- uses: actions/checkout@v3
@@ -66,7 +66,7 @@ jobs:
run: pip install dataclasses
- name: Install pytest
- run: pip install pytest~=7.1.2
+ run: pip install pytest~=7.2.2
- name: Check exercises
run: |
diff --git a/bin/data.py b/bin/data.py
index 54dec7612b3..7fce51b9087 100644
--- a/bin/data.py
+++ b/bin/data.py
@@ -4,9 +4,16 @@
from itertools import chain
import json
from pathlib import Path
-import tomli
from typing import List, Any, Dict, Type
+# Tomli was subsumed into Python 3.11.x, but was renamed to to tomllib.
+# This avoids ci failures for Python < 3.11.2.
+try:
+ import tomllib
+except ModuleNotFoundError:
+ import tomli as tomllib
+
+
def _custom_dataclass_init(self, *args, **kwargs):
# print(self.__class__.__name__, "__init__")
@@ -355,7 +362,7 @@ class TestsTOML:
@classmethod
def load(cls, toml_path: Path):
with toml_path.open("rb") as f:
- data = tomli.load(f)
+ data = tomllib.load(f)
return cls({uuid: TestCaseTOML(uuid, *opts) for
uuid, opts in
data.items() if
diff --git a/bin/generate_tests.py b/bin/generate_tests.py
index d0406aad5e2..8dc74936b14 100755
--- a/bin/generate_tests.py
+++ b/bin/generate_tests.py
@@ -34,11 +34,17 @@
from itertools import repeat
from string import punctuation, whitespace
from subprocess import check_call
-import tomli
from tempfile import NamedTemporaryFile
from textwrap import wrap
from typing import Any, Dict, List, NoReturn, Union
+# Tomli was subsumed into Python 3.11.x, but was renamed to to tomllib.
+# This avoids ci failures for Python < 3.11.2.
+try:
+ import tomllib
+except ModuleNotFoundError:
+ import tomli as tomllib
+
from jinja2 import Environment, FileSystemLoader, TemplateNotFound, UndefinedError
from dateutil.parser import parse
diff --git a/exercises/practice/paasio/paasio_test.py b/exercises/practice/paasio/paasio_test.py
index 07a5cea7a55..c5c7ff73176 100644
--- a/exercises/practice/paasio/paasio_test.py
+++ b/exercises/practice/paasio/paasio_test.py
@@ -199,13 +199,13 @@ def test_meteredsocket_stats_read_only(self):
self.assertEqual(282, socket.send_bytes)
self.assertEqual(258, socket.recv_ops)
self.assertEqual(259, socket.recv_bytes)
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'send_ops' of 'MeteredSocket' object has no setter"):
socket.send_ops = 0
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'send_bytes' of 'MeteredSocket' object has no setter"):
socket.send_bytes = 0
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'recv_ops' of 'MeteredSocket' object has no setter"):
socket.recv_ops = 0
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'recv_bytes' of 'MeteredSocket' object has no setter"):
socket.recv_bytes = 0
self.assertEqual(278, socket.send_ops)
self.assertEqual(282, socket.send_bytes)
@@ -426,19 +426,15 @@ def test_meteredfile_stats_read_only(self, super_mock):
file.write(b"bytes")
self.assertEqual(78, file.write_ops)
self.assertEqual(82, file.write_bytes)
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'write_ops' of 'MeteredFile' object has no setter"):
file.write_ops = 0
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'write_bytes' of 'MeteredFile' object has no setter"):
file.write_bytes = 0
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'read_ops' of 'MeteredFile' object has no setter"):
file.read_ops = 0
- with self.assertRaisesRegex(AttributeError, "can't set"):
+ with self.assertRaises(AttributeError, msg="property 'read_bytes' of 'MeteredFile' object has no setter"):
file.read_bytes = 0
self.assertEqual(78, file.write_ops)
self.assertEqual(82, file.write_bytes)
self.assertEqual(58, file.read_ops)
self.assertEqual(59, file.read_bytes)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/requirements-generator.txt b/requirements-generator.txt
index 3c096bebc1e..d472d158b9b 100644
--- a/requirements-generator.txt
+++ b/requirements-generator.txt
@@ -1,6 +1,6 @@
-black==22.3.0
+black<=22.3.0
flake8~=5.0.4
Jinja2~=3.1.2
python-dateutil==2.8.1
markupsafe==2.0.1
-tomli~=2.0.1
+tomli>=1.1.0; python_full_version < '3.11.2'
diff --git a/requirements.txt b/requirements.txt
index d8556f8d612..5b97def747d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
astroid<=2.12.0
flake8~=5.0.4
-pylint~=2.14.5
+pylint~=2.17.1
+black<=22.3.0
yapf~=0.32.0
-tomli~=2.0.1
\ No newline at end of file
+tomli>=1.1.0; python_full_version < '3.11.2'
\ No newline at end of file
From 944f0b8a5e1e3f55e3e70b94325a0108cc15ad9b Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Sun, 9 Apr 2023 14:15:01 +0200
Subject: [PATCH 012/126] Sync etl docs with problem-specifications
The etl exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
- https://github.com/exercism/problem-specifications/pull/2250
---
exercises/practice/etl/.docs/instructions.md | 29 ++++++--------------
exercises/practice/etl/.docs/introduction.md | 16 +++++++++++
2 files changed, 24 insertions(+), 21 deletions(-)
create mode 100644 exercises/practice/etl/.docs/introduction.md
diff --git a/exercises/practice/etl/.docs/instructions.md b/exercises/practice/etl/.docs/instructions.md
index fffe64f2015..802863b5405 100644
--- a/exercises/practice/etl/.docs/instructions.md
+++ b/exercises/practice/etl/.docs/instructions.md
@@ -1,19 +1,8 @@
# Instructions
-We are going to do the `Transform` step of an Extract-Transform-Load.
+Your task is to change the data format of letters and their point values in the game.
-## ETL
-
-Extract-Transform-Load (ETL) is a fancy way of saying, "We have some crufty, legacy data over in this system, and now we need it in this shiny new system over here, so we're going to migrate this."
-
-(Typically, this is followed by, "We're only going to need to run this once."
-That's then typically followed by much forehead slapping and moaning about how stupid we could possibly be.)
-
-## The goal
-
-We're going to extract some Scrabble scores from a legacy system.
-
-The old system stored a list of letters per score:
+Currently, letters are stored in groups based on their score, in a one-to-many mapping.
- 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T",
- 2 points: "D", "G",
@@ -23,18 +12,16 @@ The old system stored a list of letters per score:
- 8 points: "J", "X",
- 10 points: "Q", "Z",
-The shiny new Scrabble system instead stores the score per letter, which makes it much faster and easier to calculate the score for a word.
-It also stores the letters in lower-case regardless of the case of the input letters:
+This needs to be changed to store each individual letter with its score in a one-to-one mapping.
- "a" is worth 1 point.
- "b" is worth 3 points.
- "c" is worth 3 points.
- "d" is worth 2 points.
-- Etc.
-
-Your mission, should you choose to accept it, is to transform the legacy data format to the shiny new format.
+- etc.
-## Notes
+As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case.
-A final note about scoring, Scrabble is played around the world in a variety of languages, each with its own unique scoring table.
-For example, an "E" is scored at 2 in the Māori-language version of the game while being scored at 4 in the Hawaiian-language version.
+~~~~exercism/note
+If you want to look at how the data was previously structured and how it needs to change, take a look at the examples in the test suite.
+~~~~
diff --git a/exercises/practice/etl/.docs/introduction.md b/exercises/practice/etl/.docs/introduction.md
new file mode 100644
index 00000000000..5be65147d7f
--- /dev/null
+++ b/exercises/practice/etl/.docs/introduction.md
@@ -0,0 +1,16 @@
+# Introduction
+
+You work for a company that makes an online multiplayer game called Lexiconia.
+
+To play the game, each player is given 13 letters, which they must rearrange to create words.
+Different letters have different point values, since it's easier to create words with some letters than others.
+
+The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well.
+
+Different languages need to support different point values for letters.
+The point values are determined by how often letters are used, compared to other letters in that language.
+
+For example, the letter 'C' is quite common in English, and is only worth 3 points.
+But in Norwegian it's a very rare letter, and is worth 10 points.
+
+To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game.
From bff0b7e31d63b854a06475b8808cc9bb5eb3da22 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Sun, 9 Apr 2023 16:43:54 +0200
Subject: [PATCH 013/126] Sync linked-list docs with problem-specifications
The linked-list exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
- https://github.com/exercism/problem-specifications/pull/2245
---
.../linked-list/.docs/instructions.md | 34 +++++++++----------
.../linked-list/.docs/introduction.md | 6 ++++
2 files changed, 23 insertions(+), 17 deletions(-)
create mode 100644 exercises/practice/linked-list/.docs/introduction.md
diff --git a/exercises/practice/linked-list/.docs/instructions.md b/exercises/practice/linked-list/.docs/instructions.md
index 3d949d39357..a47942d73db 100644
--- a/exercises/practice/linked-list/.docs/instructions.md
+++ b/exercises/practice/linked-list/.docs/instructions.md
@@ -1,26 +1,26 @@
# Instructions
-Implement a doubly linked list.
+Your team has decided to use a doubly linked list to represent each train route in the schedule.
+Each station along the train's route will be represented by a node in the linked list.
-Like an array, a linked list is a simple linear data structure.
-Several common data types can be implemented using linked lists, like queues, stacks, and associative arrays.
+You don't need to worry about arrival and departure times at the stations.
+Each station will simply be represented by a number.
-A linked list is a collection of data elements called *nodes*.
-In a *singly linked list* each node holds a value and a link to the next node.
-In a *doubly linked list* each node also holds a link to the previous node.
+Routes can be extended, adding stations to the beginning or end of a route.
+They can also be shortened by removing stations from the beginning or the end of a route.
-You will write an implementation of a doubly linked list.
-Implement a Node to hold a value and pointers to the next and previous nodes.
-Then implement a List which holds references to the first and last node and offers an array-like interface for adding and removing items:
+Sometimes a station gets closed down, and in that case the station needs to be removed from the route, even if it is not at the beginning or end of the route.
-- `push` (*insert value at back*);
-- `pop` (*remove value at back*);
-- `shift` (*remove value at front*).
-- `unshift` (*insert value at front*);
+The size of a route is measured not by how far the train travels, but by how many stations it stops at.
-To keep your implementation simple, the tests will not cover error conditions.
-Specifically: `pop` or `shift` will never be called on an empty list.
+```exercism/note
+The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures.
+As the name suggests, it is a list of nodes that are linked together.
+It is a list of "nodes", where each node links to its neighbor or neighbors.
+In a **singly linked list** each node links only to the node that follows it.
+In a **doubly linked list** each node links to both the node that comes before, as well as the node that comes after.
-Read more about [linked lists on Wikipedia][linked-lists].
+If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings.
-[linked-lists]: https://en.wikipedia.org/wiki/Linked_list
+[intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d
+```
diff --git a/exercises/practice/linked-list/.docs/introduction.md b/exercises/practice/linked-list/.docs/introduction.md
new file mode 100644
index 00000000000..6e83ae7b6e5
--- /dev/null
+++ b/exercises/practice/linked-list/.docs/introduction.md
@@ -0,0 +1,6 @@
+# Introduction
+
+You are working on a project to develop a train scheduling system for a busy railway network.
+
+You've been asked to develop a prototype for the train routes in the scheduling system.
+Each route consists of a sequence of train stations that a given train stops at.
From 999321d6237463b974a2d717c78c76e06ed5c881 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Sun, 9 Apr 2023 15:46:13 +0200
Subject: [PATCH 014/126] Sync word-count docs with problem-specifications
The word-count exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
- https://github.com/exercism/problem-specifications/pull/2247
---
.../practice/word-count/.docs/instructions.md | 52 ++++++++++++-------
.../practice/word-count/.docs/introduction.md | 8 +++
2 files changed, 42 insertions(+), 18 deletions(-)
create mode 100644 exercises/practice/word-count/.docs/introduction.md
diff --git a/exercises/practice/word-count/.docs/instructions.md b/exercises/practice/word-count/.docs/instructions.md
index 8b7f03ede7a..064393c8a0f 100644
--- a/exercises/practice/word-count/.docs/instructions.md
+++ b/exercises/practice/word-count/.docs/instructions.md
@@ -1,31 +1,47 @@
# Instructions
-Given a phrase, count the occurrences of each _word_ in that phrase.
+Your task is to count how many times each word occurs in a subtitle of a drama.
-For the purposes of this exercise you can expect that a _word_ will always be one of:
+The subtitles from these dramas use only ASCII characters.
-1. A _number_ composed of one or more ASCII digits (ie "0" or "1234") OR
-2. A _simple word_ composed of one or more ASCII letters (ie "a" or "they") OR
-3. A _contraction_ of two _simple words_ joined by a single apostrophe (ie "it's" or "they're")
+The characters often speak in casual English, using contractions like _they're_ or _it's_.
+Though these contractions come from two words (e.g. _we are_), the contraction (_we're_) is considered a single word.
-When counting words you can assume the following rules:
+Words can be separated by any form of punctuation (e.g. ":", "!", or "?") or whitespace (e.g. "\t", "\n", or " ").
+The only punctuation that does not separate words is the apostrophe in contractions.
-1. The count is _case insensitive_ (ie "You", "you", and "YOU" are 3 uses of the same word)
-2. The count is _unordered_; the tests will ignore how words and counts are ordered
-3. Other than the apostrophe in a _contraction_ all forms of _punctuation_ are regarded as spaces
-4. The words can be separated by _any_ form of whitespace (ie "\t", "\n", " ")
+Numbers are considered words.
+If the subtitles say _It costs 100 dollars._ then _100_ will be its own word.
-For example, for the phrase `"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` the count would be:
+Words are case insensitive.
+For example, the word _you_ occurs three times in the following sentence:
+
+> You come back, you hear me? DO YOU HEAR ME?
+
+The ordering of the word counts in the results doesn't matter.
+
+Here's an example that incorporates several of the elements discussed above:
+
+- simple words
+- contractions
+- numbers
+- case insensitive words
+- punctuation (including apostrophes) to separate words
+- different forms of whitespace to separate words
+
+`"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.`
+
+The mapping for this subtitle would be:
```text
-that's: 1
-the: 2
-password: 2
123: 1
-cried: 1
-special: 1
agent: 1
-so: 1
-i: 1
+cried: 1
fled: 1
+i: 1
+password: 2
+so: 1
+special: 1
+that's: 1
+the: 2
```
diff --git a/exercises/practice/word-count/.docs/introduction.md b/exercises/practice/word-count/.docs/introduction.md
new file mode 100644
index 00000000000..1654508e79d
--- /dev/null
+++ b/exercises/practice/word-count/.docs/introduction.md
@@ -0,0 +1,8 @@
+# Introduction
+
+You teach English as a foreign language to high school students.
+
+You've decided to base your entire curriculum on TV shows.
+You need to analyze which words are used, and how often they're repeated.
+
+This will let you choose the simplest shows to start with, and to gradually increase the difficulty as time passes.
From 7446286e8f3fe49d066478c9be7d3623c89e8ea8 Mon Sep 17 00:00:00 2001
From: HliebS <129861638+HliebS@users.noreply.github.com>
Date: Mon, 10 Apr 2023 20:28:53 +0200
Subject: [PATCH 015/126] Fix English-Ukrainian translation typo (#3394)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The provided translation was from English to Russian. In Ukrainian "nine" is written with an apostrophe - дев'ять.
---
exercises/concept/little-sisters-vocab/.docs/introduction.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/little-sisters-vocab/.docs/introduction.md b/exercises/concept/little-sisters-vocab/.docs/introduction.md
index d7ccff84e64..220b9b73ebc 100644
--- a/exercises/concept/little-sisters-vocab/.docs/introduction.md
+++ b/exercises/concept/little-sisters-vocab/.docs/introduction.md
@@ -36,13 +36,13 @@ Strings can be concatenated using the `+` operator.
```python
language = "Ukrainian"
number = "nine"
-word = "девять"
+word = "дев'ять"
sentence = word + " " + "means" + " " + number + " in " + language + "."
>>> print(sentence)
...
-"девять means nine in Ukrainian."
+"дев'ять means nine in Ukrainian."
```
If a `list`, `tuple`, `set` or other collection of individual strings needs to be combined into a single `str`, [`.join()`][str-join], is a better option:
From cade613a31dd4df3cff5aed75b380d451f71043a Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 10 Apr 2023 12:01:27 -0700
Subject: [PATCH 016/126] [Strings Concept] Update about.md to fix Ukrainian
Example
Per PR 3394, fixed the Ukraninian example in the concept `about.md`, to match the corrected on in the exercise `introduction.md`.
---
concepts/strings/about.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/concepts/strings/about.md b/concepts/strings/about.md
index 895bad45b42..0107f6e70f0 100644
--- a/concepts/strings/about.md
+++ b/concepts/strings/about.md
@@ -159,13 +159,13 @@ This method should be used sparingly, as it is not very performant or easily mai
```python
language = "Ukrainian"
number = "nine"
-word = "девять"
+word = "дев'ять"
sentence = word + " " + "means" + " " + number + " in " + language + "."
>>> print(sentence)
...
-"девять means nine in Ukrainian."
+"дев'ять means nine in Ukrainian."
```
If a `list`, `tuple`, `set` or other collection of individual strings needs to be combined into a single `str`, [`.join()`][str-join], is a better option:
From 869f0723275a2f7e32b89e05d9baf150f40b2275 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Mon, 10 Apr 2023 14:20:06 +0200
Subject: [PATCH 017/126] Sync sum-of-multiples docs with
problem-specifications
The sum-of-multiples exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
- https://github.com/exercism/problem-specifications/pull/2249
---
.../sum-of-multiples/.docs/instructions.md | 29 ++++++++++++-------
.../sum-of-multiples/.docs/introduction.md | 6 ++++
2 files changed, 25 insertions(+), 10 deletions(-)
create mode 100644 exercises/practice/sum-of-multiples/.docs/introduction.md
diff --git a/exercises/practice/sum-of-multiples/.docs/instructions.md b/exercises/practice/sum-of-multiples/.docs/instructions.md
index 7b7ec006e2c..9c824bf1d5f 100644
--- a/exercises/practice/sum-of-multiples/.docs/instructions.md
+++ b/exercises/practice/sum-of-multiples/.docs/instructions.md
@@ -1,18 +1,27 @@
# Instructions
-Given a list of factors and a limit, add up all the unique multiples of the factors that are less than the limit.
-All inputs will be greater than or equal to zero.
+Your task is to write the code that calculates the energy points that get awarded to players when they complete a level.
-## Example
+The points awarded depend on two things:
-Suppose the limit is 20 and the list of factors is [3, 5].
-We need to find the sum of all unique multiples of 3 and 5 that are less than 20.
+- The level (a number) that the player completed.
+- The base value of each magical item collected by the player during that level.
-Multiples of 3 less than 20: 3, 6, 9, 12, 15, 18
-Multiples of 5 less than 20: 5, 10, 15
+The energy points are awarded according to the following rules:
-The unique multiples are: 3, 5, 6, 9, 10, 12, 15, 18
+1. For each magical item, take the base value and find all the multiples of that value that are less than or equal to the level number.
+2. Combine the sets of numbers.
+3. Remove any duplicates.
+4. Calculate the sum of all the numbers that are left.
-The sum of the unique multiples is: 3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78
+Let's look an example:
-So, the answer is 78.
+**The player completed level 20 and found two magical items with base values of 3 and 5.**
+
+To calculate the energy points earned by the player, we need to find all the unique multiples of these base values that are less than or equal to level 20.
+
+- Multiples of 3 up to 20: `{3, 6, 9, 12, 15, 18}`
+- Multiples of 5 up to 20: `{5, 10, 15, 20}`
+- Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18, 20}`
+- Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 + 20 = 98`
+- Therefore, the player earns **98** energy points for completing level 20 and finding the two magical items with base values of 3 and 5.
diff --git a/exercises/practice/sum-of-multiples/.docs/introduction.md b/exercises/practice/sum-of-multiples/.docs/introduction.md
new file mode 100644
index 00000000000..69cabeed5ab
--- /dev/null
+++ b/exercises/practice/sum-of-multiples/.docs/introduction.md
@@ -0,0 +1,6 @@
+# Introduction
+
+You work for a company that makes an online, fantasy-survival game.
+
+When a player finishes a level, they are awarded energy points.
+The amount of energy awarded depends on which magical items the player found while exploring that level.
From 914774cc9bc730450295c843fc9861c16f28800f Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Tue, 11 Apr 2023 20:31:05 +0200
Subject: [PATCH 018/126] sync sum of multiples docs (#3398)
* Sync sum-of-multiples docs with problem-specifications
The sum-of-multiples exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
- https://github.com/exercism/problem-specifications/pull/2249
* Sync sum-of-multiples docs with problem-specifications
There were a few follow-on tweaks to sum-of-multiples.
For context, this is part of the project to overhaul all the practice exercises.
You can read about that here:
https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
----
If you approve this pull request, I will eventually merge it. However, if you are happy with this change **please merge the pull request**, as it will get the changes into the hands of the students much more quickly.
If this pull request contradicts the exercise on your track, **please add a review with _request changes_**. This will block the pull request from getting merged.
Otherwise we aim to take an optimistic merging approach.
If you wish to suggest tweaks to these changes, please open a pull request to the exercism/problem-specifications repository to discuss, so that everyone who has an interest in the shared exercise descriptions can participate.
* Update instructions.md
Resolved merge conflicts, and am now attempting to re-push to repo.
---------
Co-authored-by: BethanyG
---
.../sum-of-multiples/.docs/instructions.md | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/exercises/practice/sum-of-multiples/.docs/instructions.md b/exercises/practice/sum-of-multiples/.docs/instructions.md
index 9c824bf1d5f..e9a25636e51 100644
--- a/exercises/practice/sum-of-multiples/.docs/instructions.md
+++ b/exercises/practice/sum-of-multiples/.docs/instructions.md
@@ -7,21 +7,21 @@ The points awarded depend on two things:
- The level (a number) that the player completed.
- The base value of each magical item collected by the player during that level.
-The energy points are awarded according to the following rules:
+The energy points are awarded according to the following rules:
-1. For each magical item, take the base value and find all the multiples of that value that are less than or equal to the level number.
+1. For each magical item, take the base value and find all the multiples of that value that are less than the level number.
2. Combine the sets of numbers.
3. Remove any duplicates.
-4. Calculate the sum of all the numbers that are left.
+4. Calculate the sum of all the numbers that are left.
-Let's look an example:
+Let's look at an example:
**The player completed level 20 and found two magical items with base values of 3 and 5.**
-To calculate the energy points earned by the player, we need to find all the unique multiples of these base values that are less than or equal to level 20.
+To calculate the energy points earned by the player, we need to find all the unique multiples of these base values that are less than level 20.
-- Multiples of 3 up to 20: `{3, 6, 9, 12, 15, 18}`
-- Multiples of 5 up to 20: `{5, 10, 15, 20}`
-- Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18, 20}`
-- Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 + 20 = 98`
-- Therefore, the player earns **98** energy points for completing level 20 and finding the two magical items with base values of 3 and 5.
+- Multiples of 3 less than 20: `{3, 6, 9, 12, 15, 18}`
+- Multiples of 5 less than 20: `{5, 10, 15}`
+- Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18}`
+- Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78`
+- Therefore, the player earns **78** energy points for completing level 20 and finding the two magical items with base values of 3 and 5
From 8ae39796b7ebefb1cbca3d26461df3367466fbdf Mon Sep 17 00:00:00 2001
From: Al Momin Faruk <31950303+mominfaruk@users.noreply.github.com>
Date: Wed, 12 Apr 2023 22:44:51 +0600
Subject: [PATCH 019/126] Replace stale link by working link with valuable
informtion (#3399)
* Replace stale link by working link with valuable informtion
* Update concepts/tuples/links.json
---------
Co-authored-by: BethanyG
---
concepts/tuples/links.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/concepts/tuples/links.json b/concepts/tuples/links.json
index a6a22b9b8fa..a0d85de8a5a 100644
--- a/concepts/tuples/links.json
+++ b/concepts/tuples/links.json
@@ -16,8 +16,8 @@
"description": "Lists vs Tuples"
},
{
- "url": "http://e-scribe.com/post/understanding-tuples-vs-lists-in-python/",
- "description": "Understanding tuples vs lists in Python"
+ "url": "https://stackoverflow.com/a/626871",
+ "description": "What's the difference between lists and tuples?"
},
{
"url": "https://jtauber.com/blog/2006/04/15/python_tuples_are_not_just_constant_lists/",
From aecf530858499c4fbb8dcaaab24baf26e11b0cc5 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 10 Apr 2023 18:48:26 -0700
Subject: [PATCH 020/126] Synced tests.toml for depricated exercises.
---
.../practice/accumulate/.meta/tests.toml | 35 ++++++++++
.../.meta/tests.toml | 62 +++++++++++++++++
exercises/practice/strain/.meta/tests.toml | 66 +++++++++++++++++++
3 files changed, 163 insertions(+)
create mode 100644 exercises/practice/accumulate/.meta/tests.toml
create mode 100644 exercises/practice/parallel-letter-frequency/.meta/tests.toml
create mode 100644 exercises/practice/strain/.meta/tests.toml
diff --git a/exercises/practice/accumulate/.meta/tests.toml b/exercises/practice/accumulate/.meta/tests.toml
new file mode 100644
index 00000000000..150ec2e896e
--- /dev/null
+++ b/exercises/practice/accumulate/.meta/tests.toml
@@ -0,0 +1,35 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[64d97c14-36dd-44a8-9621-2cecebd6ed23]
+description = "accumulate empty"
+include = false
+
+[00008ed2-4651-4929-8c08-8b4dbd70872e]
+description = "accumulate squares"
+include = false
+
+[551016da-4396-4cae-b0ec-4c3a1a264125]
+description = "accumulate upcases"
+include = false
+
+[cdf95597-b6ec-4eac-a838-3480d13d0d05]
+description = "accumulate reversed strings"
+include = false
+
+[bee8e9b6-b16f-4cd2-be3b-ccf7457e50bb]
+description = "accumulate recursively"
+include = false
+
+[0b357334-4cad-49e1-a741-425202edfc7c]
+description = "accumulate recursively"
+include = false
+reimplements = "bee8e9b6-b16f-4cd2-be3b-ccf7457e50bb"
diff --git a/exercises/practice/parallel-letter-frequency/.meta/tests.toml b/exercises/practice/parallel-letter-frequency/.meta/tests.toml
new file mode 100644
index 00000000000..6cf36e6fd2d
--- /dev/null
+++ b/exercises/practice/parallel-letter-frequency/.meta/tests.toml
@@ -0,0 +1,62 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[c054d642-c1fa-4234-8007-9339f2337886]
+description = "no texts"
+include = false
+
+[818031be-49dc-4675-b2f9-c4047f638a2a]
+description = "one text with one letter"
+include = false
+
+[c0b81d1b-940d-4cea-9f49-8445c69c17ae]
+description = "one text with multiple letters"
+include = false
+
+[708ff1e0-f14a-43fd-adb5-e76750dcf108]
+description = "two texts with one letter"
+include = false
+
+[1b5c28bb-4619-4c9d-8db9-a4bb9c3bdca0]
+description = "two texts with multiple letters"
+include = false
+
+[6366e2b8-b84c-4334-a047-03a00a656d63]
+description = "ignore letter casing"
+include = false
+
+[92ebcbb0-9181-4421-a784-f6f5aa79f75b]
+description = "ignore whitespace"
+include = false
+
+[bc5f4203-00ce-4acc-a5fa-f7b865376fd9]
+description = "ignore punctuation"
+include = false
+
+[68032b8b-346b-4389-a380-e397618f6831]
+description = "ignore numbers"
+include = false
+
+[aa9f97ac-3961-4af1-88e7-6efed1bfddfd]
+description = "Unicode letters"
+include = false
+
+[7b1da046-701b-41fc-813e-dcfb5ee51813]
+description = "combination of lower- and uppercase letters, punctuation and white space"
+include = false
+
+[4727f020-df62-4dcf-99b2-a6e58319cb4f]
+description = "large texts"
+include = false
+
+[adf8e57b-8e54-4483-b6b8-8b32c115884c]
+description = "many small texts"
+include = false
diff --git a/exercises/practice/strain/.meta/tests.toml b/exercises/practice/strain/.meta/tests.toml
new file mode 100644
index 00000000000..66ed1044bd6
--- /dev/null
+++ b/exercises/practice/strain/.meta/tests.toml
@@ -0,0 +1,66 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[26af8c32-ba6a-4eb3-aa0a-ebd8f136e003]
+description = "keep on empty list returns empty list"
+include = false
+
+[f535cb4d-e99b-472a-bd52-9fa0ffccf454]
+description = "keeps everything"
+include = false
+
+[950b8e8e-f628-42a8-85e2-9b30f09cde38]
+description = "keeps nothing"
+include = false
+
+[92694259-6e76-470c-af87-156bdf75018a]
+description = "keeps first and last"
+include = false
+
+[938f7867-bfc7-449e-a21b-7b00cbb56994]
+description = "keeps neither first nor last"
+include = false
+
+[8908e351-4437-4d2b-a0f7-770811e48816]
+description = "keeps strings"
+include = false
+
+[2728036b-102a-4f1e-a3ef-eac6160d876a]
+description = "keeps lists"
+include = false
+
+[ef16beb9-8d84-451a-996a-14e80607fce6]
+description = "discard on empty list returns empty list"
+include = false
+
+[2f42f9bc-8e06-4afe-a222-051b5d8cd12a]
+description = "discards everything"
+include = false
+
+[ca990fdd-08c2-4f95-aa50-e0f5e1d6802b]
+description = "discards nothing"
+include = false
+
+[71595dae-d283-48ca-a52b-45fa96819d2f]
+description = "discards first and last"
+include = false
+
+[ae141f79-f86d-4567-b407-919eaca0f3dd]
+description = "discards neither first nor last"
+include = false
+
+[daf25b36-a59f-4f29-bcfe-302eb4e43609]
+description = "discards strings"
+include = false
+
+[a38d03f9-95ad-4459-80d1-48e937e4acaf]
+description = "discards lists"
+include = false
From 218d2ba1cccab27cb64ccc8268e4ea88e938ae3f Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Wed, 12 Apr 2023 12:26:02 +0200
Subject: [PATCH 021/126] Sync rna-transcription docs with
problem-specifications
The rna-transcription exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
- https://github.com/exercism/problem-specifications/pull/2251
---
.../rna-transcription/.docs/instructions.md | 6 +++++-
.../rna-transcription/.docs/introduction.md | 16 ++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 exercises/practice/rna-transcription/.docs/introduction.md
diff --git a/exercises/practice/rna-transcription/.docs/instructions.md b/exercises/practice/rna-transcription/.docs/instructions.md
index 851bdb49db7..36da381f5a7 100644
--- a/exercises/practice/rna-transcription/.docs/instructions.md
+++ b/exercises/practice/rna-transcription/.docs/instructions.md
@@ -1,6 +1,6 @@
# Instructions
-Given a DNA strand, return its RNA complement (per RNA transcription).
+Your task is determine the RNA complement of a given DNA sequence.
Both DNA and RNA strands are a sequence of nucleotides.
@@ -14,3 +14,7 @@ Given a DNA strand, its transcribed RNA strand is formed by replacing each nucle
- `C` -> `G`
- `T` -> `A`
- `A` -> `U`
+
+~~~~exercism/note
+If you want to look at how the inputs and outputs are structured, take a look at the examples in the test suite.
+~~~~
diff --git a/exercises/practice/rna-transcription/.docs/introduction.md b/exercises/practice/rna-transcription/.docs/introduction.md
new file mode 100644
index 00000000000..6b3f44b532d
--- /dev/null
+++ b/exercises/practice/rna-transcription/.docs/introduction.md
@@ -0,0 +1,16 @@
+# Introduction
+
+You work for a bioengineering company that specializes in developing therapeutic solutions.
+
+Your team has just been given a new project to develop a targeted therapy for a rare type of cancer.
+
+~~~~exercism/note
+It's all very complicated, but the basic idea is that sometimes people's bodies produce too much of a given protein.
+That can cause all sorts of havoc.
+
+But if you can create a very specific molecule (called a micro-RNA), it can prevent the protein from being produced.
+
+This technique is called [RNA Interference][rnai].
+
+[rnai]: https://admin.acceleratingscience.com/ask-a-scientist/what-is-rnai/
+~~~~
From 196cfa37ea729ca5306d494767def568e5bc09e6 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Wed, 12 Apr 2023 11:29:02 +0200
Subject: [PATCH 022/126] Sync largest-series-product docs with
problem-specifications
The largest-series-product exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
- https://github.com/exercism/problem-specifications/pull/2246
---
.../.docs/instructions.md | 28 +++++++++++++------
.../.docs/introduction.md | 5 ++++
2 files changed, 25 insertions(+), 8 deletions(-)
create mode 100644 exercises/practice/largest-series-product/.docs/introduction.md
diff --git a/exercises/practice/largest-series-product/.docs/instructions.md b/exercises/practice/largest-series-product/.docs/instructions.md
index 08586dd5933..f297b57f7c4 100644
--- a/exercises/practice/largest-series-product/.docs/instructions.md
+++ b/exercises/practice/largest-series-product/.docs/instructions.md
@@ -1,14 +1,26 @@
# Instructions
-Given a string of digits, calculate the largest product for a contiguous substring of digits of length n.
+Your task is to look for patterns in the long sequence of digits in the encrypted signal.
-For example, for the input `'1027839564'`, the largest product for a series of 3 digits is 270 `(9 * 5 * 6)`, and the largest product for a series of 5 digits is 7560 `(7 * 8 * 3 * 9 * 5)`.
+The technique you're going to use here is called the largest series product.
-Note that these series are only required to occupy *adjacent positions* in the input; the digits need not be *numerically consecutive*.
+Let's define a few terms, first.
-For the input `'73167176531330624919225119674426574742355349194934'`,
-the largest product for a series of 6 digits is 23520.
+- **input**: the sequence of digits that you need to analyze
+- **series**: a sequence of adjacent digits (those that are next to each other) that is contained within the input
+- **span**: how many digits long each series is
+- **product**: what you get when you multiply numbers together
-For a series of zero digits, the largest product is 1 because 1 is the multiplicative identity.
-(You don't need to know what a multiplicative identity is to solve this problem;
-it just means that multiplying a number by 1 gives you the same number.)
+Let's work through an example, with the input `"63915"`.
+
+- To form a series, take adjacent digits in the original input.
+- If you are working with a span of `3`, there will be three possible series:
+ - `"639"`
+ - `"391"`
+ - `"915"`
+- Then we need to calculate the product of each series:
+ - The product of the series `"639"` is 162 (`6 × 3 × 9 = 162`)
+ - The product of the series `"391"` is 27 (`3 × 9 × 1 = 27`)
+ - The product of the series `"915"` is 45 (`9 × 1 × 5 = 45`)
+- 162 is bigger than both 27 and 45, so the largest series product of `"63915"` is from the series `"639"`.
+ So the answer is **162**.
diff --git a/exercises/practice/largest-series-product/.docs/introduction.md b/exercises/practice/largest-series-product/.docs/introduction.md
new file mode 100644
index 00000000000..597bb5fa15d
--- /dev/null
+++ b/exercises/practice/largest-series-product/.docs/introduction.md
@@ -0,0 +1,5 @@
+# Introduction
+
+You work for a government agency that has intercepted a series of encrypted communication signals from a group of bank robbers.
+The signals contain a long sequence of digits.
+Your team needs to use various digital signal processing techniques to analyze the signals and identify any patterns that may indicate the planning of a heist.
From 478b3b3fbecbfa4a6fefa34bba2e03e191e9bfb7 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Wed, 12 Apr 2023 11:29:18 +0200
Subject: [PATCH 023/126] Delete test cases from largest-series-product
This deletes two deprecated test cases so that we can
dramatically simplify the instructions for this exercise.
---
exercises/practice/largest-series-product/.meta/tests.toml | 2 ++
.../largest-series-product/largest_series_product_test.py | 6 ------
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/largest-series-product/.meta/tests.toml b/exercises/practice/largest-series-product/.meta/tests.toml
index 6c111adf0f1..88316925977 100644
--- a/exercises/practice/largest-series-product/.meta/tests.toml
+++ b/exercises/practice/largest-series-product/.meta/tests.toml
@@ -41,9 +41,11 @@ description = "rejects span longer than string length"
[06bc8b90-0c51-4c54-ac22-3ec3893a079e]
description = "reports 1 for empty string and empty product (0 span)"
+include = false
[3ec0d92e-f2e2-4090-a380-70afee02f4c0]
description = "reports 1 for nonempty string and empty product (0 span)"
+include = false
[6d96c691-4374-4404-80ee-2ea8f3613dd4]
description = "rejects empty string and nonzero span"
diff --git a/exercises/practice/largest-series-product/largest_series_product_test.py b/exercises/practice/largest-series-product/largest_series_product_test.py
index 0a67d4ca14e..ba8ae10dc8e 100644
--- a/exercises/practice/largest-series-product/largest_series_product_test.py
+++ b/exercises/practice/largest-series-product/largest_series_product_test.py
@@ -46,12 +46,6 @@ def test_rejects_span_longer_than_string_length(self):
err.exception.args[0], "span must be smaller than string length"
)
- def test_reports_1_for_empty_string_and_empty_product_0_span(self):
- self.assertEqual(largest_product("", 0), 1)
-
- def test_reports_1_for_nonempty_string_and_empty_product_0_span(self):
- self.assertEqual(largest_product("123", 0), 1)
-
def test_rejects_empty_string_and_nonzero_span(self):
with self.assertRaises(ValueError) as err:
largest_product("", 1)
From 17d4f7b5ea7b5e984fd9b419913d44c28c02e82a Mon Sep 17 00:00:00 2001
From: Matthijs <19817960+MatthijsBlom@users.noreply.github.com>
Date: Thu, 6 Apr 2023 20:18:26 +0200
Subject: [PATCH 024/126] Synchronize example code with tests
---
.../largest-series-product/.docs/instructions.append.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/largest-series-product/.docs/instructions.append.md b/exercises/practice/largest-series-product/.docs/instructions.append.md
index 9250db2e8ca..5a0f9b92064 100644
--- a/exercises/practice/largest-series-product/.docs/instructions.append.md
+++ b/exercises/practice/largest-series-product/.docs/instructions.append.md
@@ -12,8 +12,8 @@ To raise a `ValueError` with a message, write the message as an argument to the
# span of numbers is longer than number series
raise ValueError("span must be smaller than string length")
-# span of number is zero or negative
-raise ValueError("span must be greater than zero")
+# span of number is negative
+raise ValueError("span must not be negative")
# series includes non-number input
raise ValueError("digits input must only contain digits")
From f333803a543ceb89a01239a0bbad1bae865dc73b Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Thu, 13 Apr 2023 12:50:41 +0200
Subject: [PATCH 025/126] Sync saddle-points docs with problem-specifications
The saddle-points exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
---
.../saddle-points/.docs/instructions.md | 31 +++++++++----------
.../saddle-points/.docs/introduction.md | 9 ++++++
2 files changed, 24 insertions(+), 16 deletions(-)
create mode 100644 exercises/practice/saddle-points/.docs/introduction.md
diff --git a/exercises/practice/saddle-points/.docs/instructions.md b/exercises/practice/saddle-points/.docs/instructions.md
index 920ecffed98..d861388e437 100644
--- a/exercises/practice/saddle-points/.docs/instructions.md
+++ b/exercises/practice/saddle-points/.docs/instructions.md
@@ -1,25 +1,24 @@
# Instructions
-Detect saddle points in a matrix.
+Your task is to find the potential trees where you could build your tree house.
-So say you have a matrix like so:
+The data company provides the data as grids that show the heights of the trees.
+The rows of the grid represent the east-west direction, and the columns represent the north-south direction.
-```text
- 1 2 3
- |---------
-1 | 9 8 7
-2 | 5 3 2 <--- saddle point at row 2, column 1, with value 5
-3 | 6 6 7
-```
+An acceptable tree will be the the largest in its row, while being the smallest in its column.
-It has a saddle point at row 2, column 1.
+A grid might not have any good trees at all.
+Or it might have one, or even several.
-It's called a "saddle point" because it is greater than or equal to every element in its row and less than or equal to every element in its column.
+Here is a grid that has exactly one candidate tree.
-A matrix may have zero or more saddle points.
+ 1 2 3 4
+ |-----------
+1 | 9 8 7 8
+2 | 5 3 2 4 <--- potential tree house at row 2, column 1, for tree with height 5
+3 | 6 6 7 1
-Your code should be able to provide the (possibly empty) list of all the saddle points for any given matrix.
+- Row 2 has values 5, 3, and 1. The largest value is 5.
+- Column 1 has values 9, 5, and 6. The smallest value is 5.
-The matrix can have a different number of rows and columns (Non square).
-
-Note that you may find other definitions of matrix saddle points online, but the tests for this exercise follow the above unambiguous definition.
+So the point at `[2, 1]` (row: 2, column: 1) is a great spot for a tree house.
diff --git a/exercises/practice/saddle-points/.docs/introduction.md b/exercises/practice/saddle-points/.docs/introduction.md
new file mode 100644
index 00000000000..b582efbd21d
--- /dev/null
+++ b/exercises/practice/saddle-points/.docs/introduction.md
@@ -0,0 +1,9 @@
+# Introduction
+
+You are planning on building a tree house in the woods near your house so that you can watch the sun rise and set.
+
+You've obtained data from a local survey company that shows the heights of all the trees in each rectangular section of the map.
+You need to analyze each grid on the map to find the perfect tree for your tree house.
+
+The best tree will be the tallest tree compared to all the other trees to the east and west, so that you have the best possible view of the sunrises and sunsets.
+You don't like climbing too much, so the perfect tree will also be the shortest among all the trees to the north and to the south.
From c652ff3089fc253f62ed2b76a68f55a0937766a6 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 19 Dec 2022 09:27:16 +0100
Subject: [PATCH 026/126] Updated bank account
---
bin/githelp.py | 2 +-
.../practice/bank-account/.meta/template.j2 | 47 +++
.../practice/bank-account/.meta/tests.toml | 43 +++
.../bank-account/bank_account_test.py | 89 ++----
.../practice/bank-account/canonical_data.json | 285 ++++++++++++++++++
5 files changed, 397 insertions(+), 69 deletions(-)
create mode 100644 exercises/practice/bank-account/.meta/template.j2
create mode 100644 exercises/practice/bank-account/.meta/tests.toml
create mode 100644 exercises/practice/bank-account/canonical_data.json
diff --git a/bin/githelp.py b/bin/githelp.py
index 3d3be06f664..9710e0c1361 100644
--- a/bin/githelp.py
+++ b/bin/githelp.py
@@ -6,7 +6,7 @@
from typing import Iterator, Union
-GITHUB_EXERCISM = f"https://github.com/exercism"
+GITHUB_EXERCISM = f"https://github.com/meatball133"
class Repo(Enum):
diff --git a/exercises/practice/bank-account/.meta/template.j2 b/exercises/practice/bank-account/.meta/template.j2
new file mode 100644
index 00000000000..99c21bc8f6a
--- /dev/null
+++ b/exercises/practice/bank-account/.meta/template.j2
@@ -0,0 +1,47 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{% macro test_case(case) -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ account = BankAccount()
+ {%- set inputs = case["input"]["operations"] -%}
+ {%- if case["expected"] -%}
+ {%- set error_case = true -%}
+ {%- set error_msg = case["expected"]["error"] -%}
+ {%- set error_operation = inputs[-1]["operation"] -%}
+ {%- set final_value = inputs[-1]["value"] -%}
+ {% endif %}
+ {%- if case["expected"] and inputs|length > 1 -%}
+ {%- set inputs = inputs[:-1] -%}
+ {%- endif -%}
+ {%- for input in inputs -%}
+ {%- set operation = input["operation"] -%}
+ {%- set value = input["value"] -%}
+ {%- set expected = input["expected"] %}
+ {%- if operation and value %}
+ account.{{ operation }}({{ value }})
+ {%- elif operation == "amount" %}
+ self.assertEqual(account.get_balance(), {{ expected }})
+ {%- elif operation and not value %}
+ account.{{ operation }}()
+ {%- endif %}
+ {%- endfor %}
+ {%- if error_case %}
+ with self.assertRaises(ValueError) as err:
+ account.{{ error_operation }}({{ final_value if final_value else "" }})
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
+ {%- endif %}
+{% endmacro %}
+
+{{ macros.header(["BankAccount"]) }}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
+ {% if additional_cases | length -%}
+
+ # Additional tests for this track
+ {% for case in additional_cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
+ {%- endif %}
diff --git a/exercises/practice/bank-account/.meta/tests.toml b/exercises/practice/bank-account/.meta/tests.toml
new file mode 100644
index 00000000000..6778e39f458
--- /dev/null
+++ b/exercises/practice/bank-account/.meta/tests.toml
@@ -0,0 +1,43 @@
+# This is an auto-generated file. Regular comments will be removed when this
+# file is regenerated. Regenerating will not touch any manually added keys,
+# so comments can be added in a "comment" key.
+
+[983a1528-4ceb-45e5-8257-8ce01aceb5ed]
+description = "encode yes"
+
+[e88d4ec3-c6bf-4752-8e59-5046c44e3ba7]
+description = "encode no"
+
+[08f1af07-27ae-4b38-aa19-770bde558064]
+description = "encode OMG"
+
+[6f6d242f-8c31-4ac6-8995-a90d42cad59f]
+description = "encode spaces"
+
+[f9facfaa-d824-486e-8381-48832c4bbffd]
+description = "encode mindblowingly"
+
+[7a65ba52-e35c-4fd2-8159-bda2bde6e59c]
+description = "encode numbers"
+
+[570dfaa5-0532-4c1f-a7d3-0f65c3265608]
+description = "encode deep thought"
+
+[c396d233-1c49-4272-98dc-7f502dbb9470]
+description = "encode all the letters"
+
+[c06f534f-bdc2-4a02-a388-1063400684de]
+description = "decode exercism"
+
+[0722d404-6116-4f92-ba3b-da7f88f1669c]
+description = "decode a sentence"
+
+[ec42245f-9361-4341-8231-a22e8d19c52f]
+description = "decode numbers"
+
+[4f381ef8-10ef-4507-8e1d-0631ecc8ee72]
+description = "decode all the letters"
+
+[d45df9ea-1db0-47f3-b18c-d365db49d938]
+description = "decode with too many spaces"
+
diff --git a/exercises/practice/bank-account/bank_account_test.py b/exercises/practice/bank-account/bank_account_test.py
index 3a36b3ccb42..5a50505010a 100644
--- a/exercises/practice/bank-account/bank_account_test.py
+++ b/exercises/practice/bank-account/bank_account_test.py
@@ -1,87 +1,75 @@
-import sys
-import threading
-import time
import unittest
-from bank_account import BankAccount
+from bank_account import (
+ BankAccount,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
class BankAccountTest(unittest.TestCase):
- def test_newly_opened_account_has_zero_balance(self):
+ def test_using_pop_raises_an_error_if_the_list_is_empty(self):
account = BankAccount()
account.open()
self.assertEqual(account.get_balance(), 0)
- def test_can_deposit_money(self):
+ def test_can_return_with_pop_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.deposit(100)
self.assertEqual(account.get_balance(), 100)
- def test_can_deposit_money_sequentially(self):
- account = BankAccount()
- account.open()
- account.deposit(100)
- account.deposit(50)
-
- self.assertEqual(account.get_balance(), 150)
-
- def test_can_withdraw_money(self):
+ def test_using_shift_raises_an_error_if_the_list_is_empty(self):
account = BankAccount()
account.open()
account.deposit(100)
account.withdraw(50)
-
self.assertEqual(account.get_balance(), 50)
- def test_can_withdraw_money_sequentially(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.deposit(100)
- account.withdraw(20)
account.withdraw(80)
-
+ account.withdraw(20)
self.assertEqual(account.get_balance(), 0)
- def test_checking_balance_of_closed_account_throws_error(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.close()
-
with self.assertRaises(ValueError) as err:
- account.get_balance()
+ account.amount()
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
- def test_deposit_into_closed_account(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.close()
-
with self.assertRaises(ValueError) as err:
account.deposit(50)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
-
- def test_withdraw_from_closed_account(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.close()
-
with self.assertRaises(ValueError) as err:
account.withdraw(50)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
- def test_close_already_closed_account(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
+ account.close()
with self.assertRaises(ValueError) as err:
account.close()
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
- def test_open_already_opened_account(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
with self.assertRaises(ValueError) as err:
@@ -89,7 +77,7 @@ def test_open_already_opened_account(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account already open")
- def test_reopened_account_does_not_retain_balance(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.deposit(50)
@@ -97,63 +85,28 @@ def test_reopened_account_does_not_retain_balance(self):
account.open()
self.assertEqual(account.get_balance(), 0)
- def test_cannot_withdraw_more_than_deposited(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.deposit(25)
-
with self.assertRaises(ValueError) as err:
account.withdraw(50)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "amount must be less than balance")
- def test_cannot_withdraw_negative(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
account.deposit(100)
-
with self.assertRaises(ValueError) as err:
account.withdraw(-50)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "amount must be greater than 0")
- def test_cannot_deposit_negative(self):
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account = BankAccount()
account.open()
-
with self.assertRaises(ValueError) as err:
account.deposit(-50)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "amount must be greater than 0")
-
- def test_can_handle_concurrent_transactions(self):
- account = BankAccount()
- account.open()
- account.deposit(1000)
-
- self.adjust_balance_concurrently(account)
-
- self.assertEqual(account.get_balance(), 1000)
-
- def adjust_balance_concurrently(self, account):
- def transact():
- account.deposit(5)
- time.sleep(0.001)
- account.withdraw(5)
-
- # Greatly improve the chance of an operation being interrupted
- # by thread switch, thus testing synchronization effectively
- try:
- sys.setswitchinterval(1e-12)
- except AttributeError:
- # For Python 2 compatibility
- sys.setcheckinterval(1)
-
- threads = [threading.Thread(target=transact) for _ in range(1000)]
- for thread in threads:
- thread.start()
- for thread in threads:
- thread.join()
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/exercises/practice/bank-account/canonical_data.json b/exercises/practice/bank-account/canonical_data.json
new file mode 100644
index 00000000000..21e868eac9d
--- /dev/null
+++ b/exercises/practice/bank-account/canonical_data.json
@@ -0,0 +1,285 @@
+{
+ "exercise": "bank-account",
+ "comments": [
+ "In order to keep the interface for the exercise close or equal to the ",
+ "description.md and also satisfying the canonical-data schema, the only ",
+ "properties are pop, push, shift, unshift. Some tracks also implement ",
+ "delete and count, via Track-Inserted hints. ",
+ " ",
+ "It is hard to have interesting tests using the property based approach so",
+ "this canonical data uses the following approach: ",
+ "- Each test input is an array of { operation, value?, expected? } ",
+ "- If operation is push, unshift, delete, then value is given ",
+ "- If operation is pop, shift, count, the expected value can be given ",
+ " ",
+ "Encoding tests and operations using the same field is necessary to have ",
+ "tests that don't just initialize the list, and then have one operation. "
+ ],
+ "cases": [
+ {
+ "uuid": "983a1528-4ceb-45e5-8257-8ce01aceb5ed",
+ "description": "Using pop raises an error if the list is empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "amount",
+ "value": 0
+ }
+ ]
+ }, "expected": {}
+ },
+ {
+ "uuid": "e88d4ec3-c6bf-4752-8e59-5046c44e3ba7",
+ "description" : "Can return with pop and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "deposit",
+ "value": 100
+ },
+ {
+ "operation": "amount",
+ "expected": 100
+ }
+ ]
+ },
+ "expected": {}
+ },
+ {
+ "uuid": "08f1af07-27ae-4b38-aa19-770bde558064",
+ "description": "Using shift raises an error if the list is empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "deposit",
+ "value": 100
+ },
+ {
+ "operation": "withdraw",
+ "value": 50
+ },
+ {
+ "operation": "amount",
+ "value": 50
+ }
+ ]
+ },
+ "expected": {}
+ },
+ {
+ "uuid": "6f6d242f-8c31-4ac6-8995-a90d42cad59f",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "deposit",
+ "value": 100
+ },
+ {
+ "operation": "withdraw",
+ "value": 80
+ },
+ {
+ "operation": "withdraw",
+ "value": 20
+ },
+ {
+ "operation": "amount",
+ "value": 0
+ }
+ ]
+ },
+ "expected": {}
+ },
+ {
+ "uuid": "f9facfaa-d824-486e-8381-48832c4bbffd",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "close"
+ },
+ {
+ "operation": "amount"
+ }
+ ]
+ },
+ "expected": {"error": "account not open"}
+ },
+ {
+ "uuid": "7a65ba52-e35c-4fd2-8159-bda2bde6e59c",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "close"
+ },
+ {
+ "operation": "deposit",
+ "value": 50
+ }
+ ]
+ },
+ "expected": {"error": "account not open"}
+ },
+ {
+ "uuid": "570dfaa5-0532-4c1f-a7d3-0f65c3265608",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "close"
+ },
+ {
+ "operation": "withdraw",
+ "value": 50
+ }
+ ]
+ },
+ "expected": {"error": "account not open"}
+ },
+ {
+ "uuid": "c396d233-1c49-4272-98dc-7f502dbb9470",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "close"
+ }
+ ]
+ },
+ "expected": {"error": "account not open"}
+ },
+ {
+ "uuid": "c06f534f-bdc2-4a02-a388-1063400684de",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "open"
+ }
+ ]
+ },
+ "expected": {"error": "account already open"}
+ },
+ {
+ "uuid": "0722d404-6116-4f92-ba3b-da7f88f1669c",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "deposit",
+ "value": 50
+ },
+ {
+ "operation": "close"
+ },
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "amount",
+ "value": 0
+ }
+ ]
+ },
+ "expected": {}
+ },
+ {
+ "uuid": "ec42245f-9361-4341-8231-a22e8d19c52f",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "deposit",
+ "value": 25
+ },
+ {
+ "operation": "withdraw",
+ "value": 50
+ }
+ ]
+ },
+ "expected": {"error": "amount must be less than balance"}
+ },
+ {
+ "uuid": "4f381ef8-10ef-4507-8e1d-0631ecc8ee72",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "deposit",
+ "value": 100
+ },
+ {
+ "operation": "withdraw",
+ "value": -50
+ }
+ ]
+ },
+ "expected": {"error": "amount must be greater than 0"}
+ },
+ {
+ "uuid": "d45df9ea-1db0-47f3-b18c-d365db49d938",
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "bankAccont",
+ "input": {
+ "operations": [
+ {
+ "operation": "open"
+ },
+ {
+ "operation": "deposit",
+ "value": -50
+ }
+ ]
+ },
+ "expected": {"error": "amount must be greater than 0"}
+ }
+]
+}
\ No newline at end of file
From 9bf39ef617a1960b393643bbe5cfc8933c56573e Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 19 Dec 2022 09:38:24 +0100
Subject: [PATCH 027/126] fix
---
exercises/practice/bank-account/.meta/template.j2 | 2 ++
1 file changed, 2 insertions(+)
diff --git a/exercises/practice/bank-account/.meta/template.j2 b/exercises/practice/bank-account/.meta/template.j2
index 99c21bc8f6a..3e2980484b6 100644
--- a/exercises/practice/bank-account/.meta/template.j2
+++ b/exercises/practice/bank-account/.meta/template.j2
@@ -11,6 +11,8 @@
{% endif %}
{%- if case["expected"] and inputs|length > 1 -%}
{%- set inputs = inputs[:-1] -%}
+ {%- elif case["expected"] -%}
+ {%- set inputs = [] -%}
{%- endif -%}
{%- for input in inputs -%}
{%- set operation = input["operation"] -%}
From f95892f4be9b5c896901571befd4bb26ca0b4f32 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 14 Apr 2023 19:07:29 -0700
Subject: [PATCH 028/126] Re-worked PR for Newly Accepted Canonical Data
- Removed and regenerated `tests.toml`
- Removed and regenerated `bank_account_test.py`
- Re-worked the JinJa2 template
- Removed the custom `canonical_data.json`
-Removed the `bin/githelp.py` workaround
- Updated `example.py`
- Added meatball and bethanyg as contributors
---
bin/githelp.py | 2 +-
.../practice/bank-account/.meta/config.json | 4 +-
.../practice/bank-account/.meta/example.py | 2 +-
.../practice/bank-account/.meta/template.j2 | 42 +--
.../practice/bank-account/.meta/tests.toml | 51 +++-
.../bank-account/bank_account_test.py | 57 ++--
.../practice/bank-account/canonical_data.json | 285 ------------------
7 files changed, 102 insertions(+), 341 deletions(-)
delete mode 100644 exercises/practice/bank-account/canonical_data.json
diff --git a/bin/githelp.py b/bin/githelp.py
index 9710e0c1361..3d3be06f664 100644
--- a/bin/githelp.py
+++ b/bin/githelp.py
@@ -6,7 +6,7 @@
from typing import Iterator, Union
-GITHUB_EXERCISM = f"https://github.com/meatball133"
+GITHUB_EXERCISM = f"https://github.com/exercism"
class Repo(Enum):
diff --git a/exercises/practice/bank-account/.meta/config.json b/exercises/practice/bank-account/.meta/config.json
index 8ab9aa0d888..51483ecb667 100644
--- a/exercises/practice/bank-account/.meta/config.json
+++ b/exercises/practice/bank-account/.meta/config.json
@@ -3,7 +3,9 @@
"contributors": [
"cmccandless",
"Dog",
- "tqa236"
+ "tqa236",
+ "bethanyg",
+ "meatball133"
],
"files": {
"solution": [
diff --git a/exercises/practice/bank-account/.meta/example.py b/exercises/practice/bank-account/.meta/example.py
index 21248fa9257..90ddf31c23b 100644
--- a/exercises/practice/bank-account/.meta/example.py
+++ b/exercises/practice/bank-account/.meta/example.py
@@ -4,7 +4,7 @@
class BankAccount:
def __init__(self):
self.is_open = False
- self.balance = 0
+ self.balance = None
self.lock = threading.Lock()
def get_balance(self):
diff --git a/exercises/practice/bank-account/.meta/template.j2 b/exercises/practice/bank-account/.meta/template.j2
index 3e2980484b6..f077837a0fd 100644
--- a/exercises/practice/bank-account/.meta/template.j2
+++ b/exercises/practice/bank-account/.meta/template.j2
@@ -3,47 +3,49 @@
def test_{{ case["description"] | to_snake }}(self):
account = BankAccount()
{%- set inputs = case["input"]["operations"] -%}
- {%- if case["expected"] -%}
+ {%- if case["expected"]["error"] -%}
{%- set error_case = true -%}
{%- set error_msg = case["expected"]["error"] -%}
{%- set error_operation = inputs[-1]["operation"] -%}
- {%- set final_value = inputs[-1]["value"] -%}
- {% endif %}
- {%- if case["expected"] and inputs|length > 1 -%}
+ {%- set final_value = inputs[-1]["amount"] -%}
{%- set inputs = inputs[:-1] -%}
- {%- elif case["expected"] -%}
- {%- set inputs = [] -%}
- {%- endif -%}
+ {% endif %}
{%- for input in inputs -%}
{%- set operation = input["operation"] -%}
- {%- set value = input["value"] -%}
- {%- set expected = input["expected"] %}
+ {%- set value = input["amount"] -%}
+ {%- set expected = case["expected"] %}
{%- if operation and value %}
account.{{ operation }}({{ value }})
- {%- elif operation == "amount" %}
+ {%- elif not value and operation %}
+ {%- if not error_case and operation == "balance" %}
self.assertEqual(account.get_balance(), {{ expected }})
- {%- elif operation and not value %}
+ {%- else %}
account.{{ operation }}()
- {%- endif %}
- {%- endfor %}
- {%- if error_case %}
+ {%- endif %}
+ {%- endif %}
+ {%- endfor %}
+ {%- if error_case %}
with self.assertRaises(ValueError) as err:
+ {%- if error_operation == "balance" %}
+ account.get_balance({{ final_value if final_value else "" }})
+ {%- else %}
account.{{ error_operation }}({{ final_value if final_value else "" }})
+ {%- endif %}
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ error_msg }}")
- {%- endif %}
+ {%- endif %}
{% endmacro %}
{{ macros.header(["BankAccount"]) }}
class {{ exercise | camel_case }}Test(unittest.TestCase):
{% for case in cases -%}
- {{ test_case(case) }}
+ {{ test_case(case) }}
{% endfor %}
{% if additional_cases | length -%}
- # Additional tests for this track
- {% for case in additional_cases -%}
- {{ test_case(case) }}
- {% endfor %}
+ # Additional tests for this track
+ {% for case in additional_cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
{%- endif %}
diff --git a/exercises/practice/bank-account/.meta/tests.toml b/exercises/practice/bank-account/.meta/tests.toml
index 6778e39f458..1704a08c5ad 100644
--- a/exercises/practice/bank-account/.meta/tests.toml
+++ b/exercises/practice/bank-account/.meta/tests.toml
@@ -1,43 +1,62 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
[983a1528-4ceb-45e5-8257-8ce01aceb5ed]
-description = "encode yes"
+description = "Newly opened account has zero balance"
[e88d4ec3-c6bf-4752-8e59-5046c44e3ba7]
-description = "encode no"
+description = "Single deposit"
+
+[3d9147d4-63f4-4844-8d2b-1fee2e9a2a0d]
+description = "Multiple deposits"
[08f1af07-27ae-4b38-aa19-770bde558064]
-description = "encode OMG"
+description = "Withdraw once"
[6f6d242f-8c31-4ac6-8995-a90d42cad59f]
-description = "encode spaces"
+description = "Withdraw twice"
+
+[45161c94-a094-4c77-9cec-998b70429bda]
+description = "Can do multiple operations sequentially"
[f9facfaa-d824-486e-8381-48832c4bbffd]
-description = "encode mindblowingly"
+description = "Cannot check balance of closed account"
[7a65ba52-e35c-4fd2-8159-bda2bde6e59c]
-description = "encode numbers"
+description = "Cannot deposit into closed account"
+
+[a0a1835d-faae-4ad4-a6f3-1fcc2121380b]
+description = "Cannot deposit into unopened account"
[570dfaa5-0532-4c1f-a7d3-0f65c3265608]
-description = "encode deep thought"
+description = "Cannot withdraw from closed account"
[c396d233-1c49-4272-98dc-7f502dbb9470]
-description = "encode all the letters"
+description = "Cannot close an account that was not opened"
[c06f534f-bdc2-4a02-a388-1063400684de]
-description = "decode exercism"
+description = "Cannot open an already opened account"
[0722d404-6116-4f92-ba3b-da7f88f1669c]
-description = "decode a sentence"
+description = "Reopened account does not retain balance"
[ec42245f-9361-4341-8231-a22e8d19c52f]
-description = "decode numbers"
+description = "Cannot withdraw more than deposited"
[4f381ef8-10ef-4507-8e1d-0631ecc8ee72]
-description = "decode all the letters"
+description = "Cannot withdraw negative"
[d45df9ea-1db0-47f3-b18c-d365db49d938]
-description = "decode with too many spaces"
+description = "Cannot deposit negative"
+[ba0c1e0b-0f00-416f-8097-a7dfc97871ff]
+description = "Can handle concurrent transactions"
+include = false
diff --git a/exercises/practice/bank-account/bank_account_test.py b/exercises/practice/bank-account/bank_account_test.py
index 5a50505010a..76ddbfe404a 100644
--- a/exercises/practice/bank-account/bank_account_test.py
+++ b/exercises/practice/bank-account/bank_account_test.py
@@ -8,25 +8,32 @@
class BankAccountTest(unittest.TestCase):
- def test_using_pop_raises_an_error_if_the_list_is_empty(self):
+ def test_newly_opened_account_has_zero_balance(self):
account = BankAccount()
account.open()
self.assertEqual(account.get_balance(), 0)
- def test_can_return_with_pop_and_then_raise_an_error_if_empty(self):
+ def test_single_deposit(self):
account = BankAccount()
account.open()
account.deposit(100)
self.assertEqual(account.get_balance(), 100)
- def test_using_shift_raises_an_error_if_the_list_is_empty(self):
+ def test_multiple_deposits(self):
account = BankAccount()
account.open()
account.deposit(100)
- account.withdraw(50)
- self.assertEqual(account.get_balance(), 50)
+ account.deposit(50)
+ self.assertEqual(account.get_balance(), 150)
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_withdraw_once(self):
+ account = BankAccount()
+ account.open()
+ account.deposit(100)
+ account.withdraw(75)
+ self.assertEqual(account.get_balance(), 25)
+
+ def test_withdraw_twice(self):
account = BankAccount()
account.open()
account.deposit(100)
@@ -34,16 +41,26 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account.withdraw(20)
self.assertEqual(account.get_balance(), 0)
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_can_do_multiple_operations_sequentially(self):
+ account = BankAccount()
+ account.open()
+ account.deposit(100)
+ account.deposit(110)
+ account.withdraw(200)
+ account.deposit(60)
+ account.withdraw(50)
+ self.assertEqual(account.get_balance(), 20)
+
+ def test_cannot_check_balance_of_closed_account(self):
account = BankAccount()
account.open()
account.close()
with self.assertRaises(ValueError) as err:
- account.amount()
+ account.get_balance()
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_cannot_deposit_into_closed_account(self):
account = BankAccount()
account.open()
account.close()
@@ -52,7 +69,14 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_cannot_deposit_into_unopened_account(self):
+ account = BankAccount()
+ with self.assertRaises(ValueError) as err:
+ account.deposit(50)
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "account not open")
+
+ def test_cannot_withdraw_from_closed_account(self):
account = BankAccount()
account.open()
account.close()
@@ -61,15 +85,14 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_cannot_close_an_account_that_was_not_opened(self):
account = BankAccount()
- account.close()
with self.assertRaises(ValueError) as err:
account.close()
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account not open")
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_cannot_open_an_already_opened_account(self):
account = BankAccount()
account.open()
with self.assertRaises(ValueError) as err:
@@ -77,7 +100,7 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "account already open")
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_reopened_account_does_not_retain_balance(self):
account = BankAccount()
account.open()
account.deposit(50)
@@ -85,7 +108,7 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
account.open()
self.assertEqual(account.get_balance(), 0)
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_cannot_withdraw_more_than_deposited(self):
account = BankAccount()
account.open()
account.deposit(25)
@@ -94,7 +117,7 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "amount must be less than balance")
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_cannot_withdraw_negative(self):
account = BankAccount()
account.open()
account.deposit(100)
@@ -103,7 +126,7 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "amount must be greater than 0")
- def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ def test_cannot_deposit_negative(self):
account = BankAccount()
account.open()
with self.assertRaises(ValueError) as err:
diff --git a/exercises/practice/bank-account/canonical_data.json b/exercises/practice/bank-account/canonical_data.json
deleted file mode 100644
index 21e868eac9d..00000000000
--- a/exercises/practice/bank-account/canonical_data.json
+++ /dev/null
@@ -1,285 +0,0 @@
-{
- "exercise": "bank-account",
- "comments": [
- "In order to keep the interface for the exercise close or equal to the ",
- "description.md and also satisfying the canonical-data schema, the only ",
- "properties are pop, push, shift, unshift. Some tracks also implement ",
- "delete and count, via Track-Inserted hints. ",
- " ",
- "It is hard to have interesting tests using the property based approach so",
- "this canonical data uses the following approach: ",
- "- Each test input is an array of { operation, value?, expected? } ",
- "- If operation is push, unshift, delete, then value is given ",
- "- If operation is pop, shift, count, the expected value can be given ",
- " ",
- "Encoding tests and operations using the same field is necessary to have ",
- "tests that don't just initialize the list, and then have one operation. "
- ],
- "cases": [
- {
- "uuid": "983a1528-4ceb-45e5-8257-8ce01aceb5ed",
- "description": "Using pop raises an error if the list is empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "amount",
- "value": 0
- }
- ]
- }, "expected": {}
- },
- {
- "uuid": "e88d4ec3-c6bf-4752-8e59-5046c44e3ba7",
- "description" : "Can return with pop and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "deposit",
- "value": 100
- },
- {
- "operation": "amount",
- "expected": 100
- }
- ]
- },
- "expected": {}
- },
- {
- "uuid": "08f1af07-27ae-4b38-aa19-770bde558064",
- "description": "Using shift raises an error if the list is empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "deposit",
- "value": 100
- },
- {
- "operation": "withdraw",
- "value": 50
- },
- {
- "operation": "amount",
- "value": 50
- }
- ]
- },
- "expected": {}
- },
- {
- "uuid": "6f6d242f-8c31-4ac6-8995-a90d42cad59f",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "deposit",
- "value": 100
- },
- {
- "operation": "withdraw",
- "value": 80
- },
- {
- "operation": "withdraw",
- "value": 20
- },
- {
- "operation": "amount",
- "value": 0
- }
- ]
- },
- "expected": {}
- },
- {
- "uuid": "f9facfaa-d824-486e-8381-48832c4bbffd",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "close"
- },
- {
- "operation": "amount"
- }
- ]
- },
- "expected": {"error": "account not open"}
- },
- {
- "uuid": "7a65ba52-e35c-4fd2-8159-bda2bde6e59c",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "close"
- },
- {
- "operation": "deposit",
- "value": 50
- }
- ]
- },
- "expected": {"error": "account not open"}
- },
- {
- "uuid": "570dfaa5-0532-4c1f-a7d3-0f65c3265608",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "close"
- },
- {
- "operation": "withdraw",
- "value": 50
- }
- ]
- },
- "expected": {"error": "account not open"}
- },
- {
- "uuid": "c396d233-1c49-4272-98dc-7f502dbb9470",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "close"
- }
- ]
- },
- "expected": {"error": "account not open"}
- },
- {
- "uuid": "c06f534f-bdc2-4a02-a388-1063400684de",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "open"
- }
- ]
- },
- "expected": {"error": "account already open"}
- },
- {
- "uuid": "0722d404-6116-4f92-ba3b-da7f88f1669c",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "deposit",
- "value": 50
- },
- {
- "operation": "close"
- },
- {
- "operation": "open"
- },
- {
- "operation": "amount",
- "value": 0
- }
- ]
- },
- "expected": {}
- },
- {
- "uuid": "ec42245f-9361-4341-8231-a22e8d19c52f",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "deposit",
- "value": 25
- },
- {
- "operation": "withdraw",
- "value": 50
- }
- ]
- },
- "expected": {"error": "amount must be less than balance"}
- },
- {
- "uuid": "4f381ef8-10ef-4507-8e1d-0631ecc8ee72",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "deposit",
- "value": 100
- },
- {
- "operation": "withdraw",
- "value": -50
- }
- ]
- },
- "expected": {"error": "amount must be greater than 0"}
- },
- {
- "uuid": "d45df9ea-1db0-47f3-b18c-d365db49d938",
- "description": "Can return with shift and then raise an error if empty",
- "property": "bankAccont",
- "input": {
- "operations": [
- {
- "operation": "open"
- },
- {
- "operation": "deposit",
- "value": -50
- }
- ]
- },
- "expected": {"error": "amount must be greater than 0"}
- }
-]
-}
\ No newline at end of file
From c64b14e4c835ab4e150c4b8610c6290e59d7b7aa Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Tue, 18 Apr 2023 09:31:14 +0200
Subject: [PATCH 029/126] Sync simple-linked-list docs with
problem-specifications
The simple-linked-list exercise has been overhauled as part of a project
to make practice exercises more consistent and friendly.
For more context, please see the discussion in the forum, as well as
the pull request that updated the exercise in the problem-specifications
repository:
- https://forum.exercism.org/t/new-project-making-practice-exercises-more-consistent-and-human-across-exercism/3943
---
.../simple-linked-list/.docs/instructions.md | 20 +++++++++++--------
.../simple-linked-list/.docs/introduction.md | 5 +++++
2 files changed, 17 insertions(+), 8 deletions(-)
create mode 100644 exercises/practice/simple-linked-list/.docs/introduction.md
diff --git a/exercises/practice/simple-linked-list/.docs/instructions.md b/exercises/practice/simple-linked-list/.docs/instructions.md
index 4d845fac06c..04640b1fb03 100644
--- a/exercises/practice/simple-linked-list/.docs/instructions.md
+++ b/exercises/practice/simple-linked-list/.docs/instructions.md
@@ -1,15 +1,19 @@
# Instructions
-Write a simple linked list implementation that uses Elements and a List.
+Write a prototype of the music player application.
-The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures.
-They're pervasive in functional programming languages, such as Clojure, Erlang, or Haskell, but far less common in imperative languages such as Ruby or Python.
+For the prototype, each song will simply be represented by a number.
+Given a range of numbers (the song IDs), create a singly linked list.
+
+Given a singly linked list, you should be able to reverse the list to play the songs in the opposite order.
-The simplest kind of linked list is a singly linked list.
-Each element in the list contains data and a "next" field pointing to the next element in the list of elements.
+~~~~exercism/note
+The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures.
-This variant of linked lists is often used to represent sequences or push-down stacks (also called a LIFO stack; Last In, First Out).
+The simplest kind of linked list is a **singly** linked list.
+That means that each element (or "node") contains data, along with something that points to the next node in the list.
-As a first take, lets create a singly linked list to contain the range (1..10), and provide functions to reverse a linked list and convert to and from arrays.
+If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings.
-When implementing this in a language with built-in linked lists, implement your own abstract data type.
+[intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d
+~~~~
diff --git a/exercises/practice/simple-linked-list/.docs/introduction.md b/exercises/practice/simple-linked-list/.docs/introduction.md
new file mode 100644
index 00000000000..0e1df72f9bf
--- /dev/null
+++ b/exercises/practice/simple-linked-list/.docs/introduction.md
@@ -0,0 +1,5 @@
+# Introduction
+
+You work for a music streaming company.
+
+You've been tasked with creating a playlist feature for your music player application.
From 86ac5442fe0e7f6a9dbf5fd4514899cfbb4c76df Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Fri, 7 Apr 2023 15:40:16 -0700
Subject: [PATCH 030/126] list-ops: add foldl canonical test; bring foldr in
line with canonical specs and add canonical test
---
.../practice/list-ops/.meta/additional_tests.json | 4 ++--
exercises/practice/list-ops/.meta/example.py | 2 +-
exercises/practice/list-ops/.meta/template.j2 | 1 -
exercises/practice/list-ops/.meta/tests.toml | 2 --
exercises/practice/list-ops/list_ops_test.py | 10 +++++++++-
5 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/exercises/practice/list-ops/.meta/additional_tests.json b/exercises/practice/list-ops/.meta/additional_tests.json
index 7eb1d65c767..39c0a8bfa63 100644
--- a/exercises/practice/list-ops/.meta/additional_tests.json
+++ b/exercises/practice/list-ops/.meta/additional_tests.json
@@ -6,7 +6,7 @@
"input": {
"list": ["e", "x", "e", "r", "c", "i", "s", "m"],
"initial": "!",
- "function": "(x, y) -> x + y"
+ "function": "(acc, el) -> el + acc"
},
"expected": "exercism!"
},
@@ -19,4 +19,4 @@
"expected": [1, "cat", 4.0, "xyz"]
}
]
-}
\ No newline at end of file
+}
diff --git a/exercises/practice/list-ops/.meta/example.py b/exercises/practice/list-ops/.meta/example.py
index ee43248f99e..75f45033e2b 100644
--- a/exercises/practice/list-ops/.meta/example.py
+++ b/exercises/practice/list-ops/.meta/example.py
@@ -29,7 +29,7 @@ def foldr(function, list, initial):
if len(list) == 0:
return initial
else:
- return function(list[0], foldr(function, list[1:], initial))
+ return function(foldr(function, list[1:], initial), list[0])
def reverse(list):
diff --git a/exercises/practice/list-ops/.meta/template.j2 b/exercises/practice/list-ops/.meta/template.j2
index c346d3c20fb..cc28280f174 100644
--- a/exercises/practice/list-ops/.meta/template.j2
+++ b/exercises/practice/list-ops/.meta/template.j2
@@ -2,7 +2,6 @@
{% macro lambdify(function) -%}
{% set function = function.replace("(", "", 1).replace(")", "", 1).replace(" ->", ":") %}
{% set function = function.replace("modulo", "%") %}
- {% set function = function.replace("/", "//") %}
lambda {{function}}
{%- endmacro %}
diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml
index 7fe3c8d1a9c..08b1edc0443 100644
--- a/exercises/practice/list-ops/.meta/tests.toml
+++ b/exercises/practice/list-ops/.meta/tests.toml
@@ -71,7 +71,6 @@ reimplements = "e56df3eb-9405-416a-b13a-aabb4c3b5194"
[d7fcad99-e88e-40e1-a539-4c519681f390]
description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list"
reimplements = "d2cf5644-aee1-4dfc-9b88-06896676fe27"
-include = false
[aeb576b9-118e-4a57-a451-db49fac20fdc]
description = "folds (reduces) the given list from the right with a function -> empty list"
@@ -96,7 +95,6 @@ reimplements = "c4b64e58-313e-4c47-9c68-7764964efb8e"
[8066003b-f2ff-437e-9103-66e6df474844]
description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list"
reimplements = "be396a53-c074-4db3-8dd6-f7ed003cce7c"
-include = false
[94231515-050e-4841-943d-d4488ab4ee30]
description = "reverse the elements of the list -> empty list"
diff --git a/exercises/practice/list-ops/list_ops_test.py b/exercises/practice/list-ops/list_ops_test.py
index cff6156dc65..a2a301295ac 100644
--- a/exercises/practice/list-ops/list_ops_test.py
+++ b/exercises/practice/list-ops/list_ops_test.py
@@ -63,12 +63,18 @@ def test_foldl_empty_list(self):
def test_foldl_direction_independent_function_applied_to_non_empty_list(self):
self.assertEqual(foldl(lambda acc, el: el + acc, [1, 2, 3, 4], 5), 15)
+ def test_foldl_direction_dependent_function_applied_to_non_empty_list(self):
+ self.assertEqual(foldl(lambda acc, el: el / acc, [1, 2, 3, 4], 24), 64)
+
def test_foldr_empty_list(self):
self.assertEqual(foldr(lambda acc, el: el * acc, [], 2), 2)
def test_foldr_direction_independent_function_applied_to_non_empty_list(self):
self.assertEqual(foldr(lambda acc, el: el + acc, [1, 2, 3, 4], 5), 15)
+ def test_foldr_direction_dependent_function_applied_to_non_empty_list(self):
+ self.assertEqual(foldr(lambda acc, el: el / acc, [1, 2, 3, 4], 24), 9)
+
def test_reverse_empty_list(self):
self.assertEqual(reverse([]), [])
@@ -84,7 +90,9 @@ def test_reverse_list_of_lists_is_not_flattened(self):
def test_foldr_foldr_add_string(self):
self.assertEqual(
- foldr(lambda x, y: x + y, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"),
+ foldr(
+ lambda acc, el: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"
+ ),
"exercism!",
)
From 4c5cdbf5c11b4cb3463210de9164469dbaed896b Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Mon, 10 Apr 2023 19:46:21 -0700
Subject: [PATCH 031/126] list-ops tests: swap foldr args order to use func(el,
acc)
---
exercises/practice/list-ops/.meta/example.py | 2 +-
exercises/practice/list-ops/.meta/template.j2 | 10 +++++++++-
exercises/practice/list-ops/list_ops_test.py | 8 ++++----
3 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/list-ops/.meta/example.py b/exercises/practice/list-ops/.meta/example.py
index 75f45033e2b..ee43248f99e 100644
--- a/exercises/practice/list-ops/.meta/example.py
+++ b/exercises/practice/list-ops/.meta/example.py
@@ -29,7 +29,7 @@ def foldr(function, list, initial):
if len(list) == 0:
return initial
else:
- return function(foldr(function, list[1:], initial), list[0])
+ return function(list[0], foldr(function, list[1:], initial))
def reverse(list):
diff --git a/exercises/practice/list-ops/.meta/template.j2 b/exercises/practice/list-ops/.meta/template.j2
index cc28280f174..be224ff251b 100644
--- a/exercises/practice/list-ops/.meta/template.j2
+++ b/exercises/practice/list-ops/.meta/template.j2
@@ -1,4 +1,10 @@
{%- import "generator_macros.j2" as macros with context -%}
+{% macro swap_args(function) -%}
+ {% set function = function.replace("(acc, el)", "(el, acc)") %}
+ {% set function = function.replace("(x, y)", "(y, x)") %}
+ {{function}}
+{%- endmacro %}
+
{% macro lambdify(function) -%}
{% set function = function.replace("(", "", 1).replace(")", "", 1).replace(" ->", ":") %}
{% set function = function.replace("modulo", "%") %}
@@ -29,8 +35,10 @@
{{ lambdify(input["function"]) }}, {{ input["list"] }}
{%- elif case["property"] == "length" or case["property"] == "reverse" -%}
{{ input["list"] }}
- {%- elif case["property"] == "foldl" or case["property"] == "foldr" -%}
+ {%- elif case["property"] == "foldl" -%}
{{ lambdify(input["function"]) }}, {{ input["list"] }}, {{ stringify(input["initial"]) }}
+ {%- elif case["property"] == "foldr" -%}
+ {{ lambdify(swap_args(input["function"])) }}, {{ input["list"] }}, {{ stringify(input["initial"]) }}
{%- endif -%}
),
{{ stringify(case["expected"]) }}
diff --git a/exercises/practice/list-ops/list_ops_test.py b/exercises/practice/list-ops/list_ops_test.py
index a2a301295ac..617be353c66 100644
--- a/exercises/practice/list-ops/list_ops_test.py
+++ b/exercises/practice/list-ops/list_ops_test.py
@@ -67,13 +67,13 @@ def test_foldl_direction_dependent_function_applied_to_non_empty_list(self):
self.assertEqual(foldl(lambda acc, el: el / acc, [1, 2, 3, 4], 24), 64)
def test_foldr_empty_list(self):
- self.assertEqual(foldr(lambda acc, el: el * acc, [], 2), 2)
+ self.assertEqual(foldr(lambda el, acc: el * acc, [], 2), 2)
def test_foldr_direction_independent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldr(lambda acc, el: el + acc, [1, 2, 3, 4], 5), 15)
+ self.assertEqual(foldr(lambda el, acc: el + acc, [1, 2, 3, 4], 5), 15)
def test_foldr_direction_dependent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldr(lambda acc, el: el / acc, [1, 2, 3, 4], 24), 9)
+ self.assertEqual(foldr(lambda el, acc: el / acc, [1, 2, 3, 4], 24), 9)
def test_reverse_empty_list(self):
self.assertEqual(reverse([]), [])
@@ -91,7 +91,7 @@ def test_reverse_list_of_lists_is_not_flattened(self):
def test_foldr_foldr_add_string(self):
self.assertEqual(
foldr(
- lambda acc, el: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"
+ lambda el, acc: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"
),
"exercism!",
)
From 7e470c1d54edb7d3746260ddc7512db5ab80e433 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Wed, 12 Apr 2023 15:15:21 -0700
Subject: [PATCH 032/126] Make foldl also call func(el, acc)
---
exercises/practice/list-ops/.meta/example.py | 2 +-
exercises/practice/list-ops/.meta/template.j2 | 4 +---
exercises/practice/list-ops/list_ops_test.py | 6 +++---
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/exercises/practice/list-ops/.meta/example.py b/exercises/practice/list-ops/.meta/example.py
index ee43248f99e..eac3b3c7742 100644
--- a/exercises/practice/list-ops/.meta/example.py
+++ b/exercises/practice/list-ops/.meta/example.py
@@ -22,7 +22,7 @@ def foldl(function, list, initial):
if len(list) == 0:
return initial
else:
- return foldl(function, list[1:], function(initial, list[0]))
+ return foldl(function, list[1:], function(list[0], initial))
def foldr(function, list, initial):
diff --git a/exercises/practice/list-ops/.meta/template.j2 b/exercises/practice/list-ops/.meta/template.j2
index be224ff251b..76c97ace37b 100644
--- a/exercises/practice/list-ops/.meta/template.j2
+++ b/exercises/practice/list-ops/.meta/template.j2
@@ -35,9 +35,7 @@
{{ lambdify(input["function"]) }}, {{ input["list"] }}
{%- elif case["property"] == "length" or case["property"] == "reverse" -%}
{{ input["list"] }}
- {%- elif case["property"] == "foldl" -%}
- {{ lambdify(input["function"]) }}, {{ input["list"] }}, {{ stringify(input["initial"]) }}
- {%- elif case["property"] == "foldr" -%}
+ {%- elif case["property"] == "foldl" or case["property"] == "foldr" -%}
{{ lambdify(swap_args(input["function"])) }}, {{ input["list"] }}, {{ stringify(input["initial"]) }}
{%- endif -%}
),
diff --git a/exercises/practice/list-ops/list_ops_test.py b/exercises/practice/list-ops/list_ops_test.py
index 617be353c66..94dd32e0d5a 100644
--- a/exercises/practice/list-ops/list_ops_test.py
+++ b/exercises/practice/list-ops/list_ops_test.py
@@ -58,13 +58,13 @@ def test_map_non_empty_list(self):
self.assertEqual(list_ops_map(lambda x: x + 1, [1, 3, 5, 7]), [2, 4, 6, 8])
def test_foldl_empty_list(self):
- self.assertEqual(foldl(lambda acc, el: el * acc, [], 2), 2)
+ self.assertEqual(foldl(lambda el, acc: el * acc, [], 2), 2)
def test_foldl_direction_independent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldl(lambda acc, el: el + acc, [1, 2, 3, 4], 5), 15)
+ self.assertEqual(foldl(lambda el, acc: el + acc, [1, 2, 3, 4], 5), 15)
def test_foldl_direction_dependent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldl(lambda acc, el: el / acc, [1, 2, 3, 4], 24), 64)
+ self.assertEqual(foldl(lambda el, acc: el / acc, [1, 2, 3, 4], 24), 64)
def test_foldr_empty_list(self):
self.assertEqual(foldr(lambda el, acc: el * acc, [], 2), 2)
From 648088ed514494594566010f05fcf3aedb9da78f Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Wed, 12 Apr 2023 21:21:25 -0700
Subject: [PATCH 033/126] Add a comment explaining the fold arg swap
---
exercises/practice/list-ops/.meta/template.j2 | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/list-ops/.meta/template.j2 b/exercises/practice/list-ops/.meta/template.j2
index 76c97ace37b..af52ffbcce2 100644
--- a/exercises/practice/list-ops/.meta/template.j2
+++ b/exercises/practice/list-ops/.meta/template.j2
@@ -1,5 +1,6 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro swap_args(function) -%}
+ {# Args are swapped to use Rust's fold tradition where it calls func(el, acc) and not func(acc, el) #}
{% set function = function.replace("(acc, el)", "(el, acc)") %}
{% set function = function.replace("(x, y)", "(y, x)") %}
{{function}}
From 888021b1e8617196338c2cc17cecce323d573609 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Wed, 12 Apr 2023 21:23:28 -0700
Subject: [PATCH 034/126] Remove the swap and use Haskell tradition, i.e. what
the problem spec uses
---
exercises/practice/list-ops/.meta/example.py | 4 ++--
exercises/practice/list-ops/.meta/template.j2 | 9 +--------
exercises/practice/list-ops/list_ops_test.py | 14 +++++++-------
3 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/exercises/practice/list-ops/.meta/example.py b/exercises/practice/list-ops/.meta/example.py
index eac3b3c7742..75f45033e2b 100644
--- a/exercises/practice/list-ops/.meta/example.py
+++ b/exercises/practice/list-ops/.meta/example.py
@@ -22,14 +22,14 @@ def foldl(function, list, initial):
if len(list) == 0:
return initial
else:
- return foldl(function, list[1:], function(list[0], initial))
+ return foldl(function, list[1:], function(initial, list[0]))
def foldr(function, list, initial):
if len(list) == 0:
return initial
else:
- return function(list[0], foldr(function, list[1:], initial))
+ return function(foldr(function, list[1:], initial), list[0])
def reverse(list):
diff --git a/exercises/practice/list-ops/.meta/template.j2 b/exercises/practice/list-ops/.meta/template.j2
index af52ffbcce2..cc28280f174 100644
--- a/exercises/practice/list-ops/.meta/template.j2
+++ b/exercises/practice/list-ops/.meta/template.j2
@@ -1,11 +1,4 @@
{%- import "generator_macros.j2" as macros with context -%}
-{% macro swap_args(function) -%}
- {# Args are swapped to use Rust's fold tradition where it calls func(el, acc) and not func(acc, el) #}
- {% set function = function.replace("(acc, el)", "(el, acc)") %}
- {% set function = function.replace("(x, y)", "(y, x)") %}
- {{function}}
-{%- endmacro %}
-
{% macro lambdify(function) -%}
{% set function = function.replace("(", "", 1).replace(")", "", 1).replace(" ->", ":") %}
{% set function = function.replace("modulo", "%") %}
@@ -37,7 +30,7 @@
{%- elif case["property"] == "length" or case["property"] == "reverse" -%}
{{ input["list"] }}
{%- elif case["property"] == "foldl" or case["property"] == "foldr" -%}
- {{ lambdify(swap_args(input["function"])) }}, {{ input["list"] }}, {{ stringify(input["initial"]) }}
+ {{ lambdify(input["function"]) }}, {{ input["list"] }}, {{ stringify(input["initial"]) }}
{%- endif -%}
),
{{ stringify(case["expected"]) }}
diff --git a/exercises/practice/list-ops/list_ops_test.py b/exercises/practice/list-ops/list_ops_test.py
index 94dd32e0d5a..a2a301295ac 100644
--- a/exercises/practice/list-ops/list_ops_test.py
+++ b/exercises/practice/list-ops/list_ops_test.py
@@ -58,22 +58,22 @@ def test_map_non_empty_list(self):
self.assertEqual(list_ops_map(lambda x: x + 1, [1, 3, 5, 7]), [2, 4, 6, 8])
def test_foldl_empty_list(self):
- self.assertEqual(foldl(lambda el, acc: el * acc, [], 2), 2)
+ self.assertEqual(foldl(lambda acc, el: el * acc, [], 2), 2)
def test_foldl_direction_independent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldl(lambda el, acc: el + acc, [1, 2, 3, 4], 5), 15)
+ self.assertEqual(foldl(lambda acc, el: el + acc, [1, 2, 3, 4], 5), 15)
def test_foldl_direction_dependent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldl(lambda el, acc: el / acc, [1, 2, 3, 4], 24), 64)
+ self.assertEqual(foldl(lambda acc, el: el / acc, [1, 2, 3, 4], 24), 64)
def test_foldr_empty_list(self):
- self.assertEqual(foldr(lambda el, acc: el * acc, [], 2), 2)
+ self.assertEqual(foldr(lambda acc, el: el * acc, [], 2), 2)
def test_foldr_direction_independent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldr(lambda el, acc: el + acc, [1, 2, 3, 4], 5), 15)
+ self.assertEqual(foldr(lambda acc, el: el + acc, [1, 2, 3, 4], 5), 15)
def test_foldr_direction_dependent_function_applied_to_non_empty_list(self):
- self.assertEqual(foldr(lambda el, acc: el / acc, [1, 2, 3, 4], 24), 9)
+ self.assertEqual(foldr(lambda acc, el: el / acc, [1, 2, 3, 4], 24), 9)
def test_reverse_empty_list(self):
self.assertEqual(reverse([]), [])
@@ -91,7 +91,7 @@ def test_reverse_list_of_lists_is_not_flattened(self):
def test_foldr_foldr_add_string(self):
self.assertEqual(
foldr(
- lambda el, acc: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"
+ lambda acc, el: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"
),
"exercism!",
)
From 461e6fc91e418bbd2e36d0504903c3b3ad8cd00d Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Tue, 9 May 2023 13:41:17 -0700
Subject: [PATCH 035/126] [list-ops] Add IsaacG as a contributor
---
exercises/practice/list-ops/.meta/config.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json
index efc025a3543..cdb33138a4e 100644
--- a/exercises/practice/list-ops/.meta/config.json
+++ b/exercises/practice/list-ops/.meta/config.json
@@ -8,6 +8,7 @@
"Dog",
"dvermd",
"gabriel376",
+ "IsaacG",
"N-Parsons",
"pheanex",
"rootulp",
From c1cc9f9594034cc202d16fe0c8d984e23c47e6fa Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 18 May 2023 09:20:22 -0700
Subject: [PATCH 036/126] Updated metadata from problem specifications.
---
exercises/practice/binary/.meta/config.json | 2 +-
exercises/practice/etl/.meta/config.json | 4 ++--
exercises/practice/hexadecimal/.meta/config.json | 2 +-
exercises/practice/nucleotide-count/.meta/config.json | 2 +-
exercises/practice/octal/.meta/config.json | 2 +-
exercises/practice/point-mutations/.meta/config.json | 2 +-
exercises/practice/trinary/.meta/config.json | 2 +-
7 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/binary/.meta/config.json b/exercises/practice/binary/.meta/config.json
index 7170a4ab191..b9f5b07508a 100644
--- a/exercises/practice/binary/.meta/config.json
+++ b/exercises/practice/binary/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "All of Computer Science",
- "source_url": "http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-"
+ "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
}
diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json
index 4d99e6314da..32aab8ba3ba 100644
--- a/exercises/practice/etl/.meta/config.json
+++ b/exercises/practice/etl/.meta/config.json
@@ -27,7 +27,7 @@
".meta/example.py"
]
},
- "blurb": "We are going to do the `Transform` step of an Extract-Transform-Load.",
- "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
+ "blurb": "Change the data format for scoring a game to more easily add other languages.",
+ "source": "Based on an exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
"source_url": "https://turing.edu"
}
diff --git a/exercises/practice/hexadecimal/.meta/config.json b/exercises/practice/hexadecimal/.meta/config.json
index 352ca16d78c..b61e1a2c057 100644
--- a/exercises/practice/hexadecimal/.meta/config.json
+++ b/exercises/practice/hexadecimal/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "All of Computer Science",
- "source_url": "http://www.wolframalpha.com/examples/NumberBases.html"
+ "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
}
diff --git a/exercises/practice/nucleotide-count/.meta/config.json b/exercises/practice/nucleotide-count/.meta/config.json
index 4e91bb7ee9f..e0c108f7b88 100644
--- a/exercises/practice/nucleotide-count/.meta/config.json
+++ b/exercises/practice/nucleotide-count/.meta/config.json
@@ -28,5 +28,5 @@
]
},
"source": "The Calculating DNA Nucleotides_problem at Rosalind",
- "source_url": "http://rosalind.info/problems/dna/"
+ "source_url": "https://rosalind.info/problems/dna/"
}
diff --git a/exercises/practice/octal/.meta/config.json b/exercises/practice/octal/.meta/config.json
index e39e3fb6870..4fe6c11bdef 100644
--- a/exercises/practice/octal/.meta/config.json
+++ b/exercises/practice/octal/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "All of Computer Science",
- "source_url": "http://www.wolframalpha.com/input/?i=base+8"
+ "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
}
diff --git a/exercises/practice/point-mutations/.meta/config.json b/exercises/practice/point-mutations/.meta/config.json
index 2d8d765c187..b02f3706511 100644
--- a/exercises/practice/point-mutations/.meta/config.json
+++ b/exercises/practice/point-mutations/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "The Calculating Point Mutations problem at Rosalind",
- "source_url": "http://rosalind.info/problems/hamm/"
+ "source_url": "https://rosalind.info/problems/hamm/"
}
diff --git a/exercises/practice/trinary/.meta/config.json b/exercises/practice/trinary/.meta/config.json
index 104b505a620..da59ec6c571 100644
--- a/exercises/practice/trinary/.meta/config.json
+++ b/exercises/practice/trinary/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "All of Computer Science",
- "source_url": "http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-"
+ "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
}
From e03b3cdd56495a8497bb3108a86213f7a29c43f1 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Wed, 7 Dec 2022 22:58:17 +0100
Subject: [PATCH 037/126] depricated diffie-hellman
---
config.json | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/config.json b/config.json
index 1d118f79c88..47548a89015 100644
--- a/config.json
+++ b/config.json
@@ -1247,14 +1247,6 @@
],
"difficulty": 3
},
- {
- "slug": "diffie-hellman",
- "name": "Diffie-Hellman",
- "uuid": "92e2d5f8-7d8a-4e81-a55c-52fa6be80c74",
- "practices": ["numbers"],
- "prerequisites": ["basics", "bools", "numbers"],
- "difficulty": 3
- },
{
"slug": "connect",
"name": "Connect",
@@ -2201,6 +2193,15 @@
"difficulty": 3,
"status": "deprecated"
},
+ {
+ "slug": "diffie-hellman",
+ "name": "Diffie-Hellman",
+ "uuid": "92e2d5f8-7d8a-4e81-a55c-52fa6be80c74",
+ "practices": [],
+ "prerequisites": [],
+ "difficulty": 3,
+ "status": "deprecated"
+ },
{
"slug": "trinary",
"name": "Trinary",
From 520c3e637033c1ed1329c46ef880ad03b5582d80 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 27 Dec 2022 22:13:00 +0100
Subject: [PATCH 038/126] Started on improvments
---
concepts/generators/about.md | 63 +++++++++++++++--------------
concepts/generators/introduction.md | 4 +-
concepts/generators/links.json | 4 +-
3 files changed, 37 insertions(+), 34 deletions(-)
diff --git a/concepts/generators/about.md b/concepts/generators/about.md
index f1b97291e4c..a136dde5076 100644
--- a/concepts/generators/about.md
+++ b/concepts/generators/about.md
@@ -4,65 +4,63 @@
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:
-
+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
+... 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(list_of_numbers):
- for number in list_of_number:
- yield number ** 2
+>>> 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 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.
+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:
+To use the `squares_generator()` generator:
```python
->>> squared_numbers = squares([1, 2, 3, 4])
+>>> squared_numbers = squares_generator([1, 2, 3, 4])
>>> for square in squared_numbers:
->>> print(square)
+... print(square)
+...
1
4
9
16
```
-Values within a generator can also be produced/accessed via the `next()` function.
+Values within a generator can also be produced/accessed via the `next()` function.
`next()` calls the `__next__()` method of a generator object, "advancing" or evaluating the generator code up to its `yield` expression, which then "yields" or returns the value.
```python
-square_generator = squares([1, 2])
+>>> squared_numbers = squares_generator([1, 2])
->>> next(square_generator)
+>>> next(squared_numbers)
1
->>> next(square_generator)
+>>> next(squared_numbers)
4
```
When a `generator` is fully consumed and has no more values to return, it throws a `StopIteration` error.
```python
->>> next(square_generator)
+>>> next(squared_numbers)
Traceback (most recent call last):
File "", line 1, in
StopIteration
@@ -72,12 +70,12 @@ StopIteration
Generators are a special sub-set of _iterators_.
`Iterators` are the mechanism/protocol that enables looping over _iterables_.
-Generators and and the iterators returned by common Python (`iterables`)[https://wiki.python.org/moin/Iterator] act very similarly, but there are some important differences to note:
-
+Generators and the iterators returned by common Python [`iterables`][iterables] act very similarly, but there are some important differences to note:
- Generators are _one-way_; there is no "backing up" to a previous value.
- Iterating over generators consume the returned values; no resetting.
+
- Generators (_being lazily evaluated_) are not sortable and can not be reversed.
- Generators do _not_ have `indexes`, so you can't reference a previous or future value using addition or subtraction.
@@ -88,7 +86,7 @@ Generators and and the iterators returned by common Python (`iterables`)[https:/
## The yield expression
-The [yield expression](https://docs.python.org/3.8/reference/expressions.html#yield-expressions) is very similar to the `return` 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.
@@ -100,10 +98,10 @@ Note: _Using `yield` expressions is prohibited outside of functions._
```python
>>> def infinite_sequence():
->>> current_number = 0
->>> while True:
->>> yield current_number
->>> current_number += 1
+... current_number = 0
+... while True:
+... yield current_number
+... current_number += 1
>>> lets_try = infinite_sequence()
>>> lets_try.__next__()
@@ -123,10 +121,13 @@ Generators are also very helpful when a process or calculation is _complex_, _ex
```python
>>> def infinite_sequence():
->>> current_number = 0
->>> while True:
->>> yield current_number
->>> current_number += 1
+... 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.
+
+[iterables]: https://wiki.python.org/moin/Iterator
+[yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions
diff --git a/concepts/generators/introduction.md b/concepts/generators/introduction.md
index 2c148371330..82a686d1e05 100644
--- a/concepts/generators/introduction.md
+++ b/concepts/generators/introduction.md
@@ -1,5 +1,7 @@
# Introduction
-A generator in Python is a _callable function_ that returns a [lazy iterator](https://en.wikipedia.org/wiki/Lazy_evaluation).
+A generator in Python is a _callable function_ that returns a [lazy iterator][lazy iterator].
_Lazy iterators_ are similar to `lists`, and other `iterators`, but with one key difference: They do not store their `values` in memory, but _generate_ their values when needed.
+
+[lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation
diff --git a/concepts/generators/links.json b/concepts/generators/links.json
index b8ae2f7b648..eea7d4ae338 100644
--- a/concepts/generators/links.json
+++ b/concepts/generators/links.json
@@ -1,7 +1,7 @@
[
{
- "url": "https://docs.python.org/3.8/reference/expressions.html#yield-expressions",
- "description": "Official Python 3.8 docs for the yield expression."
+ "url": "https://docs.python.org/3.11/reference/expressions.html#yield-expressions",
+ "description": "Official Python 3.11 docs for the yield expression."
},
{
"url": "https://en.wikipedia.org/wiki/Lazy_evaluation",
From e18b1e1c300243713198017aa1b84be53b704f09 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 27 Dec 2022 23:28:51 +0100
Subject: [PATCH 039/126] Added an extra exercise
---
.../plane-tickets/.docs/instructions.md | 45 +++++++++++++------
.../concept/plane-tickets/.meta/config.json | 2 +-
.../concept/plane-tickets/.meta/exemplar.py | 29 +++++++++---
.../concept/plane-tickets/plane_tickets.py | 18 +++++++-
.../plane-tickets/plane_tickets_test.py | 36 ++++++++++-----
5 files changed, 98 insertions(+), 32 deletions(-)
diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md
index 290aad2ebb7..9863fe293fb 100644
--- a/exercises/concept/plane-tickets/.docs/instructions.md
+++ b/exercises/concept/plane-tickets/.docs/instructions.md
@@ -2,9 +2,10 @@
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 automated.
+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 the assigning of seats to passengers. They require your software to be memory efficient and performant.
+They have asked _you_ to create software to automate the assigning of seats to passengers.
+They require your software to be memory efficient and performant.
Conda's airplanes have up to _4 seats_ in each row, and each airplane has many rows.
@@ -12,13 +13,29 @@ While the rows are defined using numbers, seats in each row are defined using le
You can use this table as a guide:
-| x | 1 | 2 |
-| :----: | :----: | :----:|
-| Row | 5 | 21 |
-| Seat letter | A | D |
-| Result | 5A | 21D |
+| x | 1 | 2 |
+| :---------: | :-: | :-: |
+| Row | 5 | 21 |
+| Seat letter | A | D |
+| Result | 5A | 21D |
-## 1. Generate an amount of seats
+## 1. Generate seat letters
+
+Implement the `generate_seat_letters()` function that returns an _iterable_ of seat letters given the following variable:
+
+`amount`: The amount of seat letters to be generated.
+
+The letters should be generated in alphabetical order, starting with `A` and ending with `D`.
+
+```python
+>>> letters = generate_seat_letters(4)
+>>> next(letters)
+"A"
+>>> next(letters)
+"B"
+```
+
+## 2. Generate an amount of seats
Implement the `generate_seats()` function that returns an _iterable_ of seats given the following variable:
@@ -37,9 +54,10 @@ _Note: The returned seats should be ordered, like: 1A 1B 1C._
"1B"
```
-## 2. Assign seats to passengers
+## 3. Assign seats to passengers
-Implement the `assign_seats()` function that returns a _dictionary_ of `passenger` as _key_, and `seat_number` as _value_. Given is the following _list_:
+Implement the `assign_seats()` function that returns a _dictionary_ of `passenger` as _key_, and `seat_number` as _value_.
+Given is the following _list_:
`passengers`: A list containing passenger names.
@@ -50,15 +68,16 @@ Implement the `assign_seats()` function that returns a _dictionary_ of `passenge
{'Jerimiah': '1A', 'Eric': '1B', 'Bethaney': '1C', 'Byte': '1D', 'SqueekyBoots': '2A', 'Bob': '2B'}
```
-## 3. Ticket codes
+## 4. Ticket codes
Each ticket has a _12_ character 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`.
+This code begins with the `assigned_seat` followed by the `flight_id`.
+The rest of the code is appended by `0s`.
Implement a `generator` that yields a `ticket_number` given the following arguments:
-`seat_numbers`: A _list_ of *seat_numbers*.
+`seat_numbers`: A _list_ of _seat_numbers_.
`flight_id`: A string containing the flight identification.
```python
diff --git a/exercises/concept/plane-tickets/.meta/config.json b/exercises/concept/plane-tickets/.meta/config.json
index 016ff0cef23..2698188896e 100644
--- a/exercises/concept/plane-tickets/.meta/config.json
+++ b/exercises/concept/plane-tickets/.meta/config.json
@@ -16,6 +16,6 @@
".meta/exemplar.py"
]
},
- "icon": "poker",
+ "icon": "new-passport",
"blurb": "Learn about generators by assigning seats to passengers."
}
diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py
index fa8927d759a..703e31cb3fc 100644
--- a/exercises/concept/plane-tickets/.meta/exemplar.py
+++ b/exercises/concept/plane-tickets/.meta/exemplar.py
@@ -1,11 +1,28 @@
-"""Plane Tickets Exercise"""
+"""Functions to automate Conda airlines ticketing system."""
import math
SEATS_IN_ROW = ['A', 'B', 'C', 'D']
-def generate_seats(amount):
+def generate_seat_letters(amount):
+ """ Generate a series of seat letters for airline boarding.
+
+ :param amount: Amount of seat letters to be generated. (int)
+ :return: Generator that yields seat letters.
+
+ Seat letters are generated with each row having 4 seats.
+ These should be sorted from low to high.
+
+ Example: A, B, C, D
+
+ """
+
+ for seat in range(amount):
+ yield SEATS_IN_ROW[seat % 4]
+
+
+def generate_seats(amount):
"""Generate a series of seat numbers for airline boarding.
:param amount: Amount of seats to be generated. (int)
@@ -21,16 +38,14 @@ def generate_seats(amount):
"""
amount = amount + 4 if amount >= 13 else amount
-
+ letters = generate_seat_letters(amount)
for seat in range(amount):
row_number = math.ceil((seat+1) / 4)
if row_number != 13:
- seat_letter = SEATS_IN_ROW[seat % 4]
- yield f'{str(row_number)}{seat_letter}'
+ yield f'{str(row_number)}{next(letters)}'
def assign_seats(passengers):
-
- """Assign seats to passenger.
+ """Assign seats to passengers.
:param passengers: A list of strings containing names of passengers. (list[str])
:return: A dictionary type object containing the names of the passengers as keys and seat numbers as values.
diff --git a/exercises/concept/plane-tickets/plane_tickets.py b/exercises/concept/plane-tickets/plane_tickets.py
index 44d685535b6..2d8ad345a70 100644
--- a/exercises/concept/plane-tickets/plane_tickets.py
+++ b/exercises/concept/plane-tickets/plane_tickets.py
@@ -1,4 +1,20 @@
-"""Plane Tickets Exercise"""
+"""Functions to automate Conda airlines ticketing system."""
+
+def generate_seat_letters(amount):
+ """ Generate a series of seat letters for airline boarding.
+
+ :param amount: Amount of seat letters to be generated. (int)
+ :return: Generator that yields seat letters.
+
+ Seat letters are generated with each row having 4 seats.
+ These should be sorted from low to high.
+
+ Example: A, B, C, D
+
+ """
+
+ pass
+
def generate_seats(amount):
diff --git a/exercises/concept/plane-tickets/plane_tickets_test.py b/exercises/concept/plane-tickets/plane_tickets_test.py
index f4255c43af6..1372e5cd2d7 100644
--- a/exercises/concept/plane-tickets/plane_tickets_test.py
+++ b/exercises/concept/plane-tickets/plane_tickets_test.py
@@ -3,6 +3,7 @@
import pytest
from plane_tickets import (
+ generate_seat_letters,
generate_seats,
assign_seats,
generate_codes
@@ -10,16 +11,31 @@
class PlaneTicketsTest(unittest.TestCase):
-
@pytest.mark.task(taskno=1)
def test_task1_is_generator(self): # * Tests if [Task 1] actually returns a generator.
input_var = 5
output_type = Generator
error_message = f"Expected: {str(output_type)} type, but got a different type."
- self.assertIsInstance(generate_seats(input_var), output_type, msg=error_message)
+ self.assertIsInstance(generate_seat_letters(input_var), output_type, msg=error_message)
@pytest.mark.task(taskno=1)
def test_task1_output(self):
+ input_vars = [1, 2, 3, 4, 5]
+ output = [["A"], ["A", "B"], ["A", "B", "C"], ["A", "B", "C", "D"], ["A", "B", "C", "D", "A"]]
+ for variant, (input_var, output) in enumerate(zip(input_vars, output), start=1):
+ error_message = f"Expected: {output}, but something went wrong while generating {input_var} seat(s)."
+ with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
+ self.assertEqual(list(generate_seat_letters(input_var)), output, msg=error_message)
+
+ @pytest.mark.task(taskno=2)
+ def test_task2_is_generator(self): # * Tests if [Task 2] actually returns a generator.
+ input_var = 5
+ output_type = Generator
+ error_message = f"Expected: {str(output_type)} type, but got a different type."
+ self.assertIsInstance(generate_seats(input_var), output_type, msg=error_message)
+
+ @pytest.mark.task(taskno=2)
+ def test_task2_output(self):
input_vars = [1, 2, 3, 4, 5]
output = [["1A"], ["1A", "1B"], ["1A", "1B", "1C"], ["1A", "1B", "1C", "1D"], ["1A", "1B", "1C", "1D", "2A"]]
for variant, (input_var, output) in enumerate(zip(input_vars, output), start=1):
@@ -27,8 +43,8 @@ def test_task1_output(self):
with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
self.assertEqual(list(generate_seats(input_var)), output, msg=error_message)
- @pytest.mark.task(taskno=1)
- def test_task1_skips_row_13(self):
+ @pytest.mark.task(taskno=2)
+ def test_task3_skips_row_13(self):
input_vars = [14 * 4]
output = [["1A", "1B", "1C", "1D", "2A", "2B", "2C", "2D",
"3A", "3B", "3C", "3D", "4A", "4B", "4C", "4D",
@@ -42,8 +58,8 @@ def test_task1_skips_row_13(self):
with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
self.assertEqual(list(generate_seats(input_var)), output, msg=error_message)
- @pytest.mark.task(taskno=2)
- def test_task2(self):
+ @pytest.mark.task(taskno=3)
+ def test_task3(self):
input_vars = [["Passenger1", "Passenger2", "Passenger3", "Passenger4", "Passenger5"],
["TicketNo=5644", "TicketNo=2273", "TicketNo=493", "TicketNo=5411", "TicketNo=824"]]
output = [{"Passenger1": "1A", "Passenger2": "1B", "Passenger3": "1C", "Passenger4": "1D", "Passenger5": "2A"},
@@ -53,15 +69,15 @@ def test_task2(self):
with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
self.assertEqual(assign_seats(input_var), output, msg=error_message)
- @pytest.mark.task(taskno=3)
- def test_task3_is_generator(self):
+ @pytest.mark.task(taskno=4)
+ def test_task4_is_generator(self):
input_var = ("11B", "HA80085")
output_type = Generator
error_message = f"Expected: {str(output_type)} type, but got a different type."
self.assertIsInstance(generate_codes(input_var[0], input_var[1]), output_type, msg=error_message)
- @pytest.mark.task(taskno=3)
- def test_task3(self):
+ @pytest.mark.task(taskno=4)
+ def test_task4(self):
input_vars = [(["12A", "38B", "69C", "102B"],"KL1022"),
(["22C", "88B", "33A", "44B"], "DL1002")]
output = [['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200'],
From ea3e886757332b4afb1beeec5017750e89b2f0c6 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 14:25:37 +0100
Subject: [PATCH 040/126] Small fix
---
exercises/concept/plane-tickets/.meta/exemplar.py | 1 -
exercises/concept/plane-tickets/plane_tickets.py | 3 ---
2 files changed, 4 deletions(-)
diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py
index 703e31cb3fc..6bf9ce68a11 100644
--- a/exercises/concept/plane-tickets/.meta/exemplar.py
+++ b/exercises/concept/plane-tickets/.meta/exemplar.py
@@ -61,7 +61,6 @@ def assign_seats(passengers):
return output
def generate_codes(seat_numbers, flight_id):
-
"""Generate codes for a ticket.
:param seat_numbers: A list of seat numbers. (list[str])
diff --git a/exercises/concept/plane-tickets/plane_tickets.py b/exercises/concept/plane-tickets/plane_tickets.py
index 2d8ad345a70..ff2e8a58eed 100644
--- a/exercises/concept/plane-tickets/plane_tickets.py
+++ b/exercises/concept/plane-tickets/plane_tickets.py
@@ -17,7 +17,6 @@ def generate_seat_letters(amount):
def generate_seats(amount):
-
""" Generate a series of seat numbers for airline boarding.
:param amount: Amount of seats to be generated. (int)
@@ -35,7 +34,6 @@ def generate_seats(amount):
pass
def assign_seats(passengers):
-
""" Assign seats to passengers.
:param passengers: A list of strings containing names of passengers. (list[str])
@@ -48,7 +46,6 @@ def assign_seats(passengers):
pass
def generate_codes(seat_numbers, flight_id):
-
"""Generate codes for a ticket.
:param seat_numbers: A list of seat numbers. (list[str])
From 16916968e77938041f2aa849731ffc008af68a1f Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 14:46:58 +0100
Subject: [PATCH 041/126] Runed prettier and major changes to instructions.md
---
.../plane-tickets/.docs/instructions.md | 54 ++++++++--------
.../concept/plane-tickets/.meta/design.md | 62 ++++++++-----------
2 files changed, 54 insertions(+), 62 deletions(-)
diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md
index 9863fe293fb..2798bf8651e 100644
--- a/exercises/concept/plane-tickets/.docs/instructions.md
+++ b/exercises/concept/plane-tickets/.docs/instructions.md
@@ -7,25 +7,14 @@ They are currently assigning all seats to passengers by hand, this will need to
They have asked _you_ to create software to automate the assigning of seats to passengers.
They require your software to be memory efficient and performant.
-Conda's airplanes have up to _4 seats_ in each row, and each airplane has many rows.
-
-While the rows are defined using numbers, seats in each row are defined using letters from the alphabet, with `seat A` being the first _seat_ in the row.
-
-You can use this table as a guide:
-
-| x | 1 | 2 |
-| :---------: | :-: | :-: |
-| Row | 5 | 21 |
-| Seat letter | A | D |
-| Result | 5A | 21D |
-
## 1. Generate seat letters
-Implement the `generate_seat_letters()` function that returns an _iterable_ of seat letters given the following variable:
+Conda wants to generate seat letters for their airplanes.
+Every row has _4 seats_.
+They all have the same pattern: `A`, `B`, `C`, `D`.
-`amount`: The amount of seat letters to be generated.
-
-The letters should be generated in alphabetical order, starting with `A` and ending with `D`.
+Implement a function `generate_seat_letters()` 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)
@@ -37,14 +26,24 @@ The letters should be generated in alphabetical order, starting with `A` and end
## 2. Generate an amount of seats
-Implement the `generate_seats()` function that returns an _iterable_ of seats given the following variable:
+Conda wants a system that can generate an amount 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:
-`amount`: The amount of seats to be generated.
+| 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.
-_Note: The returned seats should be ordered, like: 1A 1B 1C._
+Implement a function `generate_seats()` hat accepts an `int` 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)
@@ -56,10 +55,10 @@ _Note: The returned seats should be ordered, like: 1A 1B 1C._
## 3. Assign seats to passengers
-Implement the `assign_seats()` function that returns a _dictionary_ of `passenger` as _key_, and `seat_number` as _value_.
-Given is the following _list_:
+Now that you have a function that generates seats, you can use it to assign seats to passengers.
-`passengers`: A list containing passenger names.
+Implement a function `assign_seats()` 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', 'Bethaney', 'Byte', 'SqueekyBoots', 'Bob']
@@ -70,15 +69,16 @@ Given is the following _list_:
## 4. Ticket codes
-Each ticket has a _12_ character long string code for identification.
+Conda Airlines would like to have a unique code for each ticket.
+Since they are a big airline, they have a lot of flights.
+Meaning that there are multiple flights with the same seat number.
+They want you to create a system that creats a unique ticket that has _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 `generator` that yields a `ticket_number` given the following arguments:
-
-`seat_numbers`: A _list_ of _seat_numbers_.
-`flight_id`: A string containing the flight identification.
+Implement a function `generate_codes()` 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']
diff --git a/exercises/concept/plane-tickets/.meta/design.md b/exercises/concept/plane-tickets/.meta/design.md
index 58542aab87c..0323418c857 100644
--- a/exercises/concept/plane-tickets/.meta/design.md
+++ b/exercises/concept/plane-tickets/.meta/design.md
@@ -2,16 +2,15 @@ This issue describes how to implement the `generators` concept exercise for the
## Goal
-The goal of this exercise is to teach the syntax and use of `generators` in Python.
+The goal of this exercise is to teach the syntax and use of `generators` in Python.
## Learning objectives
-- Understand what generators are and how/when to use them
-- Understand how generators relate to `loops` and `iterators`
-- Understand how to use the `yield` keyword
-- Understand the `__next__()` method
-- Create a generator
-
+- Understand what generators are and how/when to use them
+- Understand how generators relate to `loops` and `iterators`
+- Understand how to use the `yield` keyword
+- Understand the `__next__()` method
+- Create a generator
## Out of scope
@@ -22,32 +21,28 @@ The goal of this exercise is to teach the syntax and use of `generators` in Pyth
- `yield from`
- `generators` used as coroutines
-
## Concepts covered
-- `generators`
-- `yield`
-- `__next__()`
-- `iterators`
-
+- `generators`
+- `yield`
+- `__next__()`
+- `iterators`
## Prerequisites
-- `conditionals`
-- `dicts`
-- `functions`
-- `higher-order-functions`
-- `lists`
-- `loops`
-- `iteration`
-- `iterators`
-- `sequences`
-
-
+- `conditionals`
+- `dicts`
+- `functions`
+- `higher-order-functions`
+- `lists`
+- `loops`
+- `iteration`
+- `iterators`
+- `sequences`
## Resources to refer to
-- [Generators (Python official docs)](https://docs.python.org/3/howto/functional.html#generators)
+- [Generators (Python official docs)](https://docs.python.org/3/howto/functional.html#generators)
- [generator (Python official docs glossary)](https://docs.python.org/3/glossary.html#term-generator)
- [The yield statement (Python official docs)](https://docs.python.org/3/reference/simple_stmts.html#the-yield-statement)
- [Yield expressions (Python official docs)](https://docs.python.org/3/reference/expressions.html#yieldexpr)
@@ -57,21 +52,20 @@ The goal of this exercise is to teach the syntax and use of `generators` in Pyth
### Hints
-- Referring to one or more of the resources linked above, or analogous resources from a trusted source.
-- `Generators` section of the Python Docs Functional How to tutorial: [Generators](https://docs.python.org/3/howto/functional.html#generators)
-
+- Referring to one or more of the resources linked above, or analogous resources from a trusted source.
+- `Generators` section of the Python Docs Functional How to tutorial: [Generators](https://docs.python.org/3/howto/functional.html#generators)
-## Concept Description
+## Concept Description
-(_a variant of this can be used for the `v3/languages/python/concepts//about.md` doc and this exercises `introduction.md` doc._)
+(_a variant of this can be used for the `v3/languages/python/concepts//about.md` doc and this exercises `introduction.md` doc._)
_**Concept Description Needs to Be Filled In Here/Written**_
_Some "extras" that we might want to include as notes in the concept description, or as links in `links.json`:_
-- Additional `Generator-iterator methods`, such as `generator.send()` and `generator.throw()`
+- Additional `Generator-iterator methods`, such as `generator.send()` and `generator.throw()`
- `generator expressions`
-- Asynchronous generator functions
+- Asynchronous generator functions
- `generators` used as coroutines
## Implementing
@@ -80,10 +74,8 @@ The general Python track concept exercise implantation guide can be found [here]
Tests should be written using `unittest.TestCase` and the test file named `generators_test.py`.
-Code in the `.meta/example.py` file should **only use syntax & concepts introduced in this exercise or one of its prerequisites.** Please do not use comprehensions, generator expressions, or other syntax not previously covered. Please also follow [PEP8](https://www.python.org/dev/peps/pep-0008/) guidelines.
+Code in the `.meta/example.py` file should **only use syntax & concepts introduced in this exercise or one of its prerequisites.** Please do not use comprehensions, generator expressions, or other syntax not previously covered. Please also follow [PEP8](https://www.python.org/dev/peps/pep-0008/) guidelines.
## Help
If you have any questions while implementing the exercise, please post the questions as comments in this issue, or contact one of the maintainers on our Slack channel.
-
-
From 0a08d61f4d7cabdf8b7885d668223a76fd20f404 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 21:03:43 +0100
Subject: [PATCH 042/126] spell fixes
---
exercises/concept/plane-tickets/.docs/instructions.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md
index 2798bf8651e..f49c4ff8dfb 100644
--- a/exercises/concept/plane-tickets/.docs/instructions.md
+++ b/exercises/concept/plane-tickets/.docs/instructions.md
@@ -42,7 +42,7 @@ Here is an example:
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()` hat accepts an `int` that accepts an `int` that holds how many seats to be generated.
+Implement a function `generate_seats()` that accepts an `int` that accepts an `int` that holds how many seats to be generated.
The function should then return an _iterable_ of seats given.
```python
@@ -72,7 +72,7 @@ The function should then return a _dictionary_ of `passenger` as _key_, and `sea
Conda Airlines would like to have a unique code for each ticket.
Since they are a big airline, they have a lot of flights.
Meaning that there are multiple flights with the same seat number.
-They want you to create a system that creats a unique ticket that has _12_ characters long string code for identification.
+They want you to create a system that creates a unique ticket that has _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`.
From 4378e0c8d8fdbd7fe7adaa5ac4cae8a93067acef Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 14:18:54 +0100
Subject: [PATCH 043/126] some improvments
---
exercises/concept/plane-tickets/.docs/instructions.md | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md
index f49c4ff8dfb..b321addbd38 100644
--- a/exercises/concept/plane-tickets/.docs/instructions.md
+++ b/exercises/concept/plane-tickets/.docs/instructions.md
@@ -10,8 +10,10 @@ They require your software to be memory efficient and performant.
## 1. Generate seat letters
Conda wants to generate seat letters for their airplanes.
-Every row has _4 seats_.
-They all have the same pattern: `A`, `B`, `C`, `D`.
+An airplane is made of rows of seats.
+Each row has _4 seats_.
+The rows seats has the same naming: `A`, `B`, `C`, `D`.
+Meaning the first seat in the row is `A`, the second seat in the row is `B`, and so on.
Implement a function `generate_seat_letters()` that accepts an `int` that holds how many seat letters to be generated.
The function should then return an _iterable_ of seat letters.
@@ -72,7 +74,7 @@ The function should then return a _dictionary_ of `passenger` as _key_, and `sea
Conda Airlines would like to have a unique code for each ticket.
Since they are a big airline, they have a lot of flights.
Meaning that there are multiple flights with the same seat number.
-They want you to create a system that creates a unique ticket that has _12_ characters long string code for identification.
+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`.
From 1cc64ac300f3eac61ef5b07a26561709b3d36211 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 14:30:16 +0100
Subject: [PATCH 044/126] Updated doc-string
---
.../concept/plane-tickets/.meta/exemplar.py | 18 +++++++++---------
.../concept/plane-tickets/plane_tickets.py | 18 +++++++++---------
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py
index 6bf9ce68a11..27995eba7bc 100644
--- a/exercises/concept/plane-tickets/.meta/exemplar.py
+++ b/exercises/concept/plane-tickets/.meta/exemplar.py
@@ -8,8 +8,8 @@
def generate_seat_letters(amount):
""" Generate a series of seat letters for airline boarding.
- :param amount: Amount of seat letters to be generated. (int)
- :return: Generator that yields seat letters.
+ :param amount: int - amount of seat letters to be generated.
+ :return: generator - generator that yields seat letters.
Seat letters are generated with each row having 4 seats.
These should be sorted from low to high.
@@ -25,8 +25,8 @@ def generate_seat_letters(amount):
def generate_seats(amount):
"""Generate a series of seat numbers for airline boarding.
- :param amount: Amount of seats to be generated. (int)
- :return: Generator that generates seat numbers. (generator)
+ :param amount: int - Amount of seats to be generated.
+ :return: generator - generator that yields seat numbers.
There should be no row 13
@@ -47,8 +47,8 @@ def generate_seats(amount):
def assign_seats(passengers):
"""Assign seats to passengers.
- :param passengers: A list of strings containing names of passengers. (list[str])
- :return: A dictionary type object containing the names of the passengers as keys and seat numbers as values.
+ :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: {"Foo": "1A", "Bar": "1B"}
@@ -63,9 +63,9 @@ def assign_seats(passengers):
def generate_codes(seat_numbers, flight_id):
"""Generate codes for a ticket.
- :param seat_numbers: A list of seat numbers. (list[str])
- :param flight_id: A string containing the flight identification. (str)
- :return: Generator that generates 12 character long strings. (generator[str])
+ :param seat_numbers: list[str] - list of seat numbers.
+ :param flight_id: str - string containing the flight identification.
+ :return: generator - generator that yields 12 character long strings.
"""
diff --git a/exercises/concept/plane-tickets/plane_tickets.py b/exercises/concept/plane-tickets/plane_tickets.py
index ff2e8a58eed..b0c481e46ba 100644
--- a/exercises/concept/plane-tickets/plane_tickets.py
+++ b/exercises/concept/plane-tickets/plane_tickets.py
@@ -3,8 +3,8 @@
def generate_seat_letters(amount):
""" Generate a series of seat letters for airline boarding.
- :param amount: Amount of seat letters to be generated. (int)
- :return: Generator that yields seat letters.
+ :param amount: int - amount of seat letters to be generated.
+ :return: generator - generator that yields seat letters.
Seat letters are generated with each row having 4 seats.
These should be sorted from low to high.
@@ -19,8 +19,8 @@ def generate_seat_letters(amount):
def generate_seats(amount):
""" Generate a series of seat numbers for airline boarding.
- :param amount: Amount of seats to be generated. (int)
- :return: Generator that yields seat numbers.
+ :param amount: int - Amount of seats to be generated.
+ :return: generator - generator that yields seat numbers.
There should be no row 13
@@ -36,8 +36,8 @@ def generate_seats(amount):
def assign_seats(passengers):
""" Assign seats to passengers.
- :param passengers: A list of strings containing names of passengers. (list[str])
- :return: A dictionary type object containing the names of the passengers as keys and seat numbers as values.
+ :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: {"Foo": "1A", "Bar": "1B"}
@@ -48,8 +48,8 @@ def assign_seats(passengers):
def generate_codes(seat_numbers, flight_id):
"""Generate codes for a ticket.
- :param seat_numbers: A list of seat numbers. (list[str])
- :param flight_id: A string containing the flight identification. (str)
- :return: Generator that generates 12 character long strings. (generator[str])
+ :param seat_numbers: list[str] - list of seat numbers.
+ :param flight_id: str - string containing the flight identification.
+ :return: generator - generator that yields 12 character long strings.
"""
From cd77e99ea0d62a23ac2bea9fc4d8d813681b4c45 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 23:43:26 +0100
Subject: [PATCH 045/126] added ckasses as prereq and minor changes
---
concepts/generators/about.md | 4 +++-
config.json | 2 +-
exercises/concept/plane-tickets/.meta/design.md | 1 +
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/concepts/generators/about.md b/concepts/generators/about.md
index a136dde5076..9a26ab55480 100644
--- a/concepts/generators/about.md
+++ b/concepts/generators/about.md
@@ -94,7 +94,9 @@ When `yield` is evaluated, it pauses the execution of the enclosing function and
The function then _stays in scope_, and when `__next__()` is called, execution resumes until `yield` is encountered again.
-Note: _Using `yield` expressions is prohibited outside of functions._
+```exercism/note
+Using `yield` expressions is prohibited outside of functions.
+```
```python
>>> def infinite_sequence():
diff --git a/config.json b/config.json
index 47548a89015..038123f69e5 100644
--- a/config.json
+++ b/config.json
@@ -184,7 +184,7 @@
"name": "Plane Tickets",
"uuid": "3ba3fc89-3e1b-48a5-aff0-5aeaba8c8810",
"concepts": ["generators"],
- "prerequisites": ["conditionals", "dicts", "lists", "loops"],
+ "prerequisites": ["conditionals", "dicts", "lists", "loops", "classes"],
"status": "wip"
},
{
diff --git a/exercises/concept/plane-tickets/.meta/design.md b/exercises/concept/plane-tickets/.meta/design.md
index 0323418c857..96a4cc3cc91 100644
--- a/exercises/concept/plane-tickets/.meta/design.md
+++ b/exercises/concept/plane-tickets/.meta/design.md
@@ -39,6 +39,7 @@ The goal of this exercise is to teach the syntax and use of `generators` in Pyth
- `iteration`
- `iterators`
- `sequences`
+- `classes`
## Resources to refer to
From 9b5bd3910708da4e6bcc5499789a985d1644e08f Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 00:13:33 +0100
Subject: [PATCH 046/126] Updated doc string
---
exercises/concept/plane-tickets/.meta/exemplar.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py
index 27995eba7bc..20a4f252ebc 100644
--- a/exercises/concept/plane-tickets/.meta/exemplar.py
+++ b/exercises/concept/plane-tickets/.meta/exemplar.py
@@ -6,13 +6,13 @@
def generate_seat_letters(amount):
- """ Generate a series of seat letters for airline boarding.
+ """Generate a series of seat letters for airline boarding.
:param amount: int - amount of seat letters to be generated.
:return: generator - generator that yields seat letters.
- Seat letters are generated with each row having 4 seats.
- These should be sorted from low to high.
+ Seat letters are generated with from A to D.
+ After D it should start again with A.
Example: A, B, C, D
From 0f0d8c86f279969bf0f191d177f2dd330582f513 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 12:41:11 +0100
Subject: [PATCH 047/126] Updated, links, added blurb, various other changes
---
concepts/generators/.meta/config.json | 2 +-
concepts/generators/links.json | 4 +
.../concept/plane-tickets/.docs/hints.md | 17 ++-
.../plane-tickets/.docs/instructions.md | 5 +-
.../plane-tickets/.docs/introduction.md | 136 +++++++++++++++++-
.../concept/plane-tickets/.meta/exemplar.py | 2 +-
.../concept/plane-tickets/plane_tickets.py | 4 +-
7 files changed, 159 insertions(+), 11 deletions(-)
diff --git a/concepts/generators/.meta/config.json b/concepts/generators/.meta/config.json
index 2204a700df6..6c29169d3f4 100644
--- a/concepts/generators/.meta/config.json
+++ b/concepts/generators/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "Learn about generators by assigning seats to passengers.",
+ "blurb": "Generator are functions that returns a lazy iterator, lazy iterator is an iterator like: list, tuple, etc. But doesn't need store its content in memory",
"authors": ["J08K"],
"contributors": []
}
diff --git a/concepts/generators/links.json b/concepts/generators/links.json
index eea7d4ae338..2f43923cf5d 100644
--- a/concepts/generators/links.json
+++ b/concepts/generators/links.json
@@ -6,5 +6,9 @@
{
"url": "https://en.wikipedia.org/wiki/Lazy_evaluation",
"description": "Wikipedia page about lazy evaluation"
+ },
+ {
+ "url": "https://realpython.com/introduction-to-python-generators/",
+ "description": "Real python, introduction to generators and yield"
}
]
diff --git a/exercises/concept/plane-tickets/.docs/hints.md b/exercises/concept/plane-tickets/.docs/hints.md
index 72d814b22ae..11508ee3838 100644
--- a/exercises/concept/plane-tickets/.docs/hints.md
+++ b/exercises/concept/plane-tickets/.docs/hints.md
@@ -1,11 +1,24 @@
# Hints
-## 1. Generate an amount of seats
+## 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.
+- And use `yield` to return the next letter.
+
+## 2. Generate an amount of 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, ...`
+- It might be good to reuse or call other functions you have already completed here.
-## 2. Assign seats to passengers
+## 3. Assign seats to passengers
- Make sure your seat numbers do not have any space in them.
+- It might be good to reuse or call other functions you have already completed here.
+
+## 4. Ticket codes
+
+- You can use `len()` to get the length of a string.
+- You can use `"" * ` to repeat a string.
diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md
index b321addbd38..550c7f3f787 100644
--- a/exercises/concept/plane-tickets/.docs/instructions.md
+++ b/exercises/concept/plane-tickets/.docs/instructions.md
@@ -14,6 +14,7 @@ An airplane is made of rows of seats.
Each row has _4 seats_.
The rows seats has the same naming: `A`, `B`, `C`, `D`.
Meaning 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()` that accepts an `int` that holds how many seat letters to be generated.
The function should then return an _iterable_ of seat letters.
@@ -63,10 +64,10 @@ Implement a function `assign_seats()` 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', 'Bethaney', 'Byte', 'SqueekyBoots', 'Bob']
+>>> passengers = ['Jerimiah', 'Eric', 'Bethany', 'Byte', 'SqueekyBoots', 'Bob']
>>> assign_seats(passengers)
-{'Jerimiah': '1A', 'Eric': '1B', 'Bethaney': '1C', 'Byte': '1D', 'SqueekyBoots': '2A', 'Bob': '2B'}
+{'Jerimiah': '1A', 'Eric': '1B', 'Bethany': '1C', 'Byte': '1D', 'SqueekyBoots': '2A', 'Bob': '2B'}
```
## 4. Ticket codes
diff --git a/exercises/concept/plane-tickets/.docs/introduction.md b/exercises/concept/plane-tickets/.docs/introduction.md
index 1b557b447f8..5ab9d6d2611 100644
--- a/exercises/concept/plane-tickets/.docs/introduction.md
+++ b/exercises/concept/plane-tickets/.docs/introduction.md
@@ -1,5 +1,135 @@
-# Introduction
+# About
-A generator in Python is a _callable function_ that returns a [lazy iterator](https://en.wikipedia.org/wiki/Lazy_evaluation).
+## Constructing a generator
-_Lazy iterators_ are similar to iterables such as `lists`, and other types of `iterators` in Python -- but with one key difference: `generators` do not store their `values` in memory, but _generate_ their values as needed or when called.
+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 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 object, "advancing" or evaluating the generator code up to its `yield` expression, which then "yields" or returns the value.
+
+```python
+>>> squared_numbers = squares_generator([1, 2])
+
+>>> next(squared_numbers)
+1
+>>> next(squared_numbers)
+4
+```
+
+When a `generator` 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 "", line 1, in
+StopIteration
+```
+
+### Difference between iterables and generators
+
+Generators are a special sub-set of _iterators_.
+`Iterators` are the mechanism/protocol that enables looping over _iterables_.
+Generators and the iterators returned by common Python [`iterables`][iterables] act very similarly, but there are some important differences to note:
+
+- Generators are _one-way_; there is no "backing up" to a previous value.
+
+- Iterating over generators consume the returned values; no resetting.
+
+- Generators (_being lazily evaluated_) are not sortable and can not be reversed.
+
+- Generators do _not_ have `indexes`, so you can't reference a previous or future value using addition or subtraction.
+
+- Generators cannot be used with the `len()` function.
+
+- Generators can be _finite_ or _infinite_, be careful when collecting all values from an _infinite_ generator.
+
+## 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 generators?
+
+Generators are useful in a lot of applications.
+
+When working with a large collection, you might not want to put all of its values 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.
+
+[iterables]: https://wiki.python.org/moin/Iterator
+[yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions
diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py
index 20a4f252ebc..9a3d7024076 100644
--- a/exercises/concept/plane-tickets/.meta/exemplar.py
+++ b/exercises/concept/plane-tickets/.meta/exemplar.py
@@ -11,7 +11,7 @@ def generate_seat_letters(amount):
:param amount: int - amount of seat letters to be generated.
:return: generator - generator that yields seat letters.
- Seat letters are generated with from A to D.
+ Seat letters are generated from A to D.
After D it should start again with A.
Example: A, B, C, D
diff --git a/exercises/concept/plane-tickets/plane_tickets.py b/exercises/concept/plane-tickets/plane_tickets.py
index b0c481e46ba..4399791b223 100644
--- a/exercises/concept/plane-tickets/plane_tickets.py
+++ b/exercises/concept/plane-tickets/plane_tickets.py
@@ -6,8 +6,8 @@ def generate_seat_letters(amount):
:param amount: int - amount of seat letters to be generated.
:return: generator - generator that yields seat letters.
- Seat letters are generated with each row having 4 seats.
- These should be sorted from low to high.
+ Seat letters are generated from A to D.
+ After D it should start again with A.
Example: A, B, C, D
From bf45569950d3e638b90f005716b014f9126936fc Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 21:08:53 +0100
Subject: [PATCH 048/126] fix
---
exercises/concept/plane-tickets/.docs/instructions.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md
index 550c7f3f787..b22a84f2213 100644
--- a/exercises/concept/plane-tickets/.docs/instructions.md
+++ b/exercises/concept/plane-tickets/.docs/instructions.md
@@ -45,7 +45,7 @@ Here is an example:
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()` that accepts an `int` that accepts an `int` that holds how many seats to be generated.
+Implement a function `generate_seats()` that accepts an `int` that holds how many seats to be generated.
The function should then return an _iterable_ of seats given.
```python
From cb75dfa7dfb936f75dbd44bffdc08767ecf42054 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 10 Jan 2023 23:09:28 +0100
Subject: [PATCH 049/126] Update links.json
---
concepts/generators/links.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/concepts/generators/links.json b/concepts/generators/links.json
index 2f43923cf5d..972bbe7ae97 100644
--- a/concepts/generators/links.json
+++ b/concepts/generators/links.json
@@ -1,7 +1,7 @@
[
{
- "url": "https://docs.python.org/3.11/reference/expressions.html#yield-expressions",
- "description": "Official Python 3.11 docs for the yield expression."
+ "url": "https://docs.python.org/3.10/reference/expressions.html#yield-expressions",
+ "description": "Official Python 3.10 docs for the yield expression."
},
{
"url": "https://en.wikipedia.org/wiki/Lazy_evaluation",
From 89738cea0ccd5820c7fd1fffad98672bae06ba03 Mon Sep 17 00:00:00 2001
From: Safwan Samsudeen <62411302+safwansamsudeen@users.noreply.github.com>
Date: Mon, 5 Jun 2023 10:52:42 +0530
Subject: [PATCH 050/126] Write approaches for Yacht (#3420)
* write approaches for yacht
* improve wording, structure, and code
* update based on bethany's changes
* Apply suggestions from code review
Co-authored-by: BethanyG
* improve snippets, add spm approach
* Apply suggestions from code review
---------
Co-authored-by: BethanyG
---
.../practice/yacht/.approaches/config.json | 28 +++++++
.../yacht/.approaches/functions/content.md | 72 +++++++++++++++++
.../yacht/.approaches/functions/snippet.txt | 8 ++
.../yacht/.approaches/if-structure/content.md | 50 ++++++++++++
.../.approaches/if-structure/snippet.txt | 8 ++
.../yacht/.approaches/introduction.md | 77 +++++++++++++++++++
.../structural-pattern-matching/content.md | 55 +++++++++++++
.../structural-pattern-matching/snippet.txt | 8 ++
8 files changed, 306 insertions(+)
create mode 100644 exercises/practice/yacht/.approaches/config.json
create mode 100644 exercises/practice/yacht/.approaches/functions/content.md
create mode 100644 exercises/practice/yacht/.approaches/functions/snippet.txt
create mode 100644 exercises/practice/yacht/.approaches/if-structure/content.md
create mode 100644 exercises/practice/yacht/.approaches/if-structure/snippet.txt
create mode 100644 exercises/practice/yacht/.approaches/introduction.md
create mode 100644 exercises/practice/yacht/.approaches/structural-pattern-matching/content.md
create mode 100644 exercises/practice/yacht/.approaches/structural-pattern-matching/snippet.txt
diff --git a/exercises/practice/yacht/.approaches/config.json b/exercises/practice/yacht/.approaches/config.json
new file mode 100644
index 00000000000..86d37075b8b
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/config.json
@@ -0,0 +1,28 @@
+{
+ "introduction": {
+ "authors": ["safwansamsudeen"]
+ },
+ "approaches": [
+ {
+ "uuid": "3593cfe3-5cab-4141-b0a2-329148a66bb6",
+ "slug": "functions",
+ "title": "Lambdas with Functions",
+ "blurb": "Use lambdas with functions",
+ "authors": ["safwansamsudeen"]
+ },
+ {
+ "uuid": "eccd1e1e-6c88-4823-9b25-944eccaa92e7",
+ "slug": "if-structure",
+ "title": "If structure",
+ "blurb": "Use an if structure",
+ "authors": ["safwansamsudeen"]
+ },
+ {
+ "uuid": "72079791-e51f-4825-ad94-3b7516c631cc",
+ "slug": "structural-pattern-matching",
+ "title": "Structural Pattern Matching",
+ "blurb": "Use structural pattern matching",
+ "authors": ["safwansamsudeen"]
+ }
+ ]
+}
diff --git a/exercises/practice/yacht/.approaches/functions/content.md b/exercises/practice/yacht/.approaches/functions/content.md
new file mode 100644
index 00000000000..2c6bfe527df
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/functions/content.md
@@ -0,0 +1,72 @@
+## Approach: Using Lambdas with Functions
+Each bit of functionality for each category can be encoded in an anonymous function (otherwise known as a [`lambda` expression][lambda] or lambda form), and the constant name set to that function.
+
+In `score`, we call the category (as it now points to a function) passing in `dice` as an argument.
+
+```python
+def digits(num):
+ return lambda dice: dice.count(num) * num
+
+YACHT = lambda dice: 50 if len(set(dice)) == 1 else 0
+ONES = digits(1)
+TWOS = digits(2)
+THREES = digits(3)
+FOURS = digits(4)
+FIVES = digits(5)
+SIXES = digits(6)
+FULL_HOUSE = lambda dice: sum(dice) if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3] else 0
+FOUR_OF_A_KIND = lambda dice: 4 * dice[1] if dice[0] == dice[3] or dice[1] == dice[4] else 0
+LITTLE_STRAIGHT = lambda dice: 30 if sorted(dice) == [1, 2, 3, 4, 5] else 0
+BIG_STRAIGHT = lambda dice: 30 if sorted(dice) == [2, 3, 4, 5, 6] else 0
+CHOICE = sum
+
+def score(dice, category):
+ return category(dice)
+```
+
+
+Instead of setting each constant in `ONES` through `SIXES` to a separate function, we create a function `digits` that returns a function, using [closures][closures] transparently.
+
+For `LITTLE_STRAIGHT` and `BIG_STRAIGHT`, we first sort the dice and then check it against the hard-coded value.
+Another way to solve this would be to check if `sum(dice) == 20 and len(set(dice)) == 5` (15 in `LITTLE_STRAIGHT`).
+In `CHOICE`, `lambda number : sum(number)` is shortened to just `sum`.
+
+In `FULL_HOUSE`, we create a `set` to remove the duplicates and check the set's length along with the individual counts.
+For `FOUR_OF_A_KIND`, we check if the first and the fourth element are the same or the second and the last element are the same - if so, there are (at least) four of the same number in the array.
+
+This solution is a succinct way to solve the exercise, although some of the one-liners can get a little long and hard to read.
+Additionally, [PEP8][pep8] does not recommend assigning constant or variable names to `lambda` expressions, so it is a better practice to use `def`:
+```python
+def digits(num):
+ return lambda dice: dice.count(num) * num
+
+def YACHT(dice): return 50 if len(set(dice)) == 1 else 0
+ONES = digits(1)
+TWOS = digits(2)
+THREES = digits(3)
+FOURS = digits(4)
+FIVES = digits(5)
+SIXES = digits(6)
+def FULL_HOUSE(dice): return sum(dice) if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3] else 0
+def FOUR_OF_A_KIND(dice): return 4 * sorted(dice)[1] if len(set(dice)) < 3 and dice.count(dice[0]) in (1, 4, 5) else 0
+def LITTLE_STRAIGHT(dice): return 30 if sorted(dice) == [1, 2, 3, 4, 5] else 0
+def BIG_STRAIGHT(dice): return 30 if sorted(dice) == [2, 3, 4, 5, 6] else 0
+CHOICE = sum
+
+def score(dice, category):
+ return category(dice)
+```
+
+As you can see from the examples, the [ternary operator][ternary-operator] (_or ternary form_) is crucial in solving the exercise using one liners.
+As functions are being used, it might be a better strategy to spread the code over multiple lines to improve readability.
+```python
+def YACHT(dice):
+ if dice.count(dice[0]) == len(dice):
+ return 50
+ return 0
+```
+
+[closures]: https://www.programiz.com/python-programming/closure
+[ternary-operator]: https://www.tutorialspoint.com/ternary-operator-in-python
+[lambda]: https://docs.python.org/3/howto/functional.html?highlight=lambda#small-functions-and-the-lambda-expression
+[pep8]: https://peps.python.org/pep-0008/
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/functions/snippet.txt b/exercises/practice/yacht/.approaches/functions/snippet.txt
new file mode 100644
index 00000000000..34d270ad895
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/functions/snippet.txt
@@ -0,0 +1,8 @@
+def digits(num):
+ return lambda dice: dice.count(num) * num
+YACHT = lambda x: 50 if x.count(x[0]) == len(x) else 0
+ONES = digits(1)
+FULL_HOUSE = lambda x: sum(x) if len(set(x)) == 2 and x.count(x[0]) in [2, 3] else 0
+LITTLE_STRAIGHT = lambda x: 30 if sorted(x) == [1, 2, 3, 4, 5] else 0
+def score(dice, category):
+ return category(dice)
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/if-structure/content.md b/exercises/practice/yacht/.approaches/if-structure/content.md
new file mode 100644
index 00000000000..581f31d1928
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/if-structure/content.md
@@ -0,0 +1,50 @@
+# If structure
+
+The constants here can be set to random, null, or numeric values, and an `if` structure inside the `score` function can determine the code to be executed.
+
+As one-liners aren't necessary here, we can spread out the code to make it look neater:
+```python
+ONES = 1
+TWOS = 2
+THREES = 3
+FOURS = 4
+FIVES = 5
+SIXES = 6
+FULL_HOUSE = 'FULL_HOUSE'
+FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
+LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
+BIG_STRAIGHT = 'BIG_STRAIGHT'
+CHOICE = 'CHOICE'
+YACHT = 'YACHT'
+
+def score(dice, category):
+ if category in (1,2,3,4,5,6):
+ return dice.count(category) * category
+ elif category == 'FULL_HOUSE':
+ if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
+ return sum(dice) or 0
+ elif category == 'FOUR_OF_A_KIND':
+ if dice[0] == dice[3] or dice[1] == dice[4]:
+ return dice[1] * 4 or 0
+ elif category == 'LITTLE_STRAIGHT':
+ if sorted(dice) == [1, 2, 3, 4, 5]:
+ return 30 or 0
+ elif category == 'BIG_STRAIGHT':
+ if sorted(dice) == [2, 3, 4, 5, 6]:
+ return 30 or 0
+ elif category == 'YACHT':
+ if all(num == dice[0] for num in dice):
+ return 50
+ elif category == 'CHOICE':
+ return sum(dice)
+ return 0
+```
+Note that the code inside the `if` statements themselves can differ, but the key idea here is to use `if` and `elif` to branch out the code, and return `0` at the end if nothing else has been returned.
+The `if` condition itself can be different, with people commonly checking if `category == ONES` as opposed to `category == 'ONES'` (or whatever the dummy value is).
+
+This may not be an ideal way to solve the exercise, as the code is rather long and convoluted.
+However, it is a valid (_and fast_) solution.
+Using [structural pattern matching][structural pattern matching], introduced in Python 3.10, could shorten and clarify the code in this situation.
+Pulling some logic out of the `score` function and into additional "helper" functions could also help.
+
+[structural pattern matching]: https://peps.python.org/pep-0636/
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/if-structure/snippet.txt b/exercises/practice/yacht/.approaches/if-structure/snippet.txt
new file mode 100644
index 00000000000..fb590cc155b
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/if-structure/snippet.txt
@@ -0,0 +1,8 @@
+ONES = 1
+YACHT = 'YACHT'
+def score(dice, category):
+ if category == 'ONES':
+ ...
+ elif category == 'FULL_HOUSE':
+ ...
+ return 0
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/introduction.md b/exercises/practice/yacht/.approaches/introduction.md
new file mode 100644
index 00000000000..9717760637d
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/introduction.md
@@ -0,0 +1,77 @@
+# Introduction
+Yacht in Python can be solved in many ways. The most intuitive approach is to use an `if` structure.
+Alternatively, you can create functions and set their names to the constant names.
+
+## General guidance
+The main thing in this exercise is to map a category (_here defined as constants in the stub file_) to a function or a standalone piece of code.
+While mapping generally reminds us of dictionaries, here the constants are global.
+This indicates that the most idiomatic approach is not using a `dict`.
+Adhering to the principles of DRY is important - don't repeat yourself if you can help it, especially in the `ONES` through `SIXES` categories!
+
+## Approach: functions
+Each bit of functionality for each category can be encoded in a function, and the constant name set to that function.
+This can be done by assigning the constant name to a `lambda` or creating a one-line function using the constant as a function name.
+```python
+```python
+def digits(num):
+ return lambda dice: dice.count(num) * num
+YACHT = lambda dice: 50 if dice.count(dice[0]) == len(dice) else 0
+ONES = digits(1)
+TWOS = digits(2)
+THREES = digits(3)
+FOURS = digits(4)
+FIVES = digits(5)
+SIXES = digits(6)
+FULL_HOUSE = lambda dice: sum(dice) if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3] else 0
+FOUR_OF_A_KIND = lambda dice: 4 * dice[1] if dice[0] == dice[3] or dice[1] == dice[4] else 0
+LITTLE_STRAIGHT = lambda dice: 30 if sorted(dice) == [1, 2, 3, 4, 5] else 0
+BIG_STRAIGHT = lambda dice: 30 if sorted(dice) == [2, 3, 4, 5, 6] else 0
+CHOICE = sum
+def score(dice, category):
+ return category(dice)
+```
+This is a very succinct way to solve the exercise, although some one-liners get a little long.
+For more information on this approach, read [this document][approach-functions].
+
+## Approach: if structure
+The constants can be set to random, null, or numeric values, and an `if` structure inside `score` determines the code to be executed.
+As one-liners aren't necessary here, we can spread out the code to make it look neater:
+```python
+ONES = 1
+TWOS = 2
+THREES = 3
+FOURS = 4
+FIVES = 5
+SIXES = 6
+FULL_HOUSE = 'FULL_HOUSE'
+FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
+LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
+BIG_STRAIGHT = 'BIG_STRAIGHT'
+CHOICE = 'CHOICE'
+YACHT = 'YACHT'
+def score(dice, category):
+ if category in (1,2,3,4,5,6):
+ return dice.count(category) * category
+ elif category == 'FULL_HOUSE':
+ if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
+ return sum(dice) or 0
+ elif category == 'FOUR_OF_A_KIND':
+ if dice[0] == dice[3] or dice[1] == dice[4]:
+ return dice[1] * 4 or 0
+ elif category == 'LITTLE_STRAIGHT':
+ if sorted(dice) == [1, 2, 3, 4, 5]:
+ return 30 or 0
+ elif category == 'BIG_STRAIGHT':
+ if sorted(dice) == [2, 3, 4, 5, 6]:
+ return 30 or 0
+ elif category == 'YACHT':
+ if all(num == dice[0] for num in dice):
+ return 50
+ elif category == 'CHOICE':
+ return sum(dice)
+ return 0
+```
+Read more on this approach [here][approach-if-structure].
+
+[approach-functions]: https://exercism.org/tracks/python/exercises/yacht/approaches/functions
+[approach-if-structure]: https://exercism.org/tracks/python/exercises/yacht/approaches/if-structure
diff --git a/exercises/practice/yacht/.approaches/structural-pattern-matching/content.md b/exercises/practice/yacht/.approaches/structural-pattern-matching/content.md
new file mode 100644
index 00000000000..b49bb6340bd
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/structural-pattern-matching/content.md
@@ -0,0 +1,55 @@
+# Structural Pattern Matching
+
+Another very interesting approach is to use [structural pattern matching][structural pattern matching].
+Existing in Python since 3.10, this feature allows for neater code than traditional if structures.
+
+By and large, we reuse the code from the [if structure approach][approach-if-structure].
+We set the constants to random values and check for them in the `match` structure.
+`category` is the "subject", and in every other line, we check it against a "pattern".
+```python
+ONES = 1
+TWOS = 2
+THREES = 3
+FOURS = 4
+FIVES = 5
+SIXES = 6
+FULL_HOUSE = 'FULL_HOUSE'
+FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
+LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
+BIG_STRAIGHT = 'BIG_STRAIGHT'
+CHOICE = 'CHOICE'
+YACHT = 'YACHT'
+
+def score(dice, category):
+ match category:
+ case 1 | 2 | 3 | 4 | 5 | 6:
+ return dice.count(category) * category
+ case 'FULL_HOUSE' if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
+ return sum(dice)
+ case 'FOUR_OF_A_KIND' if dice[0] == dice[3] or dice[1] == dice[4]:
+ return dice[1] * 4
+ case 'LITTLE_STRAIGHT' if sorted(dice) == [1, 2, 3, 4, 5]:
+ return 30
+ case 'BIG_STRAIGHT' if sorted(dice) == [2, 3, 4, 5, 6]:
+ return 30
+ case 'YACHT' if all(num == dice[0] for num in dice):
+ return 50
+ case 'CHOICE':
+ return sum(dice)
+ case _:
+ return 0
+```
+For the first pattern, we utilize "or patterns", using the `|` operator.
+This checks whether the subject is any of the provided patterns.
+
+In the next five patterns, we check an additional condition along with the pattern matching.
+Finally, we use the wildcard operator `_` to match anything.
+As the compiler checks the patterns (`case`s) in order, `return 0` will be executed if none of the other patterns match.
+
+Note that the conditions might differ, but the patterns must have hard coded values - that is, you can't say `case ONES ...` instead of `case 1 ...`.
+This will capture the category and lead to unexpected behavior.
+
+This code is much clenaer than the corresponding `if` structure code.
+
+[structural pattern matching]: https://peps.python.org/pep-0636/
+[approach-if-structure]: https://exercism.org/tracks/python/exercises/yacht/approaches/if-structure
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/structural-pattern-matching/snippet.txt b/exercises/practice/yacht/.approaches/structural-pattern-matching/snippet.txt
new file mode 100644
index 00000000000..4ed99824d8e
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/structural-pattern-matching/snippet.txt
@@ -0,0 +1,8 @@
+ONES = 1
+YACHT = 'YACHT'
+def score(dice, category):
+ match category:
+ case 1 | 2 | 3 | 4 | 5 | 6:
+ return dice.count(category) * category
+ case _:
+ return 0
\ No newline at end of file
From c2e991afa42befbd1bf96abb1babbeddd93fa7b9 Mon Sep 17 00:00:00 2001
From: Safwan Samsudeen <62411302+safwansamsudeen@users.noreply.github.com>
Date: Mon, 5 Jun 2023 17:57:00 +0530
Subject: [PATCH 051/126] remove duplicate line
---
exercises/practice/yacht/.approaches/introduction.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/exercises/practice/yacht/.approaches/introduction.md b/exercises/practice/yacht/.approaches/introduction.md
index 9717760637d..5a37d16881b 100644
--- a/exercises/practice/yacht/.approaches/introduction.md
+++ b/exercises/practice/yacht/.approaches/introduction.md
@@ -12,7 +12,6 @@ Adhering to the principles of DRY is important - don't repeat yourself if you ca
Each bit of functionality for each category can be encoded in a function, and the constant name set to that function.
This can be done by assigning the constant name to a `lambda` or creating a one-line function using the constant as a function name.
```python
-```python
def digits(num):
return lambda dice: dice.count(num) * num
YACHT = lambda dice: 50 if dice.count(dice[0]) == len(dice) else 0
From cc3d9f2b21e739e45c2104a0f9a7e31061e44245 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 21:33:09 +0100
Subject: [PATCH 052/126] Started
---
.../binary-octal-hexdecimal/.meta/config.json | 4 +
concepts/binary-octal-hexdecimal/about.md | 252 ++++++++++++++++++
.../binary-octal-hexdecimal/introduction.md | 9 +
concepts/binary-octal-hexdecimal/links.json | 10 +
config.json | 5 +
5 files changed, 280 insertions(+)
create mode 100644 concepts/binary-octal-hexdecimal/.meta/config.json
create mode 100644 concepts/binary-octal-hexdecimal/about.md
create mode 100644 concepts/binary-octal-hexdecimal/introduction.md
create mode 100644 concepts/binary-octal-hexdecimal/links.json
diff --git a/concepts/binary-octal-hexdecimal/.meta/config.json b/concepts/binary-octal-hexdecimal/.meta/config.json
new file mode 100644
index 00000000000..80a508e68bd
--- /dev/null
+++ b/concepts/binary-octal-hexdecimal/.meta/config.json
@@ -0,0 +1,4 @@
+{
+ "blurb": "Other numerical system in python, binary (0b11), octal (0o71), and hex (0xFF)",
+ "authors": ["BethanyG", "meatball133"]
+}
diff --git a/concepts/binary-octal-hexdecimal/about.md b/concepts/binary-octal-hexdecimal/about.md
new file mode 100644
index 00000000000..87c168c2cb3
--- /dev/null
+++ b/concepts/binary-octal-hexdecimal/about.md
@@ -0,0 +1,252 @@
+# binary, otal, hexdecimal
+
+Binary, octal, and hexdecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8 and hexdecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexdecimal are all a subset of integers.
+Which means that they can only represent whole numbers and support all the operations that we can do with integers.
+
+## Binary
+
+[Binary][binary] is a base 2 numeral system.
+The most common numeral system is base 10.
+So the digits are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
+Binary is base 2, so the digits are 0 and 1.
+It is used to represent the on and off states of a computer.
+Binary can create all the numbers that we use in base 10.
+
+A snipet from the base 2 system looks like this, although it contuines infinitely and doesn't stop at 128:
+
+| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 2 \*\* 7 | 2 \*\* 6 | 2 \*\* 5 | 2 \*\* 4 | 2 \*\* 3 | 2 \*\* 2 | 2 \*\* 1 | 2 \*\* 0 |
+
+So if we want to represent the number 6, it would in binary be: 110
+
+| Place value | 4 | 2 | 1 |
+| ------------- | --- | --- | --- |
+| Binary number | 1 | 1 | 0 |
+
+And the operation would be: `4 + 2 + 0 = 6`
+
+Another example: 19
+
+| Place value | 16 | 8 | 4 | 2 | 1 |
+| ------------- | --- | --- | --- | --- | --- |
+| Binary number | 1 | 0 | 0 | 1 | 1 |
+
+The binary number would be: 10011
+And the operation would be: `16 + 0 + 0 + 2 + 1 = 19`
+
+## Binary in Python
+
+In Python, we can represent binary numbers using the `0b` prefix.
+If we write `0b10011`, Python will interpret it as a binary number and convert it to base 10.
+
+```python
+# 0b10011
+>>> 0b10011
+19
+
+>>> type(0b10011)
+
+```
+
+Binary in python is a subset of integers, therefore it will act like an integer.
+
+If you give have a number which is not in the binary system, it will raise a `SyntaxError`.
+
+```python
+Traceback (most recent call last):
+ File "c:\binary.py", line 1, in
+ 0b10211
+SyntaxError: invalid digit '2' in binary literal
+```
+
+### Operations with binary numbers
+
+Which means that we can do all the operations that we can do with integers.
+
+```python
+# addition
+>>> 0b10011 + 0b10011
+38
+
+# multiplication
+>>> 0b10011 * 0b10011
+361
+```
+
+We can do operations with integers and binary numbers.
+
+```python
+# 0b10011
+>>> 0b10011 + 19
+38
+```
+
+### Representing binary numbers
+
+Since python will automaticly convert binary to int, do we have to use the `bin()` function.
+If we want to represent a binary number.
+`bin()` will return a string with the prefix `0b`.
+
+```python
+# 0b10011
+>>> bin(0b10011)
+'0b10011'
+```
+
+To convert a binary number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
+
+```python
+# 19
+>>> int("0b10011", 2)
+```
+
+Giving the wrong base will raise a `ValueError`.
+
+```python
+Traceback (most recent call last):
+ File "c:\Users\carlh\fwfa.py", line 4, in
+ int("0b10011", 3)
+ValueError: invalid literal for int() with base 3: '0b10011'
+```
+
+### Convering int to binary
+
+We can also convert an integer to binary using the `bin()` function.
+
+```python
+# 0b10011
+>>> bin(19)
+'0b10011'
+```
+
+### Binary methods
+
+There are also [methods][numeral-systems] that we can use on binary numbers.
+
+#### `.bit_length()`
+
+`.bit_length()` will return the number of bits that are needed to represent the number.
+So for example `0b10011` will return 5.
+
+```python
+>>> 0b11011.bit_length()
+5
+```
+
+#### `.count()`
+
+```exercism/note
+`.count()` requires Python 3.10+.
+If you are using the online editor then you don't need to worry about this.
+```
+
+`.bit_count()` will return the number of ones in the binary number.
+So for example `bit_count` will return 3.
+
+```python
+>>> 0b11011.bit_count()
+4
+```
+
+## Octal
+
+[Octal][octal] is a base 8 numeral system.
+Meaning that the digits are 0, 1, 2, 3, 4, 5, 6, 7.
+
+In python, we can represent octal numbers using the `0o` prefix.
+As with binary, python will automaticly convert octal to int.
+
+```python
+# 0o123
+>>> 0o123
+83
+```
+
+As with binary you can do all the operations that you can do with integers and giving a number which is not in the octal system will raise a `SyntaxError`.
+
+### Representing octal numbers
+
+To represent an octal number, we can use the `oct()` function.
+
+```python
+# 0o123
+>>> oct(0o123)
+'0o123'
+```
+
+To convert an octal number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
+
+```python
+# 83
+>>> int("0o123", 8)
+83
+```
+
+As with binary, giving the wrong base will raise a `ValueError`.
+
+### Convering int to octal
+
+We can also convert an integer to binary using the `oct()` function.
+
+```python
+# 0o123
+>>> oct(83)
+'0o123'
+```
+
+### hexdecimal
+
+[Hexdecimal][hexdecimal] is a base 16 numeral system.
+Meaning that the digits are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.
+A is 10, B is 11, C is 12, D is 13, E is 14, F is 15.
+
+In python, we can represent hexdecimal numbers using the `0x` prefix.
+As with binary and octal, python will automaticly convert hexdecimal to int.
+
+```python
+# 0x123
+>>> 0x123
+291
+```
+
+As with binary and octal you can do all the operations that you can do with integers and giving a number which is not in the hex system will raise a `SyntaxError`.
+
+### Representing hexdecimal numbers
+
+To represent an hexdecimal number, we can use the `hex()` function.
+
+```python
+# 0x123
+>>> hex(0x123)
+'0x123'
+```
+
+To convert an hexdecimal number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
+
+```python
+# 291
+>>> int("0x123", 16)
+291
+```
+
+As with binary and octal, giving the wrong base will raise a `ValueError`.
+
+### Convering int to hexdecimal
+
+We can also convert an integer to binary using the `hex()` function.
+
+```python
+# 0x123
+>>> hex(291)
+'0x123'
+```
+
+[binary]: https://en.wikipedia.org/wiki/Binary_number
+[octal]: https://en.wikipedia.org/wiki/Octal
+[hexdecimal]: https://en.wikipedia.org/wiki/Hexadecimal
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
+[methods-int]: https://docs.python.org/3/library/stdtypes.html#additional-methods-on-integer-types
diff --git a/concepts/binary-octal-hexdecimal/introduction.md b/concepts/binary-octal-hexdecimal/introduction.md
new file mode 100644
index 00000000000..0dba132a24b
--- /dev/null
+++ b/concepts/binary-octal-hexdecimal/introduction.md
@@ -0,0 +1,9 @@
+# binary, otal, hexdecimal
+
+Binary, octal, and hexdecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8 and hexdecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexdecimal are all a subset of integers.
+Which means that they can only represent whole numbers and support all the operations that we can do with integers.
+
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
diff --git a/concepts/binary-octal-hexdecimal/links.json b/concepts/binary-octal-hexdecimal/links.json
new file mode 100644
index 00000000000..1e7d3acd599
--- /dev/null
+++ b/concepts/binary-octal-hexdecimal/links.json
@@ -0,0 +1,10 @@
+[
+ {
+ "url": "https://towardsdatascience.com/binary-hex-and-octal-in-python-20222488cee1",
+ "description": "Binary, octal, hex in python"
+ },
+ {
+ "url": "https://en.wikipedia.org/wiki/Numeral_system",
+ "description": "Numeral system"
+ }
+]
\ No newline at end of file
diff --git a/config.json b/config.json
index 038123f69e5..17be12403a1 100644
--- a/config.json
+++ b/config.json
@@ -2235,6 +2235,11 @@
"slug": "binary-data",
"name": "Binary Data"
},
+ {
+ "uuid": "78dbf248-a1e5-48cb-ba53-def4d92bf2a8",
+ "slug": "binary-octal-hexdecimal",
+ "name": "Binary, Octal, and Hexdecimal"
+ },
{
"uuid": "f8e96e60-f746-4c7a-8dc9-df6b77eefdfa",
"slug": "bitflags",
From b0ce921e5b57bad08b543b1da658dfbec956713f Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 21:34:39 +0100
Subject: [PATCH 053/126] fix
---
concepts/binary-octal-hexdecimal/links.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/binary-octal-hexdecimal/links.json b/concepts/binary-octal-hexdecimal/links.json
index 1e7d3acd599..8826182cd48 100644
--- a/concepts/binary-octal-hexdecimal/links.json
+++ b/concepts/binary-octal-hexdecimal/links.json
@@ -7,4 +7,4 @@
"url": "https://en.wikipedia.org/wiki/Numeral_system",
"description": "Numeral system"
}
-]
\ No newline at end of file
+]
From 5632f6e89f5fe53836b8c5f94258f1c12b79f531 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 12:07:17 +0100
Subject: [PATCH 054/126] Fixes
---
concepts/binary-octal-hexdecimal/about.md | 64 ++++++++++-------------
1 file changed, 28 insertions(+), 36 deletions(-)
diff --git a/concepts/binary-octal-hexdecimal/about.md b/concepts/binary-octal-hexdecimal/about.md
index 87c168c2cb3..e3052de9cd2 100644
--- a/concepts/binary-octal-hexdecimal/about.md
+++ b/concepts/binary-octal-hexdecimal/about.md
@@ -1,21 +1,21 @@
-# binary, otal, hexdecimal
+# binary, octal, hexadecimal
-Binary, octal, and hexdecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
-Binary is base 2, octal is base 8 and hexdecimal is base 16.
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8 and hexadecimal is base 16.
Normal integers are base 10 in python.
-Binary, octal, and hexdecimal are all a subset of integers.
+Binary, octal, and hexadecimal are all a subset of integers.
Which means that they can only represent whole numbers and support all the operations that we can do with integers.
## Binary
[Binary][binary] is a base 2 numeral system.
The most common numeral system is base 10.
-So the digits are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
+In the base 10 numeral system so are the digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
Binary is base 2, so the digits are 0 and 1.
It is used to represent the on and off states of a computer.
Binary can create all the numbers that we use in base 10.
-A snipet from the base 2 system looks like this, although it contuines infinitely and doesn't stop at 128:
+A snippet from the base 2 system looks like this, although it continues infinitely and doesn't stop at 128:
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
@@ -54,7 +54,7 @@ If we write `0b10011`, Python will interpret it as a binary number and convert i
Binary in python is a subset of integers, therefore it will act like an integer.
-If you give have a number which is not in the binary system, it will raise a `SyntaxError`.
+If you have a number which is not in the binary system, it will raise a `SyntaxError`.
```python
Traceback (most recent call last):
@@ -65,7 +65,7 @@ SyntaxError: invalid digit '2' in binary literal
### Operations with binary numbers
-Which means that we can do all the operations that we can do with integers.
+Since binary is a subset of integers, we can do all the operations that we can do with integers.
```python
# addition
@@ -77,22 +77,20 @@ Which means that we can do all the operations that we can do with integers.
361
```
-We can do operations with integers and binary numbers.
+We can do also have operations with both integers and binary numbers.
```python
-# 0b10011
>>> 0b10011 + 19
38
```
### Representing binary numbers
-Since python will automaticly convert binary to int, do we have to use the `bin()` function.
+Since python will automatically convert binary to `int`, do we have to use the `bin()` function.
If we want to represent a binary number.
-`bin()` will return a string with the prefix `0b`.
+`bin()` will return a `string` with the prefix `0b`.
```python
-# 0b10011
>>> bin(0b10011)
'0b10011'
```
@@ -100,11 +98,11 @@ If we want to represent a binary number.
To convert a binary number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
```python
-# 19
>>> int("0b10011", 2)
+19
```
-Giving the wrong base will raise a `ValueError`.
+Giving the wrong base will raise a `ValueError`:
```python
Traceback (most recent call last):
@@ -113,7 +111,7 @@ Traceback (most recent call last):
ValueError: invalid literal for int() with base 3: '0b10011'
```
-### Convering int to binary
+### Converting int to binary
We can also convert an integer to binary using the `bin()` function.
@@ -144,7 +142,7 @@ So for example `0b10011` will return 5.
If you are using the online editor then you don't need to worry about this.
```
-`.bit_count()` will return the number of ones in the binary number.
+`.bit_count()` will return the number of **ones** in the binary number.
So for example `bit_count` will return 3.
```python
@@ -155,10 +153,10 @@ So for example `bit_count` will return 3.
## Octal
[Octal][octal] is a base 8 numeral system.
-Meaning that the digits are 0, 1, 2, 3, 4, 5, 6, 7.
+Meaning that the digits are: 0, 1, 2, 3, 4, 5, 6, 7.
In python, we can represent octal numbers using the `0o` prefix.
-As with binary, python will automaticly convert octal to int.
+As with binary, python will automatically convert octal to int.
```python
# 0o123
@@ -173,7 +171,6 @@ As with binary you can do all the operations that you can do with integers and g
To represent an octal number, we can use the `oct()` function.
```python
-# 0o123
>>> oct(0o123)
'0o123'
```
@@ -181,72 +178,67 @@ To represent an octal number, we can use the `oct()` function.
To convert an octal number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
```python
-# 83
>>> int("0o123", 8)
83
```
As with binary, giving the wrong base will raise a `ValueError`.
-### Convering int to octal
+### Converting int to octal
We can also convert an integer to binary using the `oct()` function.
```python
-# 0o123
>>> oct(83)
'0o123'
```
-### hexdecimal
+### hexadecimal
-[Hexdecimal][hexdecimal] is a base 16 numeral system.
+[Hexadecimal][hexadecimal] is a base 16 numeral system.
Meaning that the digits are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.
A is 10, B is 11, C is 12, D is 13, E is 14, F is 15.
-In python, we can represent hexdecimal numbers using the `0x` prefix.
-As with binary and octal, python will automaticly convert hexdecimal to int.
+In python, we can represent hexadecimal numbers using the `0x` prefix.
+As with binary and octal, python will automatically convert hexadecimal to int.
```python
-# 0x123
+# 0o123
>>> 0x123
291
```
As with binary and octal you can do all the operations that you can do with integers and giving a number which is not in the hex system will raise a `SyntaxError`.
-### Representing hexdecimal numbers
+### Representing hexadecimal numbers
-To represent an hexdecimal number, we can use the `hex()` function.
+To represent an hexadecimal number, we can use the `hex()` function.
```python
-# 0x123
>>> hex(0x123)
'0x123'
```
-To convert an hexdecimal number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
+To convert an hexadecimal number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
```python
-# 291
>>> int("0x123", 16)
291
```
As with binary and octal, giving the wrong base will raise a `ValueError`.
-### Convering int to hexdecimal
+### Converting int to hexadecimal
We can also convert an integer to binary using the `hex()` function.
```python
-# 0x123
>>> hex(291)
'0x123'
```
[binary]: https://en.wikipedia.org/wiki/Binary_number
[octal]: https://en.wikipedia.org/wiki/Octal
-[hexdecimal]: https://en.wikipedia.org/wiki/Hexadecimal
+[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
[methods-int]: https://docs.python.org/3/library/stdtypes.html#additional-methods-on-integer-types
From df09f10f6ada4127530491db47ea5dd7ceaec03c Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 15:46:05 +0100
Subject: [PATCH 055/126] fixes
---
.../.meta/config.json | 0
.../about.md | 4 ++--
concepts/binary-octal-hexadecimal/introduction.md | 9 +++++++++
.../links.json | 0
concepts/binary-octal-hexdecimal/introduction.md | 9 ---------
config.json | 4 ++--
6 files changed, 13 insertions(+), 13 deletions(-)
rename concepts/{binary-octal-hexdecimal => binary-octal-hexadecimal}/.meta/config.json (100%)
rename concepts/{binary-octal-hexdecimal => binary-octal-hexadecimal}/about.md (98%)
create mode 100644 concepts/binary-octal-hexadecimal/introduction.md
rename concepts/{binary-octal-hexdecimal => binary-octal-hexadecimal}/links.json (100%)
delete mode 100644 concepts/binary-octal-hexdecimal/introduction.md
diff --git a/concepts/binary-octal-hexdecimal/.meta/config.json b/concepts/binary-octal-hexadecimal/.meta/config.json
similarity index 100%
rename from concepts/binary-octal-hexdecimal/.meta/config.json
rename to concepts/binary-octal-hexadecimal/.meta/config.json
diff --git a/concepts/binary-octal-hexdecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
similarity index 98%
rename from concepts/binary-octal-hexdecimal/about.md
rename to concepts/binary-octal-hexadecimal/about.md
index e3052de9cd2..43ddc905c8a 100644
--- a/concepts/binary-octal-hexdecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -1,7 +1,7 @@
# binary, octal, hexadecimal
Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
-Binary is base 2, octal is base 8 and hexadecimal is base 16.
+Binary is base 2, octal is base 8, and hexadecimal is base 16.
Normal integers are base 10 in python.
Binary, octal, and hexadecimal are all a subset of integers.
Which means that they can only represent whole numbers and support all the operations that we can do with integers.
@@ -106,7 +106,7 @@ Giving the wrong base will raise a `ValueError`:
```python
Traceback (most recent call last):
- File "c:\Users\carlh\fwfa.py", line 4, in
+ File "c:\binary.py", line 4, in
int("0b10011", 3)
ValueError: invalid literal for int() with base 3: '0b10011'
```
diff --git a/concepts/binary-octal-hexadecimal/introduction.md b/concepts/binary-octal-hexadecimal/introduction.md
new file mode 100644
index 00000000000..7dc63b8dcdd
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/introduction.md
@@ -0,0 +1,9 @@
+# binary, octal, hexadecimal
+
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8, and hexadecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexadecimal are all a subset of integers.
+Which means that they can only represent whole numbers and support all the operations that we can do with integers.
+
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
diff --git a/concepts/binary-octal-hexdecimal/links.json b/concepts/binary-octal-hexadecimal/links.json
similarity index 100%
rename from concepts/binary-octal-hexdecimal/links.json
rename to concepts/binary-octal-hexadecimal/links.json
diff --git a/concepts/binary-octal-hexdecimal/introduction.md b/concepts/binary-octal-hexdecimal/introduction.md
deleted file mode 100644
index 0dba132a24b..00000000000
--- a/concepts/binary-octal-hexdecimal/introduction.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# binary, otal, hexdecimal
-
-Binary, octal, and hexdecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
-Binary is base 2, octal is base 8 and hexdecimal is base 16.
-Normal integers are base 10 in python.
-Binary, octal, and hexdecimal are all a subset of integers.
-Which means that they can only represent whole numbers and support all the operations that we can do with integers.
-
-[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
diff --git a/config.json b/config.json
index 17be12403a1..3e292d7440b 100644
--- a/config.json
+++ b/config.json
@@ -2237,8 +2237,8 @@
},
{
"uuid": "78dbf248-a1e5-48cb-ba53-def4d92bf2a8",
- "slug": "binary-octal-hexdecimal",
- "name": "Binary, Octal, and Hexdecimal"
+ "slug": "binary-octal-hexadecimal",
+ "name": "Binary, Octal, and Hexadecimal"
},
{
"uuid": "f8e96e60-f746-4c7a-8dc9-df6b77eefdfa",
From 236eeacad93def73382daff104583f02d8267999 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 22:20:51 +0100
Subject: [PATCH 056/126] fixes
---
concepts/binary-octal-hexadecimal/about.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
index 43ddc905c8a..fa6d6a6a922 100644
--- a/concepts/binary-octal-hexadecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -95,7 +95,7 @@ If we want to represent a binary number.
'0b10011'
```
-To convert a binary number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
+To convert a binary number to an integer, we can use the `int()` function, and pass a string with a binary and the base as arguments.
```python
>>> int("0b10011", 2)
@@ -175,7 +175,7 @@ To represent an octal number, we can use the `oct()` function.
'0o123'
```
-To convert an octal number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
+To convert an octal number to an integer, we can use the `int()` function, pass a string with a octal and the base as arguments.
```python
>>> int("0o123", 8)
@@ -219,7 +219,7 @@ To represent an hexadecimal number, we can use the `hex()` function.
'0x123'
```
-To convert an hexadecimal number to an integer, we can use the `int()` function, and pass the string and the base as arguments.
+To convert an hexadecimal number to an integer, we can use the `int()` function, pass a string with a hexadecimal and the base as arguments.
```python
>>> int("0x123", 16)
@@ -238,7 +238,7 @@ We can also convert an integer to binary using the `hex()` function.
```
[binary]: https://en.wikipedia.org/wiki/Binary_number
-[octal]: https://en.wikipedia.org/wiki/Octal
[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
-[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
[methods-int]: https://docs.python.org/3/library/stdtypes.html#additional-methods-on-integer-types
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
+[octal]: https://en.wikipedia.org/wiki/Octal
From 86c2a5a4efd844e299104f4055949b05143931e9 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 5 Jun 2023 17:22:00 -0700
Subject: [PATCH 057/126] Apply suggestions from code review
@meatball - as you can see, I did a tad bit of violence to this. If you have time/energy, it would be great if you could give it a re-read to make sure that its still OK. _Many thanks!_
---
.../.meta/config.json | 2 +-
concepts/binary-octal-hexadecimal/about.md | 148 ++++++++----------
.../binary-octal-hexadecimal/introduction.md | 6 +-
3 files changed, 69 insertions(+), 87 deletions(-)
diff --git a/concepts/binary-octal-hexadecimal/.meta/config.json b/concepts/binary-octal-hexadecimal/.meta/config.json
index 80a508e68bd..6e2a15b607a 100644
--- a/concepts/binary-octal-hexadecimal/.meta/config.json
+++ b/concepts/binary-octal-hexadecimal/.meta/config.json
@@ -1,4 +1,4 @@
{
- "blurb": "Other numerical system in python, binary (0b11), octal (0o71), and hex (0xFF)",
+ "blurb": "Other numerical systems in Python: binary (0b11), octal (0o71), and hex (0xFF)",
"authors": ["BethanyG", "meatball133"]
}
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
index fa6d6a6a922..1106e43803c 100644
--- a/concepts/binary-octal-hexadecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -1,19 +1,16 @@
-# binary, octal, hexadecimal
+# Binary, Octal, and Hexadecimal
Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
Binary is base 2, octal is base 8, and hexadecimal is base 16.
Normal integers are base 10 in python.
-Binary, octal, and hexadecimal are all a subset of integers.
-Which means that they can only represent whole numbers and support all the operations that we can do with integers.
+Binary, octal, and hexadecimal are all representations of integers.
+Which means that they represent positive and negative numbers (_including zero_) without fractions or decimals, and support all the operations that we can do with integers.
## Binary
-[Binary][binary] is a base 2 numeral system.
-The most common numeral system is base 10.
-In the base 10 numeral system so are the digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
-Binary is base 2, so the digits are 0 and 1.
-It is used to represent the on and off states of a computer.
-Binary can create all the numbers that we use in base 10.
+[Binary][binary] is a base 2 numeral system, using only the digits 0 and 1.
+It commonly represents the 0 ("off") and 1 ("on") states of electrical flow through transistors and switches in computers, as well as the positive and negative charges in magnetic storage media.
+Binary can represent all the integers that are used in base 10.
A snippet from the base 2 system looks like this, although it continues infinitely and doesn't stop at 128:
@@ -40,7 +37,7 @@ And the operation would be: `16 + 0 + 0 + 2 + 1 = 19`
## Binary in Python
-In Python, we can represent binary numbers using the `0b` prefix.
+In Python, we can represent binary literals using the `0b` prefix.
If we write `0b10011`, Python will interpret it as a binary number and convert it to base 10.
```python
@@ -52,9 +49,9 @@ If we write `0b10011`, Python will interpret it as a binary number and convert i
```
-Binary in python is a subset of integers, therefore it will act like an integer.
+Binary in Python is just a different way of writing an integer and so the binary representation **is an integer** for all mathematical operations.
-If you have a number which is not in the binary system, it will raise a `SyntaxError`.
+If you write a number with a `0b` prefix that is not in the binary system, it will raise a `SyntaxError`.
```python
Traceback (most recent call last):
@@ -63,9 +60,9 @@ Traceback (most recent call last):
SyntaxError: invalid digit '2' in binary literal
```
-### Operations with binary numbers
+### Operations with Binary Numbers
-Since binary is a subset of integers, we can do all the operations that we can do with integers.
+Since binary numbers are integers, we can perform all operations on them that we can with integers.
```python
# addition
@@ -77,32 +74,38 @@ Since binary is a subset of integers, we can do all the operations that we can d
361
```
-We can do also have operations with both integers and binary numbers.
+We can also perform operations between both binary and integer representations.
+However, the usual mathematical operator rules apply: dividing two binary numbers or integer numbers will return a `float`, even if the division does not result in a decimal portion.
```python
>>> 0b10011 + 19
38
-```
-### Representing binary numbers
+>>> 0b10011/0b10011
+1.0
+
+>>> 0b10011/3
+6.333333333333333
+
+### Converting to and from Binary Representation
-Since python will automatically convert binary to `int`, do we have to use the `bin()` function.
-If we want to represent a binary number.
-`bin()` will return a `string` with the prefix `0b`.
+Python will automatically convert a binary literal into `int`.
+ To convert an `int` into a binary representation, use the built-in [`bin()`][bin] function.
+`bin()` will return a `str` of the binary equivalent with the prefix `0b` .
```python
->>> bin(0b10011)
+>>> bin(19)
'0b10011'
```
-To convert a binary number to an integer, we can use the `int()` function, and pass a string with a binary and the base as arguments.
+To convert a binary literal to an integer, we can use the built-in `int()` function, and pass a string of the binary representation and a base argument:
```python
>>> int("0b10011", 2)
19
```
-Giving the wrong base will raise a `ValueError`:
+Giving the wrong base (_or an invalid binary representation_) will raise a `ValueError`:
```python
Traceback (most recent call last):
@@ -111,52 +114,39 @@ Traceback (most recent call last):
ValueError: invalid literal for int() with base 3: '0b10011'
```
-### Converting int to binary
-
-We can also convert an integer to binary using the `bin()` function.
-
-```python
-# 0b10011
->>> bin(19)
-'0b10011'
-```
-
-### Binary methods
+### Binary Methods
-There are also [methods][numeral-systems] that we can use on binary numbers.
+There are also some special [methods][numeral-systems] that we can use on binary numbers.
-#### `.bit_length()`
-`.bit_length()` will return the number of bits that are needed to represent the number.
-So for example `0b10011` will return 5.
+[`.bit_length()`][bit_length] will return the number of bits that are needed to represent the number:
```python
>>> 0b11011.bit_length()
5
```
-#### `.count()`
-
-```exercism/note
-`.count()` requires Python 3.10+.
-If you are using the online editor then you don't need to worry about this.
-```
-`.bit_count()` will return the number of **ones** in the binary number.
-So for example `bit_count` will return 3.
+[`.bit_count()`][bit_count] will return the number of **ones** in the binary number.
+For example, `bit_count()` on '0b11011' will return 4:
```python
>>> 0b11011.bit_count()
4
-```
+
+~~~~exercism/note
+If you are working locally, `bit_count()` requires at least Python 3.10.
+The Exercism online editor currently supports all features through Python 3.11.
+~~~~
+
## Octal
[Octal][octal] is a base 8 numeral system.
-Meaning that the digits are: 0, 1, 2, 3, 4, 5, 6, 7.
+It uses the digits 0, 1, 2, 3, 4, 5, 6, and 7.
-In python, we can represent octal numbers using the `0o` prefix.
-As with binary, python will automatically convert octal to int.
+In Python, we can represent octal numbers using the `0o` prefix.
+As with binary, Python automatically converts an octal representation to an `int`.
```python
# 0o123
@@ -164,18 +154,20 @@ As with binary, python will automatically convert octal to int.
83
```
-As with binary you can do all the operations that you can do with integers and giving a number which is not in the octal system will raise a `SyntaxError`.
+As with binary, octal numbers **are ints** and support all integer operations.
+Prefixing a number with `0o` that is not in the octal system will raise a `SyntaxError`.
-### Representing octal numbers
+ ### Converting to and from Octal Representation
+
-To represent an octal number, we can use the `oct()` function.
+To convert an `int` into an octal representation, you can use the built-in [`oct()`][oct] function.
+This acts similarly to the `bin()` function, returning a string:
```python
->>> oct(0o123)
+>>> oct(83)
'0o123'
-```
-To convert an octal number to an integer, we can use the `int()` function, pass a string with a octal and the base as arguments.
+To convert an octal number to an integer, we can use the `int()` function, passing an octal string representation and the base (8) as arguments:
```python
>>> int("0o123", 8)
@@ -184,42 +176,36 @@ To convert an octal number to an integer, we can use the `int()` function, pass
As with binary, giving the wrong base will raise a `ValueError`.
-### Converting int to octal
-
-We can also convert an integer to binary using the `oct()` function.
-
-```python
->>> oct(83)
-'0o123'
-```
-
-### hexadecimal
+### Hexadecimal
[Hexadecimal][hexadecimal] is a base 16 numeral system.
-Meaning that the digits are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.
-A is 10, B is 11, C is 12, D is 13, E is 14, F is 15.
+It uses the digits 0 - 9 and the letters A, B, C, D, E, and F.
+A is 10, B is 11, C is 12, D is 13, E is 14, and F is 15.
-In python, we can represent hexadecimal numbers using the `0x` prefix.
-As with binary and octal, python will automatically convert hexadecimal to int.
+We can represent hexadecimal numbers in Python using the `0x` prefix.
+As with binary and octal, Python will automatically convert hexadecimal literals to `int`.
```python
-# 0o123
+# 0x123
>>> 0x123
291
```
-As with binary and octal you can do all the operations that you can do with integers and giving a number which is not in the hex system will raise a `SyntaxError`.
+As with binary and octal - hexidecimal literals **are ints**, and you can perform all integer operations.
+Prefixing a non-hexidecimal number with `0x` will raise a `SyntaxError`.
### Representing hexadecimal numbers
-To represent an hexadecimal number, we can use the `hex()` function.
+### Converting to and from Hexadecimal Representation
+
+To convert an `int` into a hexadecimal representation, you can use the built-in [`hex()`][hex] function.
+This acts similarly to the `bin()` function, returning a string:
```python
->>> hex(0x123)
+>>> hex(291)
'0x123'
-```
-To convert an hexadecimal number to an integer, we can use the `int()` function, pass a string with a hexadecimal and the base as arguments.
+To convert a hexadecimal representation to an integer, we can use the `int()` function, passing a hexadecimal string with the base (16) as arguments:
```python
>>> int("0x123", 16)
@@ -228,16 +214,10 @@ To convert an hexadecimal number to an integer, we can use the `int()` function,
As with binary and octal, giving the wrong base will raise a `ValueError`.
-### Converting int to hexadecimal
-
-We can also convert an integer to binary using the `hex()` function.
-
-```python
->>> hex(291)
-'0x123'
-```
[binary]: https://en.wikipedia.org/wiki/Binary_number
+[bit_count]: https://docs.python.org/3/library/stdtypes.html#int.bit_count
+[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
[methods-int]: https://docs.python.org/3/library/stdtypes.html#additional-methods-on-integer-types
[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
diff --git a/concepts/binary-octal-hexadecimal/introduction.md b/concepts/binary-octal-hexadecimal/introduction.md
index 7dc63b8dcdd..820aac33ee7 100644
--- a/concepts/binary-octal-hexadecimal/introduction.md
+++ b/concepts/binary-octal-hexadecimal/introduction.md
@@ -3,7 +3,9 @@
Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
Binary is base 2, octal is base 8, and hexadecimal is base 16.
Normal integers are base 10 in python.
-Binary, octal, and hexadecimal are all a subset of integers.
-Which means that they can only represent whole numbers and support all the operations that we can do with integers.
+Binary, octal, and hexadecimal literals are all considered `int` subtypes and Python automatically converts between them.
+This means that they can only represent zero, positive, and negative numbers that do not have a fractional or decimal part.
+Binary, octal, and hexidecimal numbers support all integer operations.
+However, division (_as with ints_) will return a `float`.
[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
From 6967d4a35542a38308b380d0bf9ca6a1b2449ef3 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 5 Jun 2023 17:22:29 -0700
Subject: [PATCH 058/126] Update concepts/binary-octal-hexadecimal/about.md
---
concepts/binary-octal-hexadecimal/about.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
index 1106e43803c..89515abe583 100644
--- a/concepts/binary-octal-hexadecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -218,6 +218,8 @@ As with binary and octal, giving the wrong base will raise a `ValueError`.
[binary]: https://en.wikipedia.org/wiki/Binary_number
[bit_count]: https://docs.python.org/3/library/stdtypes.html#int.bit_count
[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
+[bit_count]: https://docs.python.org/3/library/stdtypes.html#int.bit_count
+[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
[methods-int]: https://docs.python.org/3/library/stdtypes.html#additional-methods-on-integer-types
[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
From c997cc7f1e57c4b457f936034f07ff8fdfbc5db7 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 5 Jun 2023 17:22:49 -0700
Subject: [PATCH 059/126] Update concepts/binary-octal-hexadecimal/about.md
---
concepts/binary-octal-hexadecimal/about.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
index 89515abe583..53aa6de104b 100644
--- a/concepts/binary-octal-hexadecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -194,7 +194,6 @@ As with binary and octal, Python will automatically convert hexadecimal literals
As with binary and octal - hexidecimal literals **are ints**, and you can perform all integer operations.
Prefixing a non-hexidecimal number with `0x` will raise a `SyntaxError`.
-### Representing hexadecimal numbers
### Converting to and from Hexadecimal Representation
From 7aef5e9a8940a624f3cd4d604e99955d610058bf Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 6 Jun 2023 10:29:37 -0700
Subject: [PATCH 060/126] Update concepts/binary-octal-hexadecimal/about.md
---
concepts/binary-octal-hexadecimal/about.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
index 53aa6de104b..667f3eb6cd7 100644
--- a/concepts/binary-octal-hexadecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -133,7 +133,6 @@ For example, `bit_count()` on '0b11011' will return 4:
```python
>>> 0b11011.bit_count()
4
-
~~~~exercism/note
If you are working locally, `bit_count()` requires at least Python 3.10.
The Exercism online editor currently supports all features through Python 3.11.
From c93e7ad50f0089f086200d4e343abf36b228164d Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 21 Dec 2022 20:49:38 +0100
Subject: [PATCH 061/126] Start
---
concepts/itertools/about.md | 337 +++++++++++++++++++++++++++++++++-
concepts/itertools/links.json | 4 +-
2 files changed, 338 insertions(+), 3 deletions(-)
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
index c628150d565..82e6c6161a9 100644
--- a/concepts/itertools/about.md
+++ b/concepts/itertools/about.md
@@ -1,2 +1,337 @@
-#TODO: Add about for this concept.
+## Infinite iterators
+```exercism/note
+To avoid infinite loops, you can use `break` to end a loop.
+```
+
+### Count()
+
+`count(start, )` allows to print all values from the start value to infinity.
+Count also has an optional step parameter, which allows to print values with a step size other than 1.
+
+```python
+>>> import itertools
+>>> for number in itertools.count(5, 2):
+... if number > 20:
+... break
+... else:
+... print(number, end=' ')
+...
+5 7 9 11 13 15 17 19
+```
+
+Giving `count()` a negative step size will print values in a descending order.
+
+```python
+>>> import itertools
+>>> for number in itertools.count(5, -2):
+... if number < -20:
+... break
+... else:
+... print(number, end=' ')
+...
+5 3 1 -1 -3 -5 -7 -9 -11 -13 -15 -17 -19
+```
+
+### Cycle()
+
+`cycle(iterable)` allows to print all values from the iterable in an infinte loop.
+For example a `list`, `tuple`, `string` or `dict` can be used as an iterable.
+
+```python
+>>> import itertools
+>>> number = 0
+>>> for letter in itertools.cycle("ABC"):
+... if number == 10:
+... break
+... else:
+... print(letter, end=' ')
+... number += 1
+...
+A B C A B C A B C A
+```
+
+### Repeat()
+
+`repeat(object, )` allows to print the same value in an infinte loop.
+Although if the optional times parameter is given, the value will be printed that many times.
+Meaning that it is not an infinite loop if that parameter is given.
+
+```python
+>>> import itertools
+>>> for number in itertools.repeat(5, 3):
+... print(number, end=' ')
+...
+5 5 5
+```
+
+## Iterators terminating on the shortest input sequence
+
+### Accumulate()
+
+`accumulate(iterable, )` allows to print the accumulated values.
+
+perhaps skip
+
+### Chain()
+
+`chain(iterable1, iterable2...)` creates an irretator of values from the iterables in the order they are given.
+
+```python
+>>> import itertools
+>>> for number in itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]):
+... print(number, end=' ')
+...
+1 2 3 4 5 6 7 8 9
+```
+
+Chain can also be used to concate a different amount of iterables to a list or tuple by using the `list()` or `tuple()` function.
+
+```python
+>>> import itertools
+>>> list(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]))
+[1, 2, 3, 4, 5, 6, 7, 8, 9]
+```
+
+### chain.from_iterable()
+
+Works like chain but takes a single iterable argument and unpack that iterable into individual iterables.
+
+```python
+>>> import itertools
+>>> for number in itertools.chain.from_iterable([[1, 2, 3], [4, 5, 6], [7, 8, 9]]):
+... print(number, end=' ')
+...
+1 2 3 4 5 6 7 8 9
+```
+
+### Compress()
+
+`compress(iterable, selectors)` creates an irretator from the iterable where the corresponding selector is `True`.
+The selector can hold bools but can also hold integers, where 0 is `False` and any other integer is `True`.
+
+```python
+>>> import itertools
+>>> for letter in itertools.compress("Exercism", [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]):
+... print(letter, end=' ')
+...
+E r c s m
+```
+
+### Dropwhile()
+
+skip
+
+### Filterfalse()
+
+skip
+
+### Groupby()
+
+probably skip
+
+### Islice()
+
+`islice(iterable, , , )` creates an irretator of the values from the iterable, from the start index to the stop index with a step size of step.
+
+```python
+>>> import itertools
+>>> for letter in itertools.islice("Exercism", 2, 5, 2):
+... print(letter, end=' ')
+...
+e c
+```
+
+### Pairwise()
+
+```exercism/note
+`Pairwise()` requires Python 3.10+.
+If you are using the online editor then you don't need to worry about this.
+```
+
+`Pairwise(iterable)` was intruduced in Python 3.10 and returns an iterator of overlapping pairs of values from the input iterable.
+
+```python
+>>> import itertools
+>>> for pair in itertools.pairwise("Exercism"):
+... print(pair, end=' ')
+...
+('E', 'x') ('x', 'e') ('e', 'r') ('r', 'c') ('c', 'i') ('i', 's') ('s', 'm')
+```
+
+### Starmap()
+
+Pehaps skip
+
+### Takewhile()
+
+skip
+
+### Tee()
+
+Talk with Bethany about
+
+### Zip_longest()
+
+#### Explaning zip
+
+```exercism/caution
+Zip in computer science(programming) is not the same as zip in computer terms.
+Zip in computer terms reffers to a file format that supports compression.
+While zip in computer science(programming) reffers to the `zip()` function.
+```
+
+`zip()` is a built in function and is not apart of the `itertools` module.
+It takes any number of iterables and returns an iterator of tuples.
+Where the i-th tuple contains the i-th element from each of the argument iterables.
+Meaning the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable and so on.
+
+```python
+>>> zipped = zip(['x', 'y', 'z'], [1, 2, 3], [True, False, True])
+>>> list(zipped)
+[('x', 1, True),('y', 2, False), ('z', 3, True)]
+```
+
+If the iterables are not the same length, then the iterator will stop when the shortest iterable is exhausted.
+
+```python
+>>> zipped = zip(['x', 'y', 'z'], [1, 2, 3, 4], [True, False])
+>>> list(zipped)
+[('x', 1, True),('y', 2, False)]
+```
+
+#### Explaning zip_longest
+
+`zip_longest(iterator, )` is a function from the `itertools` module.
+The difference between `zip_longest()` and `zip()` is that `zip_longest()` will continue until the longest iterable is exhausted.
+If the iterables are not the same length the `fillvalue` will be used to fill in the missing values.
+By the default the `fillvalue` is `None`.
+
+```python
+>>> import itertools
+>>> zipped = itertools.zip_longest(['x', 'y', 'z'], [1, 2, 3, 4], [True, False])
+>>> list(zipped)
+[('x', 1, True),('y', 2, False), ('z', 3, None), (None, 4, None)]
+```
+
+An example where a fillvalue is given:
+
+```python
+>>> import itertools
+>>> zipped = itertools.zip_longest(['x', 'y', 'z'], [1, 2, 3, 4], [True, False], fillvalue='fill')
+>>> list(zipped)
+[('x', 1, True),('y', 2, False), ('z', 3, 'fill'), ('fill', 4, 'fill')]
+```
+
+## Combinatoric iterators
+
+### Product()
+
+`product(iterable1, iterable2..., )` creates an iterator of tuples where the i-th tuple contains the i-th element from each of the argument iterables.
+The repeat keyword argument can be used to specify the number of times the input iterables are repeated.
+By default the repeat keyword argument is 1.
+
+```python
+>>> import itertools
+>>> for product in itertools.product("ABCD", repeat=1):
+... print(product, end=' ')
+...
+('A',) ('B',) ('C',) ('D',)
+```
+
+```python
+>>> import itertools
+>>> for product in itertools.product("ABCD", repeat=2):
+... print(product, end=' ')
+...
+('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'C') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C') ('D', 'D')
+```
+
+The last one here can be seen as doing a nested for loop.
+When you increase the repeat value the number of iterations increases exponentially.
+The example above is a n\*\*2 iteration.
+
+```python
+>>> import itertools
+>>> for letter1 in "ABCD":
+... for letter2 in "ABCD":
+... print((letter1, letter2), end=' ')
+...
+('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'C') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C') ('D', 'D')
+```
+
+You can also give it multiple iterables.
+
+```python
+>>> import itertools
+>>> for product in itertools.product("ABCD", "xy" repeat=1):
+... print(product, end=' ')
+...
+('A', 'x') ('A', 'y') ('B', 'x') ('B', 'y') ('C', 'x') ('C', 'y') ('D', 'x') ('D', 'y')
+```
+
+Here is an example of doing it wihout `product()`.
+It looks similliar to the last example but since we have two iterables we need to nest the for loops.
+Even though the proudct is given repeat=1.
+The reasson to why it is only 2 for loops earlier was because we only had one iterable.
+If we had two iterables and gave it repeat=2 we would need 4 for loops.
+Since 2 \* 2 = 4.
+
+```python
+>>> for letter1 in "ABCD":
+... for letter2 in "xy":
+... print((letter1, letter2), end=' ')
+...
+('A', 'x') ('A', 'y') ('B', 'x') ('B', 'y') ('C', 'x') ('C', 'y') ('D', 'x') ('D', 'y')
+```
+
+### Permutations()
+
+`permutations(iterable, )` creates an iterator of tuples.
+It works like `product()` but it doesnt repeat values from a specific positoon from the iterable and can only take one iterable.
+The "r" keyword argument can be used to specify the number of times the input iterables are repeated.
+By default the "r" keyword argument is None.
+If "r" is None then the length of the iterable is used.
+
+```python
+>>> import itertools
+>>> for permutation in itertools.permutations("ABCD", repeat=1):
+... print(permutation, end=' ')
+...
+('A',) ('B',) ('C',) ('D',)
+```
+
+```python
+>>> import itertools
+>>> for permutation in itertools.permutations("ABCD", repeat=2):
+... print(permutation, end=' ')
+...
+('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C')
+```
+
+### Combinations()
+
+`combinations(iterable, r)` finds all the possible combinations of the given iterable.
+The r keyword argument is used to specify the length of the tuples generated.
+
+```python
+>>> import itertools
+>>> for combination in itertools.combinations("ABCD", 2):
+... print(combination, end=' ')
+...
+('A', 'B') ('A', 'C') ('A', 'D') ('B', 'C') ('B', 'D') ('C', 'D')
+```
+
+### Combinations_with_replacement()
+
+`combinations_with_replacement(iterable, r)` finds all the possible combinations of the given iterable.
+The r keyword argument is used to specify the length of the tuples generated.
+The difference between this and `combinations()` is that it can repeat values.
+
+```python
+>>> import itertools
+>>> for combination in itertools.combinations_with_replacement("ABCD", 2):
+... print(combination, end=' ')
+...
+('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'C') ('C', 'D') ('D', 'D')
+```
diff --git a/concepts/itertools/links.json b/concepts/itertools/links.json
index eb5fb7c38a5..8a44906b3f8 100644
--- a/concepts/itertools/links.json
+++ b/concepts/itertools/links.json
@@ -1,7 +1,7 @@
[
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://docs.python.org/3/library/itertools.htm",
+ "description": "Offical Python documentation for the itertools module."
},
{
"url": "http://example.com/",
From 43e5f990346c8131ac234690d9e7e3100fa6a04c Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 22 Dec 2022 16:05:04 +0100
Subject: [PATCH 062/126] Added the changes we talked about and some extras
---
concepts/itertools/.meta/config.json | 2 +-
concepts/itertools/about.md | 195 +++++++++++++--------------
2 files changed, 93 insertions(+), 104 deletions(-)
diff --git a/concepts/itertools/.meta/config.json b/concepts/itertools/.meta/config.json
index 9b9e8da5a9b..3bf2b514ccf 100644
--- a/concepts/itertools/.meta/config.json
+++ b/concepts/itertools/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "cmccandless"],
+ "authors": ["bethanyg", "meatball133"],
"contributors": []
}
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
index 82e6c6161a9..e23860db98f 100644
--- a/concepts/itertools/about.md
+++ b/concepts/itertools/about.md
@@ -1,78 +1,5 @@
-## Infinite iterators
-
-```exercism/note
-To avoid infinite loops, you can use `break` to end a loop.
-```
-
-### Count()
-
-`count(start, )` allows to print all values from the start value to infinity.
-Count also has an optional step parameter, which allows to print values with a step size other than 1.
-
-```python
->>> import itertools
->>> for number in itertools.count(5, 2):
-... if number > 20:
-... break
-... else:
-... print(number, end=' ')
-...
-5 7 9 11 13 15 17 19
-```
-
-Giving `count()` a negative step size will print values in a descending order.
-
-```python
->>> import itertools
->>> for number in itertools.count(5, -2):
-... if number < -20:
-... break
-... else:
-... print(number, end=' ')
-...
-5 3 1 -1 -3 -5 -7 -9 -11 -13 -15 -17 -19
-```
-
-### Cycle()
-
-`cycle(iterable)` allows to print all values from the iterable in an infinte loop.
-For example a `list`, `tuple`, `string` or `dict` can be used as an iterable.
-
-```python
->>> import itertools
->>> number = 0
->>> for letter in itertools.cycle("ABC"):
-... if number == 10:
-... break
-... else:
-... print(letter, end=' ')
-... number += 1
-...
-A B C A B C A B C A
-```
-
-### Repeat()
-
-`repeat(object, )` allows to print the same value in an infinte loop.
-Although if the optional times parameter is given, the value will be printed that many times.
-Meaning that it is not an infinite loop if that parameter is given.
-
-```python
->>> import itertools
->>> for number in itertools.repeat(5, 3):
-... print(number, end=' ')
-...
-5 5 5
-```
-
## Iterators terminating on the shortest input sequence
-### Accumulate()
-
-`accumulate(iterable, )` allows to print the accumulated values.
-
-perhaps skip
-
### Chain()
`chain(iterable1, iterable2...)` creates an irretator of values from the iterables in the order they are given.
@@ -95,11 +22,16 @@ Chain can also be used to concate a different amount of iterables to a list or t
### chain.from_iterable()
-Works like chain but takes a single iterable argument and unpack that iterable into individual iterables.
+Works like chain but takes a single nested iterable, unpacking that iterable into individual iterables.
```python
>>> import itertools
->>> for number in itertools.chain.from_iterable([[1, 2, 3], [4, 5, 6], [7, 8, 9]]):
+>>> for number in itertools.chain.from_iterable(
+ [
+ [1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9]
+ ]):
... print(number, end=' ')
...
1 2 3 4 5 6 7 8 9
@@ -107,32 +39,30 @@ Works like chain but takes a single iterable argument and unpack that iterable i
### Compress()
-`compress(iterable, selectors)` creates an irretator from the iterable where the corresponding selector is `True`.
-The selector can hold bools but can also hold integers, where 0 is `False` and any other integer is `True`.
+`compress(iterable, selectors)` creates an iterator from the input iterable where the corresponding selector is `True`.
+The selector can be `True`/`False` or integers, where 0 is `False` and 1 is `True`.
```python
>>> import itertools
->>> for letter in itertools.compress("Exercism", [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]):
+>>> for letter in itertools.compress("Exercism", [1, 0, 0, 1, 1, 0, 1, 1]):
... print(letter, end=' ')
...
E r c s m
```
-### Dropwhile()
-
-skip
-
-### Filterfalse()
+With `True`/`False`:
-skip
-
-### Groupby()
-
-probably skip
+```python
+>>> import itertools
+>>> for letter in itertools.compress("Exercism", [True, False, False, True, True, False, True, True]):
+... print(letter, end=' ')
+...
+E r c s m
+```
### Islice()
-`islice(iterable, , , )` creates an irretator of the values from the iterable, from the start index to the stop index with a step size of step.
+`islice(iterable, start, , )` creates a new iterator from the slice (from the start index to the stop index with a step size of step).
```python
>>> import itertools
@@ -159,14 +89,6 @@ If you are using the online editor then you don't need to worry about this.
('E', 'x') ('x', 'e') ('e', 'r') ('r', 'c') ('c', 'i') ('i', 's') ('s', 'm')
```
-### Starmap()
-
-Pehaps skip
-
-### Takewhile()
-
-skip
-
### Tee()
Talk with Bethany about
@@ -176,15 +98,13 @@ Talk with Bethany about
#### Explaning zip
```exercism/caution
-Zip in computer science(programming) is not the same as zip in computer terms.
-Zip in computer terms reffers to a file format that supports compression.
-While zip in computer science(programming) reffers to the `zip()` function.
+Pythons `zip()` function should not be confused with the zip compression format.
```
`zip()` is a built in function and is not apart of the `itertools` module.
It takes any number of iterables and returns an iterator of tuples.
Where the i-th tuple contains the i-th element from each of the argument iterables.
-Meaning the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable and so on.
+For example, the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable, and so on until the shortest iterable is exhausted.
```python
>>> zipped = zip(['x', 'y', 'z'], [1, 2, 3], [True, False, True])
@@ -203,8 +123,8 @@ If the iterables are not the same length, then the iterator will stop when the s
#### Explaning zip_longest
`zip_longest(iterator, )` is a function from the `itertools` module.
-The difference between `zip_longest()` and `zip()` is that `zip_longest()` will continue until the longest iterable is exhausted.
-If the iterables are not the same length the `fillvalue` will be used to fill in the missing values.
+Unlink `zip()`, it will not stop when the shortest iterable is exhausted.
+If the iterables are not the same length, `fillvalue` will be used to pad missing values.
By the default the `fillvalue` is `None`.
```python
@@ -239,6 +159,8 @@ By default the repeat keyword argument is 1.
('A',) ('B',) ('C',) ('D',)
```
+Giving a repeat value of 2:
+
```python
>>> import itertools
>>> for product in itertools.product("ABCD", repeat=2):
@@ -335,3 +257,70 @@ The difference between this and `combinations()` is that it can repeat values.
...
('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'C') ('C', 'D') ('D', 'D')
```
+
+## Infinite iterators
+
+```exercism/note
+To avoid infinite loops, you can use `break` to end a loop.
+```
+
+### Count()
+
+`count(start, )` produces all values from the start value to infinity.
+Count also has an optional step parameter, which will produce values with a step size other than 1.
+
+```python
+>>> import itertools
+>>> for number in itertools.count(5, 2):
+... if number > 20:
+... break
+... else:
+... print(number, end=' ')
+...
+5 7 9 11 13 15 17 19
+```
+
+Giving `count()` a negative step size will produces values in a descending order.
+
+```python
+>>> import itertools
+>>> for number in itertools.count(5, -2):
+... if number < -20:
+... break
+... else:
+... print(number, end=' ')
+...
+5 3 1 -1 -3 -5 -7 -9 -11 -13 -15 -17 -19
+```
+
+### Cycle()
+
+`cycle(iterable)` produces all values from the iterable in an infinte loop.
+A `list`, `tuple`, `string`, `dict` or any other iterable can be used.
+
+```python
+>>> import itertools
+>>> number = 0
+>>> for letter in itertools.cycle("ABC"):
+... if number == 10:
+... break
+... else:
+... print(letter, end=' ')
+... number += 1
+...
+A B C A B C A B C A
+```
+
+### Repeat()
+
+`repeat(object, )` produces the same value in an infinte loop.
+Although if the optional times parameter is given, the value will produces that many times.
+Meaning that it is not an infinite loop if that parameter is given.
+
+```python
+>>> import itertools
+>>> for number in itertools.repeat(5, 3):
+... print(number, end=' ')
+...
+5 5 5
+```
From 99689d80c9afa1dbd7a54e4681c8f41ea56bc011 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 23 Dec 2022 17:01:41 +0100
Subject: [PATCH 063/126] Added extra parts
---
concepts/itertools/about.md | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
index e23860db98f..4cfe5bca536 100644
--- a/concepts/itertools/about.md
+++ b/concepts/itertools/about.md
@@ -14,12 +14,22 @@
Chain can also be used to concate a different amount of iterables to a list or tuple by using the `list()` or `tuple()` function.
+Using `list()`:
+
```python
>>> import itertools
>>> list(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
```
+Using `tuple()`:
+
+```python
+>>> import itertools
+>>> tuple(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]))
+(1, 2, 3, 4, 5, 6, 7, 8, 9)
+```
+
### chain.from_iterable()
Works like chain but takes a single nested iterable, unpacking that iterable into individual iterables.
From 85f23648a3540530f3ec10bc51923b25f6975baf Mon Sep 17 00:00:00 2001
From: Carl
Date: Sat, 24 Dec 2022 23:34:24 +0100
Subject: [PATCH 064/126] Added blurb
---
concepts/itertools/about.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
index 4cfe5bca536..366515cf1aa 100644
--- a/concepts/itertools/about.md
+++ b/concepts/itertools/about.md
@@ -270,6 +270,10 @@ The difference between this and `combinations()` is that it can repeat values.
## Infinite iterators
+Most of iterator from the `itertools` module get exhausted after a time.
+But there are some that are infinite, these are known as infinte iterators.
+These iterators will will keep producing values until you tell them to stop.
+
```exercism/note
To avoid infinite loops, you can use `break` to end a loop.
```
From 5d31c1b37d09387016f0860cb879b42e4db5b4a3 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 13:36:11 +0100
Subject: [PATCH 065/126] Started on resistor_color_master
---
config.json | 15 ++
.../.docs/instructions.md | 54 +++++++
.../resistor-color-master/.meta/config.json | 20 +++
.../resistor-color-master/.meta/example.py | 47 ++++++
.../resistor-color-master/.meta/hi.json | 147 ++++++++++++++++++
.../resistor-color-master/.meta/template.j2 | 15 ++
.../resistor-color-master/.meta/tests.toml | 40 +++++
.../resistor_color_master.py | 49 ++++++
.../resistor_color_master_test.py | 51 ++++++
9 files changed, 438 insertions(+)
create mode 100644 exercises/practice/resistor-color-master/.docs/instructions.md
create mode 100644 exercises/practice/resistor-color-master/.meta/config.json
create mode 100644 exercises/practice/resistor-color-master/.meta/example.py
create mode 100644 exercises/practice/resistor-color-master/.meta/hi.json
create mode 100644 exercises/practice/resistor-color-master/.meta/template.j2
create mode 100644 exercises/practice/resistor-color-master/.meta/tests.toml
create mode 100644 exercises/practice/resistor-color-master/resistor_color_master.py
create mode 100644 exercises/practice/resistor-color-master/resistor_color_master_test.py
diff --git a/config.json b/config.json
index 3e292d7440b..84c5f1ea6fc 100644
--- a/config.json
+++ b/config.json
@@ -970,6 +970,21 @@
],
"difficulty": 3
},
+ {
+ "slug": "resistor-color-master",
+ "name": "Resistor Color Master",
+ "uuid": "f987a5b7-96f2-49c2-99e8-aef30d23dd58",
+ "practices": ["list-methods"],
+ "prerequisites": [
+ "basics",
+ "bools",
+ "lists",
+ "numbers",
+ "strings",
+ "comparisons"
+ ],
+ "difficulty": 3
+ },
{
"slug": "matrix",
"name": "Matrix",
diff --git a/exercises/practice/resistor-color-master/.docs/instructions.md b/exercises/practice/resistor-color-master/.docs/instructions.md
new file mode 100644
index 00000000000..fcc76958a5f
--- /dev/null
+++ b/exercises/practice/resistor-color-master/.docs/instructions.md
@@ -0,0 +1,54 @@
+# Instructions
+
+If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
+For this exercise, you need to know only three things about them:
+
+- Each resistor has a resistance value.
+- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read.
+ To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values.
+- Each band acts as a digit of a number.
+ For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15.
+ In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands.
+ The program will take 3 colors as input, and outputs the correct value, in ohms.
+ The color bands are encoded as follows:
+
+- Black: 0
+- Brown: 1
+- Red: 2
+- Orange: 3
+- Yellow: 4
+- Green: 5
+- Blue: 6
+- Violet: 7
+- Grey: 8
+- White: 9
+
+In `resistor-color duo` you decoded the first two colors.
+For instance: orange-orange got the main value `33`.
+The third color stands for how many zeros need to be added to the main value.
+The main value plus the zeros gives us a value in ohms.
+For the exercise it doesn't matter what ohms really are.
+For example:
+
+- orange-orange-black would be 33 and no zeros, which becomes 33 ohms.
+- orange-orange-red would be 33 and 2 zeros, which becomes 3300 ohms.
+- orange-orange-orange would be 33 and 3 zeros, which becomes 33000 ohms.
+
+(If Math is your thing, you may want to think of the zeros as exponents of 10.
+If Math is not your thing, go with the zeros.
+It really is the same thing, just in plain English instead of Math lingo.)
+
+This exercise is about translating the colors into a label:
+
+> "... ohms"
+
+So an input of `"orange", "orange", "black"` should return:
+
+> "33 ohms"
+
+When we get more than a thousand ohms, we say "kiloohms".
+That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams.
+
+So an input of `"orange", "orange", "orange"` should return:
+
+> "33 kiloohms"
diff --git a/exercises/practice/resistor-color-master/.meta/config.json b/exercises/practice/resistor-color-master/.meta/config.json
new file mode 100644
index 00000000000..23a09a2cdd9
--- /dev/null
+++ b/exercises/practice/resistor-color-master/.meta/config.json
@@ -0,0 +1,20 @@
+{
+ "authors": [
+ "meatball133",
+ "bethanyg"
+ ],
+ "files": {
+ "solution": [
+ "resistor_color_master.py"
+ ],
+ "test": [
+ "resistor_color_master_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "blurb": "Convert color codes, as used on resistors, to a human-readable label.",
+ "source": "Maud de Vries, Erik Schierboom",
+ "source_url": "https://github.com/exercism/problem-specifications/issues/1549"
+}
diff --git a/exercises/practice/resistor-color-master/.meta/example.py b/exercises/practice/resistor-color-master/.meta/example.py
new file mode 100644
index 00000000000..a7dd1cd367e
--- /dev/null
+++ b/exercises/practice/resistor-color-master/.meta/example.py
@@ -0,0 +1,47 @@
+COLORS = [
+ 'black',
+ 'brown',
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'blue',
+ 'violet',
+ 'grey',
+ 'white'
+]
+
+COLORS_TOLERANCE = {
+ 'brown': 1,
+ 'red': 2,
+ 'green': 0.5,
+ 'blue': 0.25,
+ 'violet': 0.1,
+ 'grey': 0.05,
+ 'gold': 5,
+ 'silver': 10
+}
+
+
+def label(colors):
+ if len(colors) == 1:
+ return f'0 ohms'
+ elif len(colors) == 4:
+ value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
+ value *= 10 ** COLORS.index(colors[2])
+ value, unit = color_code(value)
+ return f'{value} {unit}, {COLORS_TOLERANCE[colors[3]]}%'
+ else:
+ value = 10 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2])
+ value *= 10 ** COLORS.index(colors[3])
+ value, unit = color_code(value)
+ return f'{value} {unit}, {COLORS_TOLERANCE[colors[4]]}%'
+
+
+def color_code(color):
+ if color < 1000:
+ return color, 'ohms'
+ elif color < 1000000:
+ return color / 1000, 'kiloohms'
+ else:
+ return color / 1000000, 'megaohms'
\ No newline at end of file
diff --git a/exercises/practice/resistor-color-master/.meta/hi.json b/exercises/practice/resistor-color-master/.meta/hi.json
new file mode 100644
index 00000000000..b25a6b1acaf
--- /dev/null
+++ b/exercises/practice/resistor-color-master/.meta/hi.json
@@ -0,0 +1,147 @@
+{
+ "exercise": "resistor-color-master",
+ "cases": [
+ {
+ "uuid": "8c4f9fb6-d477-4250-bc57-b325d2be226f",
+ "description": "Orange, orange, black, and red",
+ "property": "label",
+ "input": {
+ "colors": ["orange", "orange", "black", "red"]
+ },
+ "expected": {
+ "value": 33,
+ "tolerance": 2,
+ "unit": "ohms"
+ }
+ },
+ {
+ "uuid": "d1d4a769-9210-43cc-9a14-6af6ce4c0b00",
+ "description": "Blue, grey, brown, and violet",
+ "property": "label",
+ "input": {
+ "colors": ["blue", "grey", "brown", "violet"]
+ },
+ "expected": {
+ "value": 680,
+ "tolerance": 0.1,
+ "unit": "ohms"
+ }
+ },
+ {
+ "uuid": "6af91bc3-8275-4c38-920a-185d30feb5f3",
+ "description": "Red, black, red, and green",
+ "property": "label",
+ "input": {
+ "colors": ["red", "black", "red", "green"]
+ },
+ "expected": {
+ "value": 2,
+ "tolerance": 0.5,
+ "unit": "kiloohms"
+ }
+ },
+ {
+ "uuid": "9c4630bf-0dda-4212-baca-2f5111530b4d",
+ "description": "Green, brown, orange, and grey",
+ "property": "label",
+ "input": {
+ "colors": ["green", "brown", "orange", "grey"]
+ },
+ "expected": {
+ "value": 51,
+ "tolerance": 0.05,
+ "unit": "kiloohms"
+ }
+ },
+ {
+ "uuid": "93b7d0ec-39fb-49f3-bb88-2dd4b8c293af",
+ "description": "Yellow, violet, yellow, and blue",
+ "property": "label",
+ "input": {
+ "colors": ["yellow", "violet", "yellow", "blue"]
+ },
+ "expected": {
+ "value": 470,
+ "tolerance": 0.25,
+ "unit": "kiloohms"
+ }
+ },
+ {
+ "uuid": "5880ddf1-0dc6-4bd0-b9de-5626117cd2c7",
+ "description": "One black band",
+ "property": "label",
+ "input": {
+ "colors": ["black"]
+ },
+ "expected": {
+ "value": 0,
+ "unit": "ohms"
+ }
+ },
+ {
+ "uuid": "a5cfda34-3c02-4bda-b183-726791fb43b2",
+ "description": "Orange, orange, yellow, black, and brown",
+ "property": "label",
+ "input": {
+ "colors": ["orange", "orange", "yellow", "black", "brown"]
+ },
+ "expected": {
+ "value": 334,
+ "tolerance": 1,
+ "unit": "ohms"
+ }
+ },
+ {
+ "uuid": "4f0ad96c-cdab-4c84-95dd-7074e889e001",
+ "description": "Red, green, yellow, yellow, and brown",
+ "property": "label",
+ "input": {
+ "colors": ["red", "green", "yellow", "yellow", "brown"]
+ },
+ "expected": {
+ "value": 2.54,
+ "tolerance": 1,
+ "unit": "megaohms"
+ }
+ },
+ {
+ "uuid": "48c66841-208c-46a7-8992-1e84a2eda9e2",
+ "description": "Blue, grey, white, brown, and brown",
+ "property": "label",
+ "input": {
+ "colors": ["blue", "grey", "white", "brown", "brown"]
+ },
+ "expected": {
+ "value": 6.89,
+ "tolerance": 1,
+ "unit": "kiloohms"
+ }
+ },
+ {
+ "uuid": "4f3aeb0c-fd9d-4cbd-b204-554e61978f73",
+ "description": "Violet, orange, red, and grey",
+ "property": "label",
+ "input": {
+ "colors": ["violet", "orange", "red", "grey"]
+ },
+ "expected": {
+ "value": 7.3,
+ "tolerance": 0.05,
+ "unit": "kiloohms"
+ }
+ },
+ {
+ "uuid": "1ae3a17f-8bc0-449c-9831-fddfd2d91c5d",
+ "description": "Brown, red, orange, green, and blue",
+ "property": "label",
+ "input": {
+ "colors": ["brown", "red", "orange", "green", "blue"]
+ },
+ "expected": {
+ "value": 12.3,
+ "tolerance": 0.25,
+ "unit": "megaohms"
+ }
+ }
+ ]
+}
diff --git a/exercises/practice/resistor-color-master/.meta/template.j2 b/exercises/practice/resistor-color-master/.meta/template.j2
new file mode 100644
index 00000000000..e9b458f34c3
--- /dev/null
+++ b/exercises/practice/resistor-color-master/.meta/template.j2
@@ -0,0 +1,15 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{% macro test_case(case) -%}
+ {%- set input = case["input"] -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ self.assertEqual(
+ {{ case["property"] | to_snake }}({{ case["input"]["colors"] }}),
+ "{{ case['expected']['value']}} {{ case['expected']['unit']}} {{case['expected']['tolerance']}}{{"%" if case['expected']['tolerance'] else ""}}"
+ )
+{%- endmacro %}
+{{ macros.header()}}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
diff --git a/exercises/practice/resistor-color-master/.meta/tests.toml b/exercises/practice/resistor-color-master/.meta/tests.toml
new file mode 100644
index 00000000000..4f57723a19f
--- /dev/null
+++ b/exercises/practice/resistor-color-master/.meta/tests.toml
@@ -0,0 +1,40 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[8c4f9fb6-d477-4250-bc57-b325d2be226f]
+description = "Orange, orange, black, and red"
+
+[d1d4a769-9210-43cc-9a14-6af6ce4c0b00]
+description = "Blue, grey, brown, and violet"
+
+[6af91bc3-8275-4c38-920a-185d30feb5f3]
+description = "Red, black, red, and green"
+
+[9c4630bf-0dda-4212-baca-2f5111530b4d]
+description = "Green, brown, orange, and grey"
+
+[5880ddf1-0dc6-4bd0-b9de-5626117cd2c7]
+description = "One black band"
+
+[a5cfda34-3c02-4bda-b183-726791fb43b2]
+description = "Orange, orange, yellow, black, and brown"
+
+[4f0ad96c-cdab-4c84-95dd-7074e889e001]
+description = "Red, green, yellow, yellow, and brown"
+
+[48c66841-208c-46a7-8992-1e84a2eda9e2]
+description = "Blue, grey, white, brown, and brown"
+
+[4f3aeb0c-fd9d-4cbd-b204-554e61978f73]
+description = "Violet, orange, red, and grey"
+
+[1ae3a17f-8bc0-449c-9831-fddfd2d91c5d]
+description = "Brown, red, orange, green, and blue"
diff --git a/exercises/practice/resistor-color-master/resistor_color_master.py b/exercises/practice/resistor-color-master/resistor_color_master.py
new file mode 100644
index 00000000000..83fd403e5c2
--- /dev/null
+++ b/exercises/practice/resistor-color-master/resistor_color_master.py
@@ -0,0 +1,49 @@
+COLORS = [
+ 'black',
+ 'brown',
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'blue',
+ 'violet',
+ 'grey',
+ 'white'
+]
+
+COLORS_TOLERANCE = {
+ 'brown': 1,
+ 'red': 2,
+ 'green': 0.5,
+ 'blue': 0.25,
+ 'violet': 0.1,
+ 'grey': 0.05,
+ 'gold': 5,
+ 'silver': 10
+}
+
+
+def label(colors):
+ if len(colors) == 1:
+ return f'0 ohms'
+ elif len(colors) == 4:
+ value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
+ value *= 10 ** COLORS.index(colors[2])
+ value, unit = color_code(value)
+ value = int(value) if value.is_integer() else value
+ return f'{value} {unit} {COLORS_TOLERANCE[colors[3]]}%'
+ else:
+ value = 100 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2])
+ value *= 10 ** COLORS.index(colors[3])
+ value, unit = color_code(value)
+ value = int(value) if value.is_integer() else value
+ return f'{value} {unit} {COLORS_TOLERANCE[colors[4]]}%'
+
+
+def color_code(color):
+ if color < 1000:
+ return color / 1, 'ohms'
+ elif color < 1000000:
+ return color / 1000, 'kiloohms'
+ else:
+ return color / 1000000, 'megaohms'
\ No newline at end of file
diff --git a/exercises/practice/resistor-color-master/resistor_color_master_test.py b/exercises/practice/resistor-color-master/resistor_color_master_test.py
new file mode 100644
index 00000000000..458ca7ab601
--- /dev/null
+++ b/exercises/practice/resistor-color-master/resistor_color_master_test.py
@@ -0,0 +1,51 @@
+import unittest
+
+from resistor_color_master import (
+ label,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class ResistorColorMasterTest(unittest.TestCase):
+ def test_orange_orange_black_and_red(self):
+ self.assertEqual(label(["orange", "orange", "black", "red"]), "33 ohms 2%")
+
+ def test_blue_grey_brown_and_violet(self):
+ self.assertEqual(label(["blue", "grey", "brown", "violet"]), "680 ohms 0.1%")
+
+ def test_red_black_red_and_green(self):
+ self.assertEqual(label(["red", "black", "red", "green"]), "2 kiloohms 0.5%")
+
+ def test_green_brown_orange_and_grey(self):
+ self.assertEqual(
+ label(["green", "brown", "orange", "grey"]), "51 kiloohms 0.05%"
+ )
+
+ def test_one_black_band(self):
+ self.assertEqual(label(["black"]), "0 ohms")
+
+ def test_orange_orange_yellow_black_and_brown(self):
+ self.assertEqual(
+ label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms 1%"
+ )
+
+ def test_red_green_yellow_yellow_and_brown(self):
+ self.assertEqual(
+ label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms 1%"
+ )
+
+ def test_blue_grey_white_red_and_brown(self):
+ self.assertEqual(
+ label(["blue", "grey", "white", "red", "brown"]), "6.89 kiloohms 1%"
+ )
+
+ def test_violet_orange_red_and_grey(self):
+ self.assertEqual(
+ label(["violet", "orange", "red", "grey"]), "7.3 kiloohms 0.05%"
+ )
+
+ def test_brown_red_orange_green_and_blue(self):
+ self.assertEqual(
+ label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms 0.25%"
+ )
From 3494dcddb522cbb259d3661b98bfdcbaa86c5e57 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:38:44 +0100
Subject: [PATCH 066/126] Fix
---
config.json | 2 +-
.../resistor-color-master/resistor_color_master_test.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/config.json b/config.json
index 84c5f1ea6fc..e9a513fafb8 100644
--- a/config.json
+++ b/config.json
@@ -973,7 +973,7 @@
{
"slug": "resistor-color-master",
"name": "Resistor Color Master",
- "uuid": "f987a5b7-96f2-49c2-99e8-aef30d23dd58",
+ "uuid": "8a738365-0efa-444f-9466-a757ddaddcdb",
"practices": ["list-methods"],
"prerequisites": [
"basics",
diff --git a/exercises/practice/resistor-color-master/resistor_color_master_test.py b/exercises/practice/resistor-color-master/resistor_color_master_test.py
index 458ca7ab601..cd595fe4524 100644
--- a/exercises/practice/resistor-color-master/resistor_color_master_test.py
+++ b/exercises/practice/resistor-color-master/resistor_color_master_test.py
@@ -37,7 +37,7 @@ def test_red_green_yellow_yellow_and_brown(self):
def test_blue_grey_white_red_and_brown(self):
self.assertEqual(
- label(["blue", "grey", "white", "red", "brown"]), "6.89 kiloohms 1%"
+ label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms 1%"
)
def test_violet_orange_red_and_grey(self):
From f168108f0e30a634e2aa01117c19b50b10a5fd12 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:41:40 +0100
Subject: [PATCH 067/126] Fixed example
---
.../practice/resistor-color-master/.meta/example.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/resistor-color-master/.meta/example.py b/exercises/practice/resistor-color-master/.meta/example.py
index a7dd1cd367e..83fd403e5c2 100644
--- a/exercises/practice/resistor-color-master/.meta/example.py
+++ b/exercises/practice/resistor-color-master/.meta/example.py
@@ -30,17 +30,19 @@ def label(colors):
value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
value *= 10 ** COLORS.index(colors[2])
value, unit = color_code(value)
- return f'{value} {unit}, {COLORS_TOLERANCE[colors[3]]}%'
+ value = int(value) if value.is_integer() else value
+ return f'{value} {unit} {COLORS_TOLERANCE[colors[3]]}%'
else:
- value = 10 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2])
+ value = 100 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2])
value *= 10 ** COLORS.index(colors[3])
value, unit = color_code(value)
- return f'{value} {unit}, {COLORS_TOLERANCE[colors[4]]}%'
+ value = int(value) if value.is_integer() else value
+ return f'{value} {unit} {COLORS_TOLERANCE[colors[4]]}%'
def color_code(color):
if color < 1000:
- return color, 'ohms'
+ return color / 1, 'ohms'
elif color < 1000000:
return color / 1000, 'kiloohms'
else:
From 3df9a8008e8e9d776507b5cb16766b352856dcd2 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:42:39 +0100
Subject: [PATCH 068/126] Added instructions
---
.../.docs/instructions.md | 72 +++++++++++++------
1 file changed, 52 insertions(+), 20 deletions(-)
diff --git a/exercises/practice/resistor-color-master/.docs/instructions.md b/exercises/practice/resistor-color-master/.docs/instructions.md
index fcc76958a5f..8263897d07f 100644
--- a/exercises/practice/resistor-color-master/.docs/instructions.md
+++ b/exercises/practice/resistor-color-master/.docs/instructions.md
@@ -1,7 +1,7 @@
-# Instructions
+# Description
If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
-For this exercise, you need to know only three things about them:
+For this exercise, you need to be able to use 1, 4, and 5 bands resistor:
- Each resistor has a resistance value.
- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read.
@@ -9,7 +9,7 @@ For this exercise, you need to know only three things about them:
- Each band acts as a digit of a number.
For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15.
In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands.
- The program will take 3 colors as input, and outputs the correct value, in ohms.
+ The program will take 1, 4, or 5 colors as input, and outputs the correct value, in ohms.
The color bands are encoded as follows:
- Black: 0
@@ -23,32 +23,64 @@ For this exercise, you need to know only three things about them:
- Grey: 8
- White: 9
-In `resistor-color duo` you decoded the first two colors.
-For instance: orange-orange got the main value `33`.
-The third color stands for how many zeros need to be added to the main value.
-The main value plus the zeros gives us a value in ohms.
-For the exercise it doesn't matter what ohms really are.
-For example:
+In `resistor-color trio` you decoded the first three colors.
+For instance: orange-orange-brown got the main value `330`.
+In this exercise you need to add tolerance to the mix.
+Tolerance is the maximum amount that the value can be above or below the main value.
+The tolerance value is always the last band.
+For example, if the last band is green, the maximum tolerance will be 0.5%.
+The tolerance band will have one of these values:
-- orange-orange-black would be 33 and no zeros, which becomes 33 ohms.
-- orange-orange-red would be 33 and 2 zeros, which becomes 3300 ohms.
-- orange-orange-orange would be 33 and 3 zeros, which becomes 33000 ohms.
+- Grey - 0.05%
+- Violet - 0.1%
+- Blue - 0.25%
+- Green - 0.5%
+- Brown - 1%
+- Red - 2%
+- Gold - 5%
+- Silver - 10%
-(If Math is your thing, you may want to think of the zeros as exponents of 10.
-If Math is not your thing, go with the zeros.
-It really is the same thing, just in plain English instead of Math lingo.)
+The fourth band resistor is built up like these:
+
+| Band_1 | Band_2 | Band_3 | band_4 |
+| ------- | ------- | ---------- | --------- |
+| Value_1 | Value_2 | Multiplier | Tolerance |
+
+Meaning
+
+- orange-orange-brown-green would be 330 ohms with a 0.5% tolerance.
+- orange-orange-red would-grey would be 3300 ohms with 0.005 tolerance.
+
+The difference between the 4 and 5 band resistor is that there is an extra band for value a more precises value.
+
+| Band_1 | Band_2 | Band_3 | Band_4 | band_5 |
+| ------- | ------- | ------- | ---------- | --------- |
+| Value_1 | Value_2 | Value_3 | Multiplier | Tolerance |
+
+Meaning
+
+- orange-orange-orange-black-green would be 330 ohms with a 0.5% tolerance.
+
+There will also be a one band resistor.
+This resistor will only have the color black and have the value 0.
This exercise is about translating the colors into a label:
-> "... ohms"
+> "... ohms ...%"
-So an input of `"orange", "orange", "black"` should return:
+So an input of `"orange", "orange", "black, green"` should return:
-> "33 ohms"
+> "33 ohms 0.5%"
When we get more than a thousand ohms, we say "kiloohms".
That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams.
-So an input of `"orange", "orange", "orange"` should return:
+So an input of `"orange", "orange", "orange", grey` should return:
+
+> "33 kiloohms 0.05%"
+
+When we get more than a million ohms, we say "megaohms".
+
+So an input of `"orange", "orange", "orange", "red"` should return:
-> "33 kiloohms"
+> "33 megaohms 2%"
From 4e60f5829d11048b847bdc73cc5c6d39b2ef502e Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 00:26:04 +0100
Subject: [PATCH 069/126] Changes
---
concepts/itertools/.meta/config.json | 4 +-
concepts/itertools/about.md | 341 +-----------------
concepts/itertools/links.json | 4 +-
.../resistor-color-master/.meta/template.j2 | 6 +-
4 files changed, 10 insertions(+), 345 deletions(-)
diff --git a/concepts/itertools/.meta/config.json b/concepts/itertools/.meta/config.json
index 3bf2b514ccf..65190cc1ef5 100644
--- a/concepts/itertools/.meta/config.json
+++ b/concepts/itertools/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "meatball133"],
+ "authors": ["bethanyg", "cmccandless"],
"contributors": []
-}
+}
\ No newline at end of file
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
index 366515cf1aa..cbc5cd89d96 100644
--- a/concepts/itertools/about.md
+++ b/concepts/itertools/about.md
@@ -1,340 +1 @@
-## Iterators terminating on the shortest input sequence
-
-### Chain()
-
-`chain(iterable1, iterable2...)` creates an irretator of values from the iterables in the order they are given.
-
-```python
->>> import itertools
->>> for number in itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]):
-... print(number, end=' ')
-...
-1 2 3 4 5 6 7 8 9
-```
-
-Chain can also be used to concate a different amount of iterables to a list or tuple by using the `list()` or `tuple()` function.
-
-Using `list()`:
-
-```python
->>> import itertools
->>> list(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]))
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
-```
-
-Using `tuple()`:
-
-```python
->>> import itertools
->>> tuple(itertools.chain([1, 2, 3], [4, 5, 6], [7, 8, 9]))
-(1, 2, 3, 4, 5, 6, 7, 8, 9)
-```
-
-### chain.from_iterable()
-
-Works like chain but takes a single nested iterable, unpacking that iterable into individual iterables.
-
-```python
->>> import itertools
->>> for number in itertools.chain.from_iterable(
- [
- [1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]
- ]):
-... print(number, end=' ')
-...
-1 2 3 4 5 6 7 8 9
-```
-
-### Compress()
-
-`compress(iterable, selectors)` creates an iterator from the input iterable where the corresponding selector is `True`.
-The selector can be `True`/`False` or integers, where 0 is `False` and 1 is `True`.
-
-```python
->>> import itertools
->>> for letter in itertools.compress("Exercism", [1, 0, 0, 1, 1, 0, 1, 1]):
-... print(letter, end=' ')
-...
-E r c s m
-```
-
-With `True`/`False`:
-
-```python
->>> import itertools
->>> for letter in itertools.compress("Exercism", [True, False, False, True, True, False, True, True]):
-... print(letter, end=' ')
-...
-E r c s m
-```
-
-### Islice()
-
-`islice(iterable, start, , )` creates a new iterator from the slice (from the start index to the stop index with a step size of step).
-
-```python
->>> import itertools
->>> for letter in itertools.islice("Exercism", 2, 5, 2):
-... print(letter, end=' ')
-...
-e c
-```
-
-### Pairwise()
-
-```exercism/note
-`Pairwise()` requires Python 3.10+.
-If you are using the online editor then you don't need to worry about this.
-```
-
-`Pairwise(iterable)` was intruduced in Python 3.10 and returns an iterator of overlapping pairs of values from the input iterable.
-
-```python
->>> import itertools
->>> for pair in itertools.pairwise("Exercism"):
-... print(pair, end=' ')
-...
-('E', 'x') ('x', 'e') ('e', 'r') ('r', 'c') ('c', 'i') ('i', 's') ('s', 'm')
-```
-
-### Tee()
-
-Talk with Bethany about
-
-### Zip_longest()
-
-#### Explaning zip
-
-```exercism/caution
-Pythons `zip()` function should not be confused with the zip compression format.
-```
-
-`zip()` is a built in function and is not apart of the `itertools` module.
-It takes any number of iterables and returns an iterator of tuples.
-Where the i-th tuple contains the i-th element from each of the argument iterables.
-For example, the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable, and so on until the shortest iterable is exhausted.
-
-```python
->>> zipped = zip(['x', 'y', 'z'], [1, 2, 3], [True, False, True])
->>> list(zipped)
-[('x', 1, True),('y', 2, False), ('z', 3, True)]
-```
-
-If the iterables are not the same length, then the iterator will stop when the shortest iterable is exhausted.
-
-```python
->>> zipped = zip(['x', 'y', 'z'], [1, 2, 3, 4], [True, False])
->>> list(zipped)
-[('x', 1, True),('y', 2, False)]
-```
-
-#### Explaning zip_longest
-
-`zip_longest(iterator, )` is a function from the `itertools` module.
-Unlink `zip()`, it will not stop when the shortest iterable is exhausted.
-If the iterables are not the same length, `fillvalue` will be used to pad missing values.
-By the default the `fillvalue` is `None`.
-
-```python
->>> import itertools
->>> zipped = itertools.zip_longest(['x', 'y', 'z'], [1, 2, 3, 4], [True, False])
->>> list(zipped)
-[('x', 1, True),('y', 2, False), ('z', 3, None), (None, 4, None)]
-```
-
-An example where a fillvalue is given:
-
-```python
->>> import itertools
->>> zipped = itertools.zip_longest(['x', 'y', 'z'], [1, 2, 3, 4], [True, False], fillvalue='fill')
->>> list(zipped)
-[('x', 1, True),('y', 2, False), ('z', 3, 'fill'), ('fill', 4, 'fill')]
-```
-
-## Combinatoric iterators
-
-### Product()
-
-`product(iterable1, iterable2..., )` creates an iterator of tuples where the i-th tuple contains the i-th element from each of the argument iterables.
-The repeat keyword argument can be used to specify the number of times the input iterables are repeated.
-By default the repeat keyword argument is 1.
-
-```python
->>> import itertools
->>> for product in itertools.product("ABCD", repeat=1):
-... print(product, end=' ')
-...
-('A',) ('B',) ('C',) ('D',)
-```
-
-Giving a repeat value of 2:
-
-```python
->>> import itertools
->>> for product in itertools.product("ABCD", repeat=2):
-... print(product, end=' ')
-...
-('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'C') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C') ('D', 'D')
-```
-
-The last one here can be seen as doing a nested for loop.
-When you increase the repeat value the number of iterations increases exponentially.
-The example above is a n\*\*2 iteration.
-
-```python
->>> import itertools
->>> for letter1 in "ABCD":
-... for letter2 in "ABCD":
-... print((letter1, letter2), end=' ')
-...
-('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'C') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C') ('D', 'D')
-```
-
-You can also give it multiple iterables.
-
-```python
->>> import itertools
->>> for product in itertools.product("ABCD", "xy" repeat=1):
-... print(product, end=' ')
-...
-('A', 'x') ('A', 'y') ('B', 'x') ('B', 'y') ('C', 'x') ('C', 'y') ('D', 'x') ('D', 'y')
-```
-
-Here is an example of doing it wihout `product()`.
-It looks similliar to the last example but since we have two iterables we need to nest the for loops.
-Even though the proudct is given repeat=1.
-The reasson to why it is only 2 for loops earlier was because we only had one iterable.
-If we had two iterables and gave it repeat=2 we would need 4 for loops.
-Since 2 \* 2 = 4.
-
-```python
->>> for letter1 in "ABCD":
-... for letter2 in "xy":
-... print((letter1, letter2), end=' ')
-...
-('A', 'x') ('A', 'y') ('B', 'x') ('B', 'y') ('C', 'x') ('C', 'y') ('D', 'x') ('D', 'y')
-```
-
-### Permutations()
-
-`permutations(iterable, )` creates an iterator of tuples.
-It works like `product()` but it doesnt repeat values from a specific positoon from the iterable and can only take one iterable.
-The "r" keyword argument can be used to specify the number of times the input iterables are repeated.
-By default the "r" keyword argument is None.
-If "r" is None then the length of the iterable is used.
-
-```python
->>> import itertools
->>> for permutation in itertools.permutations("ABCD", repeat=1):
-... print(permutation, end=' ')
-...
-('A',) ('B',) ('C',) ('D',)
-```
-
-```python
->>> import itertools
->>> for permutation in itertools.permutations("ABCD", repeat=2):
-... print(permutation, end=' ')
-...
-('A', 'B') ('A', 'C') ('A', 'D') ('B', 'A') ('B', 'C') ('B', 'D') ('C', 'A') ('C', 'B') ('C', 'D') ('D', 'A') ('D', 'B') ('D', 'C')
-```
-
-### Combinations()
-
-`combinations(iterable, r)` finds all the possible combinations of the given iterable.
-The r keyword argument is used to specify the length of the tuples generated.
-
-```python
->>> import itertools
->>> for combination in itertools.combinations("ABCD", 2):
-... print(combination, end=' ')
-...
-('A', 'B') ('A', 'C') ('A', 'D') ('B', 'C') ('B', 'D') ('C', 'D')
-```
-
-### Combinations_with_replacement()
-
-`combinations_with_replacement(iterable, r)` finds all the possible combinations of the given iterable.
-The r keyword argument is used to specify the length of the tuples generated.
-The difference between this and `combinations()` is that it can repeat values.
-
-```python
->>> import itertools
->>> for combination in itertools.combinations_with_replacement("ABCD", 2):
-... print(combination, end=' ')
-...
-('A', 'A') ('A', 'B') ('A', 'C') ('A', 'D') ('B', 'B') ('B', 'C') ('B', 'D') ('C', 'C') ('C', 'D') ('D', 'D')
-```
-
-## Infinite iterators
-
-Most of iterator from the `itertools` module get exhausted after a time.
-But there are some that are infinite, these are known as infinte iterators.
-These iterators will will keep producing values until you tell them to stop.
-
-```exercism/note
-To avoid infinite loops, you can use `break` to end a loop.
-```
-
-### Count()
-
-`count(start, )` produces all values from the start value to infinity.
-Count also has an optional step parameter, which will produce values with a step size other than 1.
-
-```python
->>> import itertools
->>> for number in itertools.count(5, 2):
-... if number > 20:
-... break
-... else:
-... print(number, end=' ')
-...
-5 7 9 11 13 15 17 19
-```
-
-Giving `count()` a negative step size will produces values in a descending order.
-
-```python
->>> import itertools
->>> for number in itertools.count(5, -2):
-... if number < -20:
-... break
-... else:
-... print(number, end=' ')
-...
-5 3 1 -1 -3 -5 -7 -9 -11 -13 -15 -17 -19
-```
-
-### Cycle()
-
-`cycle(iterable)` produces all values from the iterable in an infinte loop.
-A `list`, `tuple`, `string`, `dict` or any other iterable can be used.
-
-```python
->>> import itertools
->>> number = 0
->>> for letter in itertools.cycle("ABC"):
-... if number == 10:
-... break
-... else:
-... print(letter, end=' ')
-... number += 1
-...
-A B C A B C A B C A
-```
-
-### Repeat()
-
-`repeat(object, )` produces the same value in an infinte loop.
-Although if the optional times parameter is given, the value will produces that many times.
-Meaning that it is not an infinite loop if that parameter is given.
-
-```python
->>> import itertools
->>> for number in itertools.repeat(5, 3):
-... print(number, end=' ')
-...
-5 5 5
-```
+#TODO: Add about for this concept.
diff --git a/concepts/itertools/links.json b/concepts/itertools/links.json
index 8a44906b3f8..eb5fb7c38a5 100644
--- a/concepts/itertools/links.json
+++ b/concepts/itertools/links.json
@@ -1,7 +1,7 @@
[
{
- "url": "https://docs.python.org/3/library/itertools.htm",
- "description": "Offical Python documentation for the itertools module."
+ "url": "http://example.com/",
+ "description": "TODO: add new link (above) and write a short description here of the resource."
},
{
"url": "http://example.com/",
diff --git a/exercises/practice/resistor-color-master/.meta/template.j2 b/exercises/practice/resistor-color-master/.meta/template.j2
index e9b458f34c3..86f9580c5fe 100644
--- a/exercises/practice/resistor-color-master/.meta/template.j2
+++ b/exercises/practice/resistor-color-master/.meta/template.j2
@@ -4,7 +4,11 @@
def test_{{ case["description"] | to_snake }}(self):
self.assertEqual(
{{ case["property"] | to_snake }}({{ case["input"]["colors"] }}),
- "{{ case['expected']['value']}} {{ case['expected']['unit']}} {{case['expected']['tolerance']}}{{"%" if case['expected']['tolerance'] else ""}}"
+ {%- if case['expected']['tolerance'] %}
+ "{{ case['expected']['value']}} {{ case['expected']['unit']}} {{case['expected']['tolerance']}}%"
+ {%- else -%}
+ "{{ case['expected']['value']}} {{ case['expected']['unit']}}"
+ {% endif %}
)
{%- endmacro %}
{{ macros.header()}}
From fe65f19b1cb4892ecdd72a08d160835073af25c3 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 26 Dec 2022 00:26:59 +0100
Subject: [PATCH 070/126] Delete about.md
---
concepts/itertools/about.md | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 concepts/itertools/about.md
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
deleted file mode 100644
index cbc5cd89d96..00000000000
--- a/concepts/itertools/about.md
+++ /dev/null
@@ -1 +0,0 @@
-#TODO: Add about for this concept.
From 4afba06f43420b7319489d6ef0a6e226ec36b042 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 26 Dec 2022 00:27:09 +0100
Subject: [PATCH 071/126] Delete config.json
---
concepts/itertools/.meta/config.json | 5 -----
1 file changed, 5 deletions(-)
delete mode 100644 concepts/itertools/.meta/config.json
diff --git a/concepts/itertools/.meta/config.json b/concepts/itertools/.meta/config.json
deleted file mode 100644
index 65190cc1ef5..00000000000
--- a/concepts/itertools/.meta/config.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "cmccandless"],
- "contributors": []
-}
\ No newline at end of file
From 9374285a09f3b7556ca2b1b4ec61fd5a46929168 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 00:29:55 +0100
Subject: [PATCH 072/126] some stuff
---
concepts/itertools/.meta/config.json | 5 +++++
concepts/itertools/introduction copy.md | 1 +
2 files changed, 6 insertions(+)
create mode 100644 concepts/itertools/.meta/config.json
create mode 100644 concepts/itertools/introduction copy.md
diff --git a/concepts/itertools/.meta/config.json b/concepts/itertools/.meta/config.json
new file mode 100644
index 00000000000..9b9e8da5a9b
--- /dev/null
+++ b/concepts/itertools/.meta/config.json
@@ -0,0 +1,5 @@
+{
+ "blurb": "TODO: add blurb for this concept",
+ "authors": ["bethanyg", "cmccandless"],
+ "contributors": []
+}
diff --git a/concepts/itertools/introduction copy.md b/concepts/itertools/introduction copy.md
new file mode 100644
index 00000000000..bbe12ffd5e9
--- /dev/null
+++ b/concepts/itertools/introduction copy.md
@@ -0,0 +1 @@
+#TODO: Add introduction for this concept.
From b4e47d3f0747be0658daaa9b1e7423c1a5b6d951 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 00:30:37 +0100
Subject: [PATCH 073/126] some more
---
concepts/itertools/{introduction copy.md => about.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename concepts/itertools/{introduction copy.md => about.md} (100%)
diff --git a/concepts/itertools/introduction copy.md b/concepts/itertools/about.md
similarity index 100%
rename from concepts/itertools/introduction copy.md
rename to concepts/itertools/about.md
From ac0acdde7f03a899297275cc1da867fa436f6727 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 26 Dec 2022 00:31:42 +0100
Subject: [PATCH 074/126] Update about.md
---
concepts/itertools/about.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
index bbe12ffd5e9..cbc5cd89d96 100644
--- a/concepts/itertools/about.md
+++ b/concepts/itertools/about.md
@@ -1 +1 @@
-#TODO: Add introduction for this concept.
+#TODO: Add about for this concept.
From 9d73b67a7e9a6408f443eae3052a674da0c0070c Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 26 Dec 2022 00:31:53 +0100
Subject: [PATCH 075/126] Update about.md
---
concepts/itertools/about.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/concepts/itertools/about.md b/concepts/itertools/about.md
index cbc5cd89d96..c628150d565 100644
--- a/concepts/itertools/about.md
+++ b/concepts/itertools/about.md
@@ -1 +1,2 @@
#TODO: Add about for this concept.
+
From 03216728fc80b1b48f11a8b01b37ac4d9e9acd14 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 00:33:31 +0100
Subject: [PATCH 076/126] fix
---
.../resistor-color-master/.meta/{hi.json => canonical-data.json} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename exercises/practice/resistor-color-master/.meta/{hi.json => canonical-data.json} (100%)
diff --git a/exercises/practice/resistor-color-master/.meta/hi.json b/exercises/practice/resistor-color-master/.meta/canonical-data.json
similarity index 100%
rename from exercises/practice/resistor-color-master/.meta/hi.json
rename to exercises/practice/resistor-color-master/.meta/canonical-data.json
From 85dcb967f51b71ab62e1ae20c1faa5beabf46d14 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 00:59:27 +0100
Subject: [PATCH 077/126] Updates
---
.../resistor-color-master/.meta/template.j2 | 2 +-
.../resistor_color_master.py | 49 +------------------
.../resistor_color_master_test.py | 18 +++----
3 files changed, 11 insertions(+), 58 deletions(-)
diff --git a/exercises/practice/resistor-color-master/.meta/template.j2 b/exercises/practice/resistor-color-master/.meta/template.j2
index 86f9580c5fe..b49a7f2fd1f 100644
--- a/exercises/practice/resistor-color-master/.meta/template.j2
+++ b/exercises/practice/resistor-color-master/.meta/template.j2
@@ -5,7 +5,7 @@
self.assertEqual(
{{ case["property"] | to_snake }}({{ case["input"]["colors"] }}),
{%- if case['expected']['tolerance'] %}
- "{{ case['expected']['value']}} {{ case['expected']['unit']}} {{case['expected']['tolerance']}}%"
+ "{{ case['expected']['value']}} {{ case['expected']['unit']}} ±{{case['expected']['tolerance']}}%"
{%- else -%}
"{{ case['expected']['value']}} {{ case['expected']['unit']}}"
{% endif %}
diff --git a/exercises/practice/resistor-color-master/resistor_color_master.py b/exercises/practice/resistor-color-master/resistor_color_master.py
index 83fd403e5c2..a4b658ecdd4 100644
--- a/exercises/practice/resistor-color-master/resistor_color_master.py
+++ b/exercises/practice/resistor-color-master/resistor_color_master.py
@@ -1,49 +1,2 @@
-COLORS = [
- 'black',
- 'brown',
- 'red',
- 'orange',
- 'yellow',
- 'green',
- 'blue',
- 'violet',
- 'grey',
- 'white'
-]
-
-COLORS_TOLERANCE = {
- 'brown': 1,
- 'red': 2,
- 'green': 0.5,
- 'blue': 0.25,
- 'violet': 0.1,
- 'grey': 0.05,
- 'gold': 5,
- 'silver': 10
-}
-
-
def label(colors):
- if len(colors) == 1:
- return f'0 ohms'
- elif len(colors) == 4:
- value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
- value *= 10 ** COLORS.index(colors[2])
- value, unit = color_code(value)
- value = int(value) if value.is_integer() else value
- return f'{value} {unit} {COLORS_TOLERANCE[colors[3]]}%'
- else:
- value = 100 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2])
- value *= 10 ** COLORS.index(colors[3])
- value, unit = color_code(value)
- value = int(value) if value.is_integer() else value
- return f'{value} {unit} {COLORS_TOLERANCE[colors[4]]}%'
-
-
-def color_code(color):
- if color < 1000:
- return color / 1, 'ohms'
- elif color < 1000000:
- return color / 1000, 'kiloohms'
- else:
- return color / 1000000, 'megaohms'
\ No newline at end of file
+pass
diff --git a/exercises/practice/resistor-color-master/resistor_color_master_test.py b/exercises/practice/resistor-color-master/resistor_color_master_test.py
index cd595fe4524..41f5a1f11a3 100644
--- a/exercises/practice/resistor-color-master/resistor_color_master_test.py
+++ b/exercises/practice/resistor-color-master/resistor_color_master_test.py
@@ -9,17 +9,17 @@
class ResistorColorMasterTest(unittest.TestCase):
def test_orange_orange_black_and_red(self):
- self.assertEqual(label(["orange", "orange", "black", "red"]), "33 ohms 2%")
+ self.assertEqual(label(["orange", "orange", "black", "red"]), "33 ohms ±2%")
def test_blue_grey_brown_and_violet(self):
- self.assertEqual(label(["blue", "grey", "brown", "violet"]), "680 ohms 0.1%")
+ self.assertEqual(label(["blue", "grey", "brown", "violet"]), "680 ohms ±0.1%")
def test_red_black_red_and_green(self):
- self.assertEqual(label(["red", "black", "red", "green"]), "2 kiloohms 0.5%")
+ self.assertEqual(label(["red", "black", "red", "green"]), "2 kiloohms ±0.5%")
def test_green_brown_orange_and_grey(self):
self.assertEqual(
- label(["green", "brown", "orange", "grey"]), "51 kiloohms 0.05%"
+ label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%"
)
def test_one_black_band(self):
@@ -27,25 +27,25 @@ def test_one_black_band(self):
def test_orange_orange_yellow_black_and_brown(self):
self.assertEqual(
- label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms 1%"
+ label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%"
)
def test_red_green_yellow_yellow_and_brown(self):
self.assertEqual(
- label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms 1%"
+ label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%"
)
def test_blue_grey_white_red_and_brown(self):
self.assertEqual(
- label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms 1%"
+ label(["blue", "grey", "white", "red", "brown"]), "6.89 kiloohms ±1%"
)
def test_violet_orange_red_and_grey(self):
self.assertEqual(
- label(["violet", "orange", "red", "grey"]), "7.3 kiloohms 0.05%"
+ label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%"
)
def test_brown_red_orange_green_and_blue(self):
self.assertEqual(
- label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms 0.25%"
+ label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%"
)
From 0baf1fedae7fe0a501c441037b427ba50e491a9c Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 01:01:45 +0100
Subject: [PATCH 078/126] fix
---
.../resistor-color-master/resistor_color_master_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/resistor-color-master/resistor_color_master_test.py b/exercises/practice/resistor-color-master/resistor_color_master_test.py
index 41f5a1f11a3..27ac0f82a87 100644
--- a/exercises/practice/resistor-color-master/resistor_color_master_test.py
+++ b/exercises/practice/resistor-color-master/resistor_color_master_test.py
@@ -37,7 +37,7 @@ def test_red_green_yellow_yellow_and_brown(self):
def test_blue_grey_white_red_and_brown(self):
self.assertEqual(
- label(["blue", "grey", "white", "red", "brown"]), "6.89 kiloohms ±1%"
+ label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%"
)
def test_violet_orange_red_and_grey(self):
From ae349aeed63ca425fe2f1a813974dee51d99c36d Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 01:03:42 +0100
Subject: [PATCH 079/126] fix
---
.../practice/resistor-color-master/resistor_color_master.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/resistor-color-master/resistor_color_master.py b/exercises/practice/resistor-color-master/resistor_color_master.py
index a4b658ecdd4..1d36841cf10 100644
--- a/exercises/practice/resistor-color-master/resistor_color_master.py
+++ b/exercises/practice/resistor-color-master/resistor_color_master.py
@@ -1,2 +1,2 @@
def label(colors):
-pass
+ pass
From 216d0ad83e2bf311dd3047f5cc21d698f85732ac Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 01:05:51 +0100
Subject: [PATCH 080/126] Fix
---
exercises/practice/resistor-color-master/.meta/example.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/resistor-color-master/.meta/example.py b/exercises/practice/resistor-color-master/.meta/example.py
index 83fd403e5c2..b0d1d612b9d 100644
--- a/exercises/practice/resistor-color-master/.meta/example.py
+++ b/exercises/practice/resistor-color-master/.meta/example.py
@@ -31,13 +31,13 @@ def label(colors):
value *= 10 ** COLORS.index(colors[2])
value, unit = color_code(value)
value = int(value) if value.is_integer() else value
- return f'{value} {unit} {COLORS_TOLERANCE[colors[3]]}%'
+ return f'{value} {unit} ±{COLORS_TOLERANCE[colors[3]]}%'
else:
value = 100 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2])
value *= 10 ** COLORS.index(colors[3])
value, unit = color_code(value)
value = int(value) if value.is_integer() else value
- return f'{value} {unit} {COLORS_TOLERANCE[colors[4]]}%'
+ return f'{value} {unit} ±{COLORS_TOLERANCE[colors[4]]}%'
def color_code(color):
From b47657bf90ed025f33f3a7cabc82aebc52e4a5e5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 15 Jun 2023 21:16:37 +0200
Subject: [PATCH 081/126] Renamed the exercise
---
config.json | 4 +-
.../.docs/instructions.md | 76 +++++++++
.../.docs/introduction.md | 10 ++
.../resistor-color-expert/.meta/config.json | 18 +++
.../.meta/example.py | 2 +-
.../.meta/tests.toml | 0
.../resistor_color_expert.py | 2 +
.../resistor_color_expert_test.py | 51 ++++++
.../.docs/instructions.md | 86 ----------
.../.meta/canonical-data.json | 147 ------------------
.../resistor-color-master/.meta/config.json | 20 ---
.../resistor-color-master/.meta/template.j2 | 19 ---
.../resistor_color_master.py | 2 -
.../resistor_color_master_test.py | 51 ------
14 files changed, 160 insertions(+), 328 deletions(-)
create mode 100644 exercises/practice/resistor-color-expert/.docs/instructions.md
create mode 100644 exercises/practice/resistor-color-expert/.docs/introduction.md
create mode 100644 exercises/practice/resistor-color-expert/.meta/config.json
rename exercises/practice/{resistor-color-master => resistor-color-expert}/.meta/example.py (97%)
rename exercises/practice/{resistor-color-master => resistor-color-expert}/.meta/tests.toml (100%)
create mode 100644 exercises/practice/resistor-color-expert/resistor_color_expert.py
create mode 100644 exercises/practice/resistor-color-expert/resistor_color_expert_test.py
delete mode 100644 exercises/practice/resistor-color-master/.docs/instructions.md
delete mode 100644 exercises/practice/resistor-color-master/.meta/canonical-data.json
delete mode 100644 exercises/practice/resistor-color-master/.meta/config.json
delete mode 100644 exercises/practice/resistor-color-master/.meta/template.j2
delete mode 100644 exercises/practice/resistor-color-master/resistor_color_master.py
delete mode 100644 exercises/practice/resistor-color-master/resistor_color_master_test.py
diff --git a/config.json b/config.json
index e9a513fafb8..e8c7f2f505f 100644
--- a/config.json
+++ b/config.json
@@ -971,8 +971,8 @@
"difficulty": 3
},
{
- "slug": "resistor-color-master",
- "name": "Resistor Color Master",
+ "slug": "resistor-color-expert",
+ "name": "Resistor Color Expert",
"uuid": "8a738365-0efa-444f-9466-a757ddaddcdb",
"practices": ["list-methods"],
"prerequisites": [
diff --git a/exercises/practice/resistor-color-expert/.docs/instructions.md b/exercises/practice/resistor-color-expert/.docs/instructions.md
new file mode 100644
index 00000000000..f08801644c5
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.docs/instructions.md
@@ -0,0 +1,76 @@
+# Instructions
+
+In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands.
+The program will take 1, 4, or 5 colors as input, and outputs the correct value, in ohms.
+The color bands are encoded as follows:
+
+- Black: 0
+- Brown: 1
+- Red: 2
+- Orange: 3
+- Yellow: 4
+- Green: 5
+- Blue: 6
+- Violet: 7
+- Grey: 8
+- White: 9
+
+In `resistor-color trio` you decoded the first three colors.
+For instance: orange-orange-brown translated to the main value `330`.
+In this exercise you will need to add _tolerance_ to the mix.
+Tolerance is the maximum amount that a value can be above or below the main value.
+For example, if the last band is green, the maximum tolerance will be ±0.5%.
+
+The tolerance band will have one of these values:
+
+- Grey - 0.05%
+- Violet - 0.1%
+- Blue - 0.25%
+- Green - 0.5%
+- Brown - 1%
+- Red - 2%
+- Gold - 5%
+- Silver - 10%
+
+The four-band resistor is built up like this:
+
+| Band_1 | Band_2 | Band_3 | band_4 |
+| ------- | ------- | ---------- | --------- |
+| Value_1 | Value_2 | Multiplier | Tolerance |
+
+Meaning
+
+- orange-orange-brown-green would be 330 ohms with a ±0.5% tolerance.
+- orange-orange-red would-grey would be 3300 ohms with ±0.005% tolerance.
+The difference between a four and five-band resistor is that the five-band resistor has an extra band to indicate a more precise value.
+
+| Band_1 | Band_2 | Band_3 | Band_4 | band_5 |
+| ------- | ------- | ------- | ---------- | --------- |
+| Value_1 | Value_2 | Value_3 | Multiplier | Tolerance |
+
+Meaning
+
+- orange-orange-orange-black-green would be 330 ohms with a ±0.5% tolerance.
+There are also one band resistors.
+This type of resistor only has the color black and has a value of 0.
+
+This exercise is about translating the resistor band colors into a label:
+
+"... ohms ...%"
+
+So an input of "orange", "orange", "black, green" should return:
+
+"33 ohms ±0.5%"
+
+When there are more than a thousand ohms, we say "kiloohms".
+ That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams.
+
+So an input of "orange", "orange", "orange", grey should return:
+
+"33 kiloohms ±0.05%"
+
+When there are more than a million ohms, we say "megaohms".
+
+So an input of "orange", "orange", "orange", "red" should return:
+
+"33 megaohms ±2%"
diff --git a/exercises/practice/resistor-color-expert/.docs/introduction.md b/exercises/practice/resistor-color-expert/.docs/introduction.md
new file mode 100644
index 00000000000..1c95695d135
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.docs/introduction.md
@@ -0,0 +1,10 @@
+# Introduction
+
+If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
+For this exercise, you need to be able to use 1, 4, and 5 band resistors:
+
+- Each resistor has a resistance value.
+- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read.
+ To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values.
+- Each band acts as a digit of a number.
+ For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15.
diff --git a/exercises/practice/resistor-color-expert/.meta/config.json b/exercises/practice/resistor-color-expert/.meta/config.json
new file mode 100644
index 00000000000..66d1121b6a2
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.meta/config.json
@@ -0,0 +1,18 @@
+{
+ "authors": [
+ "meatball133",
+ "bethanyg"
+ ],
+ "files": {
+ "solution": [
+ "resistor_color_expert.py"
+ ],
+ "test": [
+ "resistor_color_expert_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "blurb": "Convert color codes, as used on resistors with different amounts of bands, to a human-readable label."
+}
diff --git a/exercises/practice/resistor-color-master/.meta/example.py b/exercises/practice/resistor-color-expert/.meta/example.py
similarity index 97%
rename from exercises/practice/resistor-color-master/.meta/example.py
rename to exercises/practice/resistor-color-expert/.meta/example.py
index b0d1d612b9d..0fd6e42fcd9 100644
--- a/exercises/practice/resistor-color-master/.meta/example.py
+++ b/exercises/practice/resistor-color-expert/.meta/example.py
@@ -23,7 +23,7 @@
}
-def label(colors):
+def resistor_label(colors):
if len(colors) == 1:
return f'0 ohms'
elif len(colors) == 4:
diff --git a/exercises/practice/resistor-color-master/.meta/tests.toml b/exercises/practice/resistor-color-expert/.meta/tests.toml
similarity index 100%
rename from exercises/practice/resistor-color-master/.meta/tests.toml
rename to exercises/practice/resistor-color-expert/.meta/tests.toml
diff --git a/exercises/practice/resistor-color-expert/resistor_color_expert.py b/exercises/practice/resistor-color-expert/resistor_color_expert.py
new file mode 100644
index 00000000000..f36881c5a0e
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/resistor_color_expert.py
@@ -0,0 +1,2 @@
+def resistor_label(colors):
+ pass
diff --git a/exercises/practice/resistor-color-expert/resistor_color_expert_test.py b/exercises/practice/resistor-color-expert/resistor_color_expert_test.py
new file mode 100644
index 00000000000..8d17a36409a
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/resistor_color_expert_test.py
@@ -0,0 +1,51 @@
+import unittest
+
+from resistor_color_expert import (
+ resistor_label,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class ResistorColorMasterTest(unittest.TestCase):
+ def test_orange_orange_black_and_red(self):
+ self.assertEqual(resistor_label(["orange", "orange", "black", "red"]), "33 ohms ±2%")
+
+ def test_blue_grey_brown_and_violet(self):
+ self.assertEqual(resistor_label(["blue", "grey", "brown", "violet"]), "680 ohms ±0.1%")
+
+ def test_red_black_red_and_green(self):
+ self.assertEqual(resistor_label(["red", "black", "red", "green"]), "2 kiloohms ±0.5%")
+
+ def test_green_brown_orange_and_grey(self):
+ self.assertEqual(
+ resistor_label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%"
+ )
+
+ def test_one_black_band(self):
+ self.assertEqual(resistor_label(["black"]), "0 ohms")
+
+ def test_orange_orange_yellow_black_and_brown(self):
+ self.assertEqual(
+ resistor_label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%"
+ )
+
+ def test_red_green_yellow_yellow_and_brown(self):
+ self.assertEqual(
+ resistor_label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%"
+ )
+
+ def test_blue_grey_white_red_and_brown(self):
+ self.assertEqual(
+ resistor_label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%"
+ )
+
+ def test_violet_orange_red_and_grey(self):
+ self.assertEqual(
+ resistor_label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%"
+ )
+
+ def test_brown_red_orange_green_and_blue(self):
+ self.assertEqual(
+ resistor_label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%"
+ )
diff --git a/exercises/practice/resistor-color-master/.docs/instructions.md b/exercises/practice/resistor-color-master/.docs/instructions.md
deleted file mode 100644
index 8263897d07f..00000000000
--- a/exercises/practice/resistor-color-master/.docs/instructions.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# Description
-
-If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
-For this exercise, you need to be able to use 1, 4, and 5 bands resistor:
-
-- Each resistor has a resistance value.
-- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read.
- To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values.
-- Each band acts as a digit of a number.
- For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15.
- In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands.
- The program will take 1, 4, or 5 colors as input, and outputs the correct value, in ohms.
- The color bands are encoded as follows:
-
-- Black: 0
-- Brown: 1
-- Red: 2
-- Orange: 3
-- Yellow: 4
-- Green: 5
-- Blue: 6
-- Violet: 7
-- Grey: 8
-- White: 9
-
-In `resistor-color trio` you decoded the first three colors.
-For instance: orange-orange-brown got the main value `330`.
-In this exercise you need to add tolerance to the mix.
-Tolerance is the maximum amount that the value can be above or below the main value.
-The tolerance value is always the last band.
-For example, if the last band is green, the maximum tolerance will be 0.5%.
-The tolerance band will have one of these values:
-
-- Grey - 0.05%
-- Violet - 0.1%
-- Blue - 0.25%
-- Green - 0.5%
-- Brown - 1%
-- Red - 2%
-- Gold - 5%
-- Silver - 10%
-
-The fourth band resistor is built up like these:
-
-| Band_1 | Band_2 | Band_3 | band_4 |
-| ------- | ------- | ---------- | --------- |
-| Value_1 | Value_2 | Multiplier | Tolerance |
-
-Meaning
-
-- orange-orange-brown-green would be 330 ohms with a 0.5% tolerance.
-- orange-orange-red would-grey would be 3300 ohms with 0.005 tolerance.
-
-The difference between the 4 and 5 band resistor is that there is an extra band for value a more precises value.
-
-| Band_1 | Band_2 | Band_3 | Band_4 | band_5 |
-| ------- | ------- | ------- | ---------- | --------- |
-| Value_1 | Value_2 | Value_3 | Multiplier | Tolerance |
-
-Meaning
-
-- orange-orange-orange-black-green would be 330 ohms with a 0.5% tolerance.
-
-There will also be a one band resistor.
-This resistor will only have the color black and have the value 0.
-
-This exercise is about translating the colors into a label:
-
-> "... ohms ...%"
-
-So an input of `"orange", "orange", "black, green"` should return:
-
-> "33 ohms 0.5%"
-
-When we get more than a thousand ohms, we say "kiloohms".
-That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams.
-
-So an input of `"orange", "orange", "orange", grey` should return:
-
-> "33 kiloohms 0.05%"
-
-When we get more than a million ohms, we say "megaohms".
-
-So an input of `"orange", "orange", "orange", "red"` should return:
-
-> "33 megaohms 2%"
diff --git a/exercises/practice/resistor-color-master/.meta/canonical-data.json b/exercises/practice/resistor-color-master/.meta/canonical-data.json
deleted file mode 100644
index b25a6b1acaf..00000000000
--- a/exercises/practice/resistor-color-master/.meta/canonical-data.json
+++ /dev/null
@@ -1,147 +0,0 @@
-{
- "exercise": "resistor-color-master",
- "cases": [
- {
- "uuid": "8c4f9fb6-d477-4250-bc57-b325d2be226f",
- "description": "Orange, orange, black, and red",
- "property": "label",
- "input": {
- "colors": ["orange", "orange", "black", "red"]
- },
- "expected": {
- "value": 33,
- "tolerance": 2,
- "unit": "ohms"
- }
- },
- {
- "uuid": "d1d4a769-9210-43cc-9a14-6af6ce4c0b00",
- "description": "Blue, grey, brown, and violet",
- "property": "label",
- "input": {
- "colors": ["blue", "grey", "brown", "violet"]
- },
- "expected": {
- "value": 680,
- "tolerance": 0.1,
- "unit": "ohms"
- }
- },
- {
- "uuid": "6af91bc3-8275-4c38-920a-185d30feb5f3",
- "description": "Red, black, red, and green",
- "property": "label",
- "input": {
- "colors": ["red", "black", "red", "green"]
- },
- "expected": {
- "value": 2,
- "tolerance": 0.5,
- "unit": "kiloohms"
- }
- },
- {
- "uuid": "9c4630bf-0dda-4212-baca-2f5111530b4d",
- "description": "Green, brown, orange, and grey",
- "property": "label",
- "input": {
- "colors": ["green", "brown", "orange", "grey"]
- },
- "expected": {
- "value": 51,
- "tolerance": 0.05,
- "unit": "kiloohms"
- }
- },
- {
- "uuid": "93b7d0ec-39fb-49f3-bb88-2dd4b8c293af",
- "description": "Yellow, violet, yellow, and blue",
- "property": "label",
- "input": {
- "colors": ["yellow", "violet", "yellow", "blue"]
- },
- "expected": {
- "value": 470,
- "tolerance": 0.25,
- "unit": "kiloohms"
- }
- },
- {
- "uuid": "5880ddf1-0dc6-4bd0-b9de-5626117cd2c7",
- "description": "One black band",
- "property": "label",
- "input": {
- "colors": ["black"]
- },
- "expected": {
- "value": 0,
- "unit": "ohms"
- }
- },
- {
- "uuid": "a5cfda34-3c02-4bda-b183-726791fb43b2",
- "description": "Orange, orange, yellow, black, and brown",
- "property": "label",
- "input": {
- "colors": ["orange", "orange", "yellow", "black", "brown"]
- },
- "expected": {
- "value": 334,
- "tolerance": 1,
- "unit": "ohms"
- }
- },
- {
- "uuid": "4f0ad96c-cdab-4c84-95dd-7074e889e001",
- "description": "Red, green, yellow, yellow, and brown",
- "property": "label",
- "input": {
- "colors": ["red", "green", "yellow", "yellow", "brown"]
- },
- "expected": {
- "value": 2.54,
- "tolerance": 1,
- "unit": "megaohms"
- }
- },
- {
- "uuid": "48c66841-208c-46a7-8992-1e84a2eda9e2",
- "description": "Blue, grey, white, brown, and brown",
- "property": "label",
- "input": {
- "colors": ["blue", "grey", "white", "brown", "brown"]
- },
- "expected": {
- "value": 6.89,
- "tolerance": 1,
- "unit": "kiloohms"
- }
- },
- {
- "uuid": "4f3aeb0c-fd9d-4cbd-b204-554e61978f73",
- "description": "Violet, orange, red, and grey",
- "property": "label",
- "input": {
- "colors": ["violet", "orange", "red", "grey"]
- },
- "expected": {
- "value": 7.3,
- "tolerance": 0.05,
- "unit": "kiloohms"
- }
- },
- {
- "uuid": "1ae3a17f-8bc0-449c-9831-fddfd2d91c5d",
- "description": "Brown, red, orange, green, and blue",
- "property": "label",
- "input": {
- "colors": ["brown", "red", "orange", "green", "blue"]
- },
- "expected": {
- "value": 12.3,
- "tolerance": 0.25,
- "unit": "megaohms"
- }
- }
- ]
-}
diff --git a/exercises/practice/resistor-color-master/.meta/config.json b/exercises/practice/resistor-color-master/.meta/config.json
deleted file mode 100644
index 23a09a2cdd9..00000000000
--- a/exercises/practice/resistor-color-master/.meta/config.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "authors": [
- "meatball133",
- "bethanyg"
- ],
- "files": {
- "solution": [
- "resistor_color_master.py"
- ],
- "test": [
- "resistor_color_master_test.py"
- ],
- "example": [
- ".meta/example.py"
- ]
- },
- "blurb": "Convert color codes, as used on resistors, to a human-readable label.",
- "source": "Maud de Vries, Erik Schierboom",
- "source_url": "https://github.com/exercism/problem-specifications/issues/1549"
-}
diff --git a/exercises/practice/resistor-color-master/.meta/template.j2 b/exercises/practice/resistor-color-master/.meta/template.j2
deleted file mode 100644
index b49a7f2fd1f..00000000000
--- a/exercises/practice/resistor-color-master/.meta/template.j2
+++ /dev/null
@@ -1,19 +0,0 @@
-{%- import "generator_macros.j2" as macros with context -%}
-{% macro test_case(case) -%}
- {%- set input = case["input"] -%}
- def test_{{ case["description"] | to_snake }}(self):
- self.assertEqual(
- {{ case["property"] | to_snake }}({{ case["input"]["colors"] }}),
- {%- if case['expected']['tolerance'] %}
- "{{ case['expected']['value']}} {{ case['expected']['unit']}} ±{{case['expected']['tolerance']}}%"
- {%- else -%}
- "{{ case['expected']['value']}} {{ case['expected']['unit']}}"
- {% endif %}
- )
-{%- endmacro %}
-{{ macros.header()}}
-
-class {{ exercise | camel_case }}Test(unittest.TestCase):
- {% for case in cases -%}
- {{ test_case(case) }}
- {% endfor %}
diff --git a/exercises/practice/resistor-color-master/resistor_color_master.py b/exercises/practice/resistor-color-master/resistor_color_master.py
deleted file mode 100644
index 1d36841cf10..00000000000
--- a/exercises/practice/resistor-color-master/resistor_color_master.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def label(colors):
- pass
diff --git a/exercises/practice/resistor-color-master/resistor_color_master_test.py b/exercises/practice/resistor-color-master/resistor_color_master_test.py
deleted file mode 100644
index 27ac0f82a87..00000000000
--- a/exercises/practice/resistor-color-master/resistor_color_master_test.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import unittest
-
-from resistor_color_master import (
- label,
-)
-
-# Tests adapted from `problem-specifications//canonical-data.json`
-
-
-class ResistorColorMasterTest(unittest.TestCase):
- def test_orange_orange_black_and_red(self):
- self.assertEqual(label(["orange", "orange", "black", "red"]), "33 ohms ±2%")
-
- def test_blue_grey_brown_and_violet(self):
- self.assertEqual(label(["blue", "grey", "brown", "violet"]), "680 ohms ±0.1%")
-
- def test_red_black_red_and_green(self):
- self.assertEqual(label(["red", "black", "red", "green"]), "2 kiloohms ±0.5%")
-
- def test_green_brown_orange_and_grey(self):
- self.assertEqual(
- label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%"
- )
-
- def test_one_black_band(self):
- self.assertEqual(label(["black"]), "0 ohms")
-
- def test_orange_orange_yellow_black_and_brown(self):
- self.assertEqual(
- label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%"
- )
-
- def test_red_green_yellow_yellow_and_brown(self):
- self.assertEqual(
- label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%"
- )
-
- def test_blue_grey_white_red_and_brown(self):
- self.assertEqual(
- label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%"
- )
-
- def test_violet_orange_red_and_grey(self):
- self.assertEqual(
- label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%"
- )
-
- def test_brown_red_orange_green_and_blue(self):
- self.assertEqual(
- label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%"
- )
From b0d3055d9794b27b1691ef7ea5163a4f9960906e Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 15 Jun 2023 21:58:36 +0200
Subject: [PATCH 082/126] Update based on feedback
---
exercises/practice/resistor-color-expert/.docs/introduction.md | 2 +-
exercises/practice/resistor-color-expert/.meta/config.json | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/resistor-color-expert/.docs/introduction.md b/exercises/practice/resistor-color-expert/.docs/introduction.md
index 1c95695d135..fd9e05efc4d 100644
--- a/exercises/practice/resistor-color-expert/.docs/introduction.md
+++ b/exercises/practice/resistor-color-expert/.docs/introduction.md
@@ -1,7 +1,7 @@
# Introduction
If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
-For this exercise, you need to be able to use 1, 4, and 5 band resistors:
+Like the previous `Resistor Color Duo` and `Resistor Color Trio` exercises, you will be translating resistor color bands to human-readable labels.
- Each resistor has a resistance value.
- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read.
diff --git a/exercises/practice/resistor-color-expert/.meta/config.json b/exercises/practice/resistor-color-expert/.meta/config.json
index 66d1121b6a2..1450661bb0d 100644
--- a/exercises/practice/resistor-color-expert/.meta/config.json
+++ b/exercises/practice/resistor-color-expert/.meta/config.json
@@ -14,5 +14,6 @@
".meta/example.py"
]
},
- "blurb": "Convert color codes, as used on resistors with different amounts of bands, to a human-readable label."
+ "blurb": "Convert color codes as used on resistors with different bands to a human-readable label.",
+ "source": "Based on earlier resistor color exercises made by Erik Schierboom and Maud de Vries"
}
From 65a81612c8639b466019e2be4ef538aff889cede Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 15 Jun 2023 13:05:21 -0700
Subject: [PATCH 083/126] Update
exercises/practice/resistor-color-expert/.meta/config.json
---
exercises/practice/resistor-color-expert/.meta/config.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/resistor-color-expert/.meta/config.json b/exercises/practice/resistor-color-expert/.meta/config.json
index 1450661bb0d..e2f35368a54 100644
--- a/exercises/practice/resistor-color-expert/.meta/config.json
+++ b/exercises/practice/resistor-color-expert/.meta/config.json
@@ -16,4 +16,5 @@
},
"blurb": "Convert color codes as used on resistors with different bands to a human-readable label.",
"source": "Based on earlier resistor color exercises made by Erik Schierboom and Maud de Vries"
+ "source_url": "https://github.com/exercism/problem-specifications/issues/1464"
}
From 89938cce4c8c52cf3d862fca90e26663f7cc4cf8 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 15 Jun 2023 22:09:18 +0200
Subject: [PATCH 084/126] Fixed config.json
---
exercises/practice/resistor-color-expert/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/resistor-color-expert/.meta/config.json b/exercises/practice/resistor-color-expert/.meta/config.json
index e2f35368a54..edf50b4a1e1 100644
--- a/exercises/practice/resistor-color-expert/.meta/config.json
+++ b/exercises/practice/resistor-color-expert/.meta/config.json
@@ -15,6 +15,6 @@
]
},
"blurb": "Convert color codes as used on resistors with different bands to a human-readable label.",
- "source": "Based on earlier resistor color exercises made by Erik Schierboom and Maud de Vries"
+ "source": "Based on earlier resistor color exercises made by Erik Schierboom and Maud de Vries",
"source_url": "https://github.com/exercism/problem-specifications/issues/1464"
}
From 79ca2dc4ab83e1c4e508a5439dce34e2f99d5a1c Mon Sep 17 00:00:00 2001
From: Safwan Samsudeen
Date: Mon, 12 Jun 2023 09:37:58 +0530
Subject: [PATCH 085/126] author intro + two approaches
---
.../practice/sublist/.approaches/config.json | 21 +++++++
.../sublist/.approaches/introduction.md | 58 +++++++++++++++++++
.../.approaches/list-manipulation/content.md | 38 ++++++++++++
.../.approaches/list-manipulation/snippet.txt | 8 +++
.../.approaches/using-strings/content.md | 27 +++++++++
.../.approaches/using-strings/snippet.txt | 8 +++
6 files changed, 160 insertions(+)
create mode 100644 exercises/practice/sublist/.approaches/config.json
create mode 100644 exercises/practice/sublist/.approaches/introduction.md
create mode 100644 exercises/practice/sublist/.approaches/list-manipulation/content.md
create mode 100644 exercises/practice/sublist/.approaches/list-manipulation/snippet.txt
create mode 100644 exercises/practice/sublist/.approaches/using-strings/content.md
create mode 100644 exercises/practice/sublist/.approaches/using-strings/snippet.txt
diff --git a/exercises/practice/sublist/.approaches/config.json b/exercises/practice/sublist/.approaches/config.json
new file mode 100644
index 00000000000..1c5eb3838b6
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/config.json
@@ -0,0 +1,21 @@
+{
+ "introduction": {
+ "authors": ["safwansamsudeen"]
+ },
+ "approaches": [
+ {
+ "uuid": "3593cfe3-5cab-4141-b0a2-329148a66bb6",
+ "slug": "list-manipulations",
+ "title": "List manipulation",
+ "blurb": "Manipulate and check lists to solve the exercise",
+ "authors": ["safwansamsudeen"]
+ },
+ {
+ "uuid": "eccd1e1e-6c88-4823-9b25-944eccaa92e7",
+ "slug": "using-strings",
+ "title": "Using strings",
+ "blurb": "Convert the lists to string and use string manipulation to solve the exercise",
+ "authors": ["safwansamsudeen"]
+ }
+ ]
+}
diff --git a/exercises/practice/sublist/.approaches/introduction.md b/exercises/practice/sublist/.approaches/introduction.md
new file mode 100644
index 00000000000..abfcfb70c3a
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/introduction.md
@@ -0,0 +1,58 @@
+# Introduction
+There are two broad ways to solve Sublist.
+
+## General guidance
+To write the code, you need to branch out (probably with `if`) into the four different possible conditions, and return the appropriate name of the category.
+
+## Approach: list manipulation
+The direct approach would be to manipulate and check the given lists to solve this.
+This solution uses a helper function, which simplifies things, but the approach can be implemented without it.
+
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def check_sub_sequences(list_one, list_two):
+ n1 = len(list_one)
+ n2 = len(list_two)
+ return any(list_two[i:i+n1] == list_one for i in range(n2 - n1 + 1))
+
+def sublist(list_one, list_two):
+ if list_one == list_two:
+ return EQUAL
+ if check_sub_sequences(list_one, list_two):
+ return SUBLIST
+ if check_sub_sequences(list_two, list_one):
+ return SUPERLIST
+ return UNEQUAL
+```
+
+Read more on the [detail of this approach][approach-list-manipulation].
+
+## Approach: using strings
+Another clever approach is to convert the lists to strings and then use the `in` operator to check for sub-sequences.
+Note that this approach is not as performant as the previous one.
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def sublist(list_one, list_two):
+ list_one_check = (str(list_one).strip("[]") + ",")
+ list_two_check = (str(list_two).strip("[]") + ",")
+
+ if list_one_check == list_two_check:
+ return EQUAL
+ elif list_one_check in list_two_check:
+ return SUBLIST
+ elif list_two_check in list_one_check:
+ return SUPERLIST
+ return UNEQUAL
+```
+To understand more about this approach, [read here][approach-using-strings]
+
+[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation
+[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/list-manipulation/content.md b/exercises/practice/sublist/.approaches/list-manipulation/content.md
new file mode 100644
index 00000000000..ac374b730e7
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/list-manipulation/content.md
@@ -0,0 +1,38 @@
+# List manipulation
+The direct approach would be to manipulate and check the given lists to solve this.
+This solution uses a helper function, which simplifies things, but the approach can be implemented without it.
+
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def check_sub_sequences(list_one, list_two):
+ n1 = len(list_one)
+ n2 = len(list_two)
+ return any(list_two[i:i+n1] == list_one for i in range(n2 - n1 + 1))
+
+def sublist(list_one, list_two):
+ if list_one == list_two:
+ return EQUAL
+ if check_sub_sequences(list_one, list_two):
+ return SUBLIST
+ if check_sub_sequences(list_two, list_one):
+ return SUPERLIST
+ return UNEQUAL
+```
+
+We first check for equality using the `==` operator, if so, then we return `EQUAL`.
+A common way to do this differently would be to return `1` directly, but this is better practice as we [remove magic values][magic values].
+
+After that we call `check_sub_sequences` passing in `list_one` and `list_two`.
+In the helper function, we check if `any` of the possible sub-sequences in `list_two` of length `n1` (the length of the first list) are equal to the first list.
+If so, then we conclude that `list_one` is a `SUBLIST` of `list_two`.
+
+To find whether `list_one` is a `SUPERLIST` of `list_two`, we just reverse this process - pass in the lists in the opposite order.
+Thus, we check if `any` of the possible sub-sequences in `list_one` of length `n2` (the length of the second list) are equal to the second list.
+
+If none of the above conditions are true, we conclude that the two lists are unequal.
+
+[magic values]: https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/list-manipulation/snippet.txt b/exercises/practice/sublist/.approaches/list-manipulation/snippet.txt
new file mode 100644
index 00000000000..290f8cdd2b8
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/list-manipulation/snippet.txt
@@ -0,0 +1,8 @@
+def sublist(list_one, list_two):
+ if list_one == list_two:
+ return EQUAL
+ if check_sub_sequences(list_one, list_two):
+ return SUBLIST
+ if check_sub_sequences(list_two, list_one):
+ return SUPERLIST
+ return UNEQUAL
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/using-strings/content.md b/exercises/practice/sublist/.approaches/using-strings/content.md
new file mode 100644
index 00000000000..48ac206fd9c
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/using-strings/content.md
@@ -0,0 +1,27 @@
+# Using strings
+
+Another clever solution is to convert the lists to strings and then use the `in` operator to check for sub-sequences.
+Note that this approach is not as performant as the previous one.
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def sublist(list_one, list_two):
+ list_one_check = (str(list_one).strip("[]") + ",")
+ list_two_check = (str(list_two).strip("[]") + ",")
+
+ if list_one_check == list_two_check:
+ return EQUAL
+ elif list_one_check in list_two_check:
+ return SUBLIST
+ elif list_two_check in list_one_check:
+ return SUPERLIST
+ return UNEQUAL
+```
+Note that we can't use `.join` as it only accepts strings inside the iterable, while there the test cases have integers.
+
+In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"` so that there's a consistent pattern of number + comma while using the `in` operator.
+
+We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`.
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/using-strings/snippet.txt b/exercises/practice/sublist/.approaches/using-strings/snippet.txt
new file mode 100644
index 00000000000..ff7a2563bec
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/using-strings/snippet.txt
@@ -0,0 +1,8 @@
+def sublist(list_one, list_two):
+ list_one_check = (str(list_one).strip("[]") + ",")
+ list_two_check = (str(list_two).strip("[]") + ",")
+ ...
+ elif list_one_check in list_two_check:
+ return SUBLIST
+ ...
+ return UNEQUAL
\ No newline at end of file
From 71998301caf084dea6121c1f69430bb6a75bf629 Mon Sep 17 00:00:00 2001
From: Safwan Samsudeen
Date: Mon, 12 Jun 2023 09:52:35 +0530
Subject: [PATCH 086/126] minor corrections
---
exercises/practice/sublist/.approaches/introduction.md | 2 +-
.../practice/sublist/.approaches/using-strings/content.md | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/sublist/.approaches/introduction.md b/exercises/practice/sublist/.approaches/introduction.md
index abfcfb70c3a..8cd30870cca 100644
--- a/exercises/practice/sublist/.approaches/introduction.md
+++ b/exercises/practice/sublist/.approaches/introduction.md
@@ -55,4 +55,4 @@ def sublist(list_one, list_two):
To understand more about this approach, [read here][approach-using-strings]
[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation
-[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings
\ No newline at end of file
+[approach-using-strings]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/using-strings/content.md b/exercises/practice/sublist/.approaches/using-strings/content.md
index 48ac206fd9c..f49f59309cb 100644
--- a/exercises/practice/sublist/.approaches/using-strings/content.md
+++ b/exercises/practice/sublist/.approaches/using-strings/content.md
@@ -20,8 +20,11 @@ def sublist(list_one, list_two):
return SUPERLIST
return UNEQUAL
```
-Note that we can't use `.join` as it only accepts strings inside the iterable, while there the test cases have integers.
+Note that we can't use directly `.join` as it only accepts strings inside the iterable, while there the test cases have integers.
+However, if one wanted to use it, we could use `map` or a [generator expression][gen-exp] inside `.join`.
In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"` so that there's a consistent pattern of number + comma while using the `in` operator.
-We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`.
\ No newline at end of file
+We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`.
+
+[gen-exp]: https://www.programiz.com/python-programming/generator
\ No newline at end of file
From 50069bc2f512855b3807a66f82e79e66a9cb556f Mon Sep 17 00:00:00 2001
From: Safwan Samsudeen
Date: Mon, 12 Jun 2023 09:54:31 +0530
Subject: [PATCH 087/126] correct config errors
---
exercises/practice/sublist/.approaches/config.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/sublist/.approaches/config.json b/exercises/practice/sublist/.approaches/config.json
index 1c5eb3838b6..ce54db9c14e 100644
--- a/exercises/practice/sublist/.approaches/config.json
+++ b/exercises/practice/sublist/.approaches/config.json
@@ -4,14 +4,14 @@
},
"approaches": [
{
- "uuid": "3593cfe3-5cab-4141-b0a2-329148a66bb6",
- "slug": "list-manipulations",
+ "uuid": "db47397a-4551-49e8-8775-7e7aad79a38b",
+ "slug": "list-manipulation",
"title": "List manipulation",
"blurb": "Manipulate and check lists to solve the exercise",
"authors": ["safwansamsudeen"]
},
{
- "uuid": "eccd1e1e-6c88-4823-9b25-944eccaa92e7",
+ "uuid": "61366160-c859-4d16-9085-171428209b8d",
"slug": "using-strings",
"title": "Using strings",
"blurb": "Convert the lists to string and use string manipulation to solve the exercise",
From a260525ea900c47b4d14bbe5600bf6d5428f9ced Mon Sep 17 00:00:00 2001
From: Safwan Samsudeen
Date: Thu, 15 Jun 2023 06:45:02 +0530
Subject: [PATCH 088/126] correct approach
---
.../sublist/.approaches/introduction.md | 8 +++--
.../.approaches/using-strings/content.md | 33 ++++++++++++++-----
.../.approaches/using-strings/snippet.txt | 4 +--
3 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/exercises/practice/sublist/.approaches/introduction.md b/exercises/practice/sublist/.approaches/introduction.md
index 8cd30870cca..ac426baec6e 100644
--- a/exercises/practice/sublist/.approaches/introduction.md
+++ b/exercises/practice/sublist/.approaches/introduction.md
@@ -32,8 +32,9 @@ def sublist(list_one, list_two):
Read more on the [detail of this approach][approach-list-manipulation].
## Approach: using strings
-Another clever approach is to convert the lists to strings and then use the `in` operator to check for sub-sequences.
-Note that this approach is not as performant as the previous one.
+Another seemingly clever approach is to convert the lists to strings and then
+use the `in` operator to check for sub-sequences.
+**However, this does not work.**
```python
SUBLIST = 1
SUPERLIST = 2
@@ -52,7 +53,8 @@ def sublist(list_one, list_two):
return SUPERLIST
return UNEQUAL
```
-To understand more about this approach, [read here][approach-using-strings]
+To understand more about this approach and **why it fails**, [read here]
+[approach-using-strings].
[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation
[approach-using-strings]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/using-strings/content.md b/exercises/practice/sublist/.approaches/using-strings/content.md
index f49f59309cb..ff960902dc9 100644
--- a/exercises/practice/sublist/.approaches/using-strings/content.md
+++ b/exercises/practice/sublist/.approaches/using-strings/content.md
@@ -1,7 +1,13 @@
# Using strings
+~~~~exercism/caution
+**This approach does not work, and this document exists to explain that.**
+Please do not use it in your code.
+~~~~
-Another clever solution is to convert the lists to strings and then use the `in` operator to check for sub-sequences.
-Note that this approach is not as performant as the previous one.
+Another seemingly clever solution is to convert the lists to strings and then
+use the `in` operator to check for sub-sequences.
+Note that this approach, even if it worked, is not as performant as the
+previous one.
```python
SUBLIST = 1
SUPERLIST = 2
@@ -9,8 +15,8 @@ EQUAL = 3
UNEQUAL = 4
def sublist(list_one, list_two):
- list_one_check = (str(list_one).strip("[]") + ",")
- list_two_check = (str(list_two).strip("[]") + ",")
+ list_one_check = str(list_one).strip("[]")
+ list_two_check = str(list_two).strip("[]")
if list_one_check == list_two_check:
return EQUAL
@@ -20,11 +26,22 @@ def sublist(list_one, list_two):
return SUPERLIST
return UNEQUAL
```
-Note that we can't use directly `.join` as it only accepts strings inside the iterable, while there the test cases have integers.
-However, if one wanted to use it, we could use `map` or a [generator expression][gen-exp] inside `.join`.
+Let's parse the code to see what it does.
+In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"`.
+We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`.
-In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"` so that there's a consistent pattern of number + comma while using the `in` operator.
+We add a comma because, say, we call `sublist` with `[1, 2]` and `[1, 22]`. `"1, 2" in "1, 22"` evaluates to `True`, so
+the **function would wrongly mark it as `SUBLIST`**.
-We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`.
+This test can be overridden by changing the code like this:
+```python
+list_one_check = str(list_one).strip("[]") + ','
+list_two_check = str(list_two).strip("[]") + ','
+```
+Yet, the test case (which doesn't exist in the Exercism test suite) `["1", "2"]` and `["5", "'1', '2',", "7"]` would
+fail.
+
+Students can add any arbitrary string into the representation to try to "defeat" this test - `list_one_check = str
+(list_one) + TOKEN`. The test suite currently test `TOKEN = ''`, but not others.
[gen-exp]: https://www.programiz.com/python-programming/generator
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/using-strings/snippet.txt b/exercises/practice/sublist/.approaches/using-strings/snippet.txt
index ff7a2563bec..26fc3ec0ec7 100644
--- a/exercises/practice/sublist/.approaches/using-strings/snippet.txt
+++ b/exercises/practice/sublist/.approaches/using-strings/snippet.txt
@@ -1,6 +1,6 @@
+# Failing approach
def sublist(list_one, list_two):
- list_one_check = (str(list_one).strip("[]") + ",")
- list_two_check = (str(list_two).strip("[]") + ",")
+ list_one_check = str(list_one).strip("[]")
...
elif list_one_check in list_two_check:
return SUBLIST
From 1cb26c6d68a24d18a78b39243414befb680c0076 Mon Sep 17 00:00:00 2001
From: Safwan Samsudeen <62411302+safwansamsudeen@users.noreply.github.com>
Date: Fri, 16 Jun 2023 16:38:43 +0530
Subject: [PATCH 089/126] formatting issue
---
exercises/practice/sublist/.approaches/introduction.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/sublist/.approaches/introduction.md b/exercises/practice/sublist/.approaches/introduction.md
index ac426baec6e..42f991ef086 100644
--- a/exercises/practice/sublist/.approaches/introduction.md
+++ b/exercises/practice/sublist/.approaches/introduction.md
@@ -53,8 +53,7 @@ def sublist(list_one, list_two):
return SUPERLIST
return UNEQUAL
```
-To understand more about this approach and **why it fails**, [read here]
-[approach-using-strings].
+To understand more about this approach and **why it fails**, [read here][approach-using-strings].
[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation
-[approach-using-strings]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings
\ No newline at end of file
+[approach-using-strings]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings
From f6caa44faa8fb7d0de9a54ddb5c6183e027429c6 Mon Sep 17 00:00:00 2001
From: Andras Nagy <20251272+BNAndras@users.noreply.github.com>
Date: Sat, 17 Jun 2023 09:46:55 -0700
Subject: [PATCH 090/126] Remove ResistorColorMaster reference
---
.../resistor-color-expert/resistor_color_expert_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/resistor-color-expert/resistor_color_expert_test.py b/exercises/practice/resistor-color-expert/resistor_color_expert_test.py
index 8d17a36409a..bcf2052c021 100644
--- a/exercises/practice/resistor-color-expert/resistor_color_expert_test.py
+++ b/exercises/practice/resistor-color-expert/resistor_color_expert_test.py
@@ -7,7 +7,7 @@
# Tests adapted from `problem-specifications//canonical-data.json`
-class ResistorColorMasterTest(unittest.TestCase):
+class ResistorColorExpertTest(unittest.TestCase):
def test_orange_orange_black_and_red(self):
self.assertEqual(resistor_label(["orange", "orange", "black", "red"]), "33 ohms ±2%")
From bfad7ef55e18c905eab2777eb1022cde4317ba10 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 4 Jul 2023 11:28:49 -0700
Subject: [PATCH 091/126] [Dictionary & Dictionary-Methods]: Updates & Edits +
"Mecha Munch Management" Concept Exercise (#3445)
* Updates and edits to dict, dict-methods and inventory-management.
* Revised the concept for dictionaries and added the concept for dictionary methods.
Added new dict-methods exercise "Mecha Munch Management" and related files.
* Updates to fix CI errors and typos.
* Fixed docstring indentation error in stub file.
* Update tests, examplar and slight adjustements of instructions
* Update syntax used
* Fix spacing
* Minor formatting changes
* Update about.md
* Update test cases
* Resolved merge confilts for exercise introduction and re-arranged the task order.
* Added exemplar code.
* Corrected parameter naming error caught by flake8.
* Added test and various other changes
* Added hints and made various readability and formatting changes.
* Fixed additional errors and refrences.
* Removed merge conflict flag from file.
* Omitted user from user cart.
* Further edits from code review.
* One. Final. Edit. For this round.
---------
Co-authored-by: meatball
Co-authored-by: meatball <69751659+meatball133@users.noreply.github.com>
---
concepts/dict-methods/about.md | 388 ++++++++++--------
concepts/dict-methods/introduction.md | 26 +-
concepts/dicts/about.md | 286 +++++++++++--
concepts/dicts/introduction.md | 19 +-
config.json | 8 +
.../.docs/instructions.md | 28 +-
.../.docs/introduction.md | 117 ++++--
.../mecha-munch-management/.docs/hints.md | 56 +++
.../.docs/instructions.md | 129 ++++++
.../.docs/introduction.md | 234 +++++++++++
.../mecha-munch-management/.meta/config.json | 21 +
.../mecha-munch-management/.meta/design.md | 59 +++
.../mecha-munch-management/.meta/exemplar.py | 79 ++++
.../mecha-munch-management/dict_methods.py | 65 +++
.../dict_methods_test.py | 161 ++++++++
15 files changed, 1431 insertions(+), 245 deletions(-)
create mode 100644 exercises/concept/mecha-munch-management/.docs/hints.md
create mode 100644 exercises/concept/mecha-munch-management/.docs/instructions.md
create mode 100644 exercises/concept/mecha-munch-management/.docs/introduction.md
create mode 100644 exercises/concept/mecha-munch-management/.meta/config.json
create mode 100644 exercises/concept/mecha-munch-management/.meta/design.md
create mode 100644 exercises/concept/mecha-munch-management/.meta/exemplar.py
create mode 100644 exercises/concept/mecha-munch-management/dict_methods.py
create mode 100644 exercises/concept/mecha-munch-management/dict_methods_test.py
diff --git a/concepts/dict-methods/about.md b/concepts/dict-methods/about.md
index e36fc7082e1..20625b1c4f5 100644
--- a/concepts/dict-methods/about.md
+++ b/concepts/dict-methods/about.md
@@ -1,74 +1,85 @@
# Dictionary Methods in Python
-A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a [hash table or hashmap][hashtable-wikipedia].
-In Python, it's considered a [mapping type][mapping-types-dict].
-`dicts` enable the retrieval of a value in constant time (on average), given the key.
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
-Compared to searching for a value within a list or array (_without knowing the index position_), a dictionary uses significantly more memory, but has very rapid retrieval.
-It's especially useful in scenarios where the collection of items is large and must be accessed/updated frequently.
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
## Dictionary Methods
The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
Some were introduced in the concept for `dicts`.
-Here are a few more - along with some techniques for iterating through and manipulating `dicts`.
+Here we cover a few more - along with some techniques for iterating through and manipulating dictionaries.
-To quickly populate a dictionary with various `keys` and default values, the _class method_ [`dict.fromkeys(iterable, )`][fromkeys] will iterate through the `keys` and create a new `dict`. All `values` will be set to the `default` value provided.
+- `dict.setdefault()` for automatically adding keys without error.
+- `dict.fromkeys(iterable, )` for creating a new `dict` from any number of iterables.
+- `.keys()`, `.values()`, and `.items()` for convenient iterators.
+- `sorted(.items())`. for re-ordering entries in a `dict`.
+- `dict_one.update()` for updating one `dict` with overlapping values from another `dict`.
+- `dict | other_dict` and `dict |= other_dict` for merging or updating two `dict`s via operators.
+- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` for reversed views.
+- `.popitem()` for removing and returning a `key`, `value` pair.
-```python
->>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
->>> new_dict
-{'Grassy Green': 'fill in hex color here',
- 'Purple Mountains Majesty': 'fill in hex color here',
- 'Misty Mountain Pink': 'fill in hex color here'}
-```
-
-`dict.clear()` will removed all `key:value` pairs from the dictionary, leaving it empty and ready for new entries.
+### `setdefault()` for Error-Free Insertion
-```python
->>> pallette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> pallette_II.clear()
->>> pallette_II
-{}
-```
+The dictionary concept previously covered that `.get(key, )` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
+This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for `key` will be present.
-`dict.get(key, )` works similarly to `dict[key]` -- but it will return the `default` if the `key` is not in the dictionary.
-If no `default` is given, the method will return `None`.
+For a similarly "safe" (_without KeyError_) insertion operation, there is the `.setdefault(key, )` method.
+`setdefault(key, )` will return the `value` if the `key` is found in the dictionary.
+If the key is **not** found, it will _insert_ the (`key`, `default value`) pair and return the `default value` for use.
```python
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
->>> palette_I['Factory Stone Purple']
-Traceback (most recent call last):
- line 1, in
- palette_I['Factory Stone Purple']
+#Looking for the value associated with key "Rock Brown".
+#The key does not exist, so it is added with the default
+# value, and the value is returned.
+>>> palette.setdefault('Rock Brown', '#694605')
+'#694605'
-KeyError: 'Factory Stone Purple'
+#The (key, default value) pair has now been added to the dictionary.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
+```
->>> palette_I.get('Factory Stone Purple', 'That color was not found.')
-'That color was not found.'
+### Use `fromkeys()` to Populate a Dictionary
->>> palette_I.get('Factory Stone Purple', False)
-False
+To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(iterable, )`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
+All `values` will be set to the `default value` provided:
->>> palette_I.get('Factory Stone Purple')
-None
+```python
+>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
+
+{'Grassy Green': 'fill in hex color here',
+ 'Purple Mountains Majesty': 'fill in hex color here',
+ 'Misty Mountain Pink': 'fill in hex color here'}
```
-`dict.popitem()` removes & returns a single `key:value` pair from the `dict`.
-Pairs are returned in Last-in-First-out (LIFO) order.
-If the dictionary is empty, calling `.dict.popitem` will raise a `KeyError`.
+### Removing and Returning a (key, value) Pair With `.popitem()`
+
+`.popitem()` removes & returns a single (`key`, `value`) pair from a dictionary.
+Pairs are returned in Last-in-First-out (`LIFO`) order.
+If the dictionary is empty, calling `popitem()` will raise a `KeyError`:
```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd'}
>>> palette_I.popitem()
('Misty Mountain Pink', '#f9c5bd')
+
>>> palette_I.popitem()
('Purple Mountains Majesty', '#8076a3')
+
>>> palette_I.popitem()
('Grassy Green', '#9bc400')
->>> palette_I.popitem()
+#All (key, value) pairs have been removed.
+>>> palette_I.popitem()
Traceback (most recent call last):
line 1, in
@@ -77,25 +88,37 @@ Traceback (most recent call last):
KeyError: 'popitem(): dictionary is empty'
```
-While `dict.clear()` and `dict.popitem()` are _destructive_ actions, the `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views].
-These views can be used for looping over `dict` content without altering it and are _dynamic_ -- when underlying dictionary data changes, the associated view object will reflect the change.
+### Iterating Over Entries in a Dictionary
+
+The `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views] of a dictionary.
+These views can be used for looping over entries without altering them.
+They are also _dynamic_ -- when underlying dictionary data changes, the associated view object will reflect the change:
```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd'}
+
+#Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
+#Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
+#Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
+#Views are dynamic. Changing values in the dict
+# changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
+>>> palette_I['Deep Red'] = '#932432'
+
>>> palette_I.values()
-dict_values(['#9bc400', (128, 118, 163), '#f9c5bd'])
+dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
->>> palette_I['Deep Red'] = '#932432'
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
@@ -103,32 +126,68 @@ dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'D
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
```
-`dict_one.update()` can be used to _combine_ two dictionaries.
-This method will take the `key:value` pairs of `dict_two` and write them into `dict_one`.
+### More on `.keys()`, `.values()`, and `.items()`
+
+In Python 3.7+, `dicts` preserve the order in which entries are inserted allowing First-in, First-out (_`FIFO`_), iteration using `.keys()`, `.values()`, or `.items()`.
+In Python 3.8+, views are also _reversible_.
+This allows keys, values, or (key, value) pairs to be iterated over in Last-in, First-out (`LIFO`) order by using `reversed(.keys())`, `reversed(.values())`, or `reversed(.items())`:
```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
->>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> palette_I.update(palette_II)
->>> palette_I
+>>> palette_II = {'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple baseline': '#161748'}
+>>> for item in palette_II.items():
+... print(item)
...
+('Factory Stone Purple', '#7c677f')
+('Green Treeline', '#478559')
+('Purple baseline', '#161748')
-{'Grassy Green': '#9bc400',
- 'Purple Mountains Majesty': '#8076a3',
- 'Misty Mountain Pink': '#f9c5bd',
- 'Factory Stone Purple': '#7c677f',
- 'Green Treeline': '#478559',
- 'Purple baseline': '#161748'}
+>>> for item in reversed(palette_II.items()):
+... print (item)
+...
+('Purple baseline', '#161748')
+('Green Treeline', '#478559')
+('Factory Stone Purple', '#7c677f')
+```
+
+### Combining Dictionaries with `.update()`
+
+`.update()` can be used to _combine_ two dictionaries.
+This method will take the (`key`,`value`) pairs of `` and write them into ``:
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_II = {'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple Baseline': '#161748'}
+
+>>> palette_I.update(palette_II)
+
+#Note that new items from palette_II are added.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
```
-Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`.
+Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`:
```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd',
- 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd',
+ 'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple baseline': '#161748'}
+
+>>> palette_III = {'Grassy Green': (155, 196, 0),
+ 'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> palette_I.update(palette_III)
+
+#Overlapping values in palette_I are replaced with
+# values from palette_III
>>> palette_I
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
@@ -137,14 +196,21 @@ Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be
'Green Treeline': '#478559', 'Purple baseline': '#161748'}
```
-Python 3.9 introduces a different means of merging `dicts`: the `union` operators.
-`dict | other_dict` will create a **new** `dict`, made up of the `key:value` pairs of `dict` and `other_dict`.
-When both dictionaries share keys, the `other_dict` values will take precedence.
-`dict |= other` will behave similar to `dict.update()`, but in this case, `other` can be either a `dict` or an iterable of `key:value` pairs.
+### Merging and Updating Dictionaries Via the Union (`|`) Operators
+
+Python 3.9 introduces a different means of merging `dicts`: the `union` operators.
+`dict_one | dict_two` will create a **new dictionary**, made up of the (`key`, `value`) pairs of `dict_one` and `dict_two`.
+When both dictionaries share keys, `dict_two` values take precedence.
```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
->>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd'}
+
+>>> palette_II = {'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple baseline': '#161748'}
+
>>> new_dict = palette_I | palette_II
>>> new_dict
...
@@ -154,44 +220,34 @@ When both dictionaries share keys, the `other_dict` values will take precedence.
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
-
- >>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163), 'Misty Mountain Pink': (249, 197, 189)}
- >>> new_dict |= palette_III
- >>> new_dict
- ...
- {'Grassy Green': (155, 196, 0),
- 'Purple Mountains Majesty': (128, 118, 163),
- 'Misty Mountain Pink': (249, 197, 189),
- 'Factory Stone Purple': '#7c677f',
- 'Green Treeline': '#478559',
- 'Purple baseline': '#161748'}
```
-## Tips and Tricks
-
-As of Python 3.6, `dicts` preserve the order in which items are inserted, allowing ordered iteration using `.items()`. As of Python 3.8, `dict` _views_ are reversible, allowing keys, values or items to be iterated over reverse of insertion order by using `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())`.
+`dict_one |= other` behaves similar to `.update()`, but in this case, `other` can be either a `dict` or an iterable of (`key`, `value`) pairs:
```python
->>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> for item in palette_II.items():
-... print(item)
-...
-('Factory Stone Purple', '#7c677f')
-('Green Treeline', '#478559')
-('Purple baseline', '#161748')
-
->>> for item in reversed(palette_II.items()):
-... print (item)
+>>> palette_III = {'Grassy Green': (155, 196, 0),
+ 'Purple Mountains Majesty': (128, 118, 163),
+ 'Misty Mountain Pink': (249, 197, 189)}
+>>> new_dict |= palette_III
+>>> new_dict
...
-('Purple baseline', '#161748')
-('Green Treeline', '#478559')
-('Factory Stone Purple', '#7c677f')
-
+{'Grassy Green': (155, 196, 0),
+'Purple Mountains Majesty': (128, 118, 163),
+'Misty Mountain Pink': (249, 197, 189),
+'Factory Stone Purple': '#7c677f',
+'Green Treeline': '#478559',
+'Purple baseline': '#161748'}
```
-While `dict` does not have a built-in sorting method, it is possible to sort a dictionary _view_ by keys or values using the built-in `sorted()` with `dict.items()`. The sorted view can then be used to create a new, sorted dictionary. Unless a _sort key_ is specified, the default sort is over dictionary keys.
+### Sorting a Dictionary
+
+Dictionaries do not have a built-in sorting method.
+However, it is possible to sort a `dict` _view_ using the built-in function `sorted()` with `.items()`.
+The sorted view can then be used to create a new dictionary.
+Unless a _sort key_ is specified, the default sort is over dictionary `keys`.
```python
+# Default ordering for a dictionary is last in, first out (LIFO).
>>> color_palette = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
@@ -200,6 +256,7 @@ While `dict` does not have a built-in sorting method, it is possible to sort a d
'Purple baseline': '#161748'}
+# The default sort order for a dictionary uses the keys.
>>> sorted_palette = dict(sorted(color_palette.items()))
>>> sorted_palette
{'Factory Stone Purple': '#7c677f',
@@ -208,7 +265,10 @@ While `dict` does not have a built-in sorting method, it is possible to sort a d
'Misty Mountain Pink': '#f9c5bd',
'Purple Mountains Majesty': '#8076a3',
'Purple baseline': '#161748'}
-
+
+
+# A sort key can be provided in the form
+# of an anonymous function (lambda).
>>> value_sorted_palette = dict(sorted(color_palette.items(), key=lambda color: color[1]))
>>> value_sorted_palette
{'Purple baseline': '#161748',
@@ -217,110 +277,96 @@ While `dict` does not have a built-in sorting method, it is possible to sort a d
'Purple Mountains Majesty': '#8076a3',
'Grassy Green': '#9bc400',
'Misty Mountain Pink': '#f9c5bd'}
-
```
-Swapping keys and values reliably in a dictionary takes a little more work, but can be accomplished via a loop using `dict.items()`. But if the values stored in the `dict` are not unique, extra checks are required. Both methods assume that `dict` keys and values are _hashable_.
-
-```python
+### Transposing a Dictionaries Keys and Values
+Swapping keys and values reliably in a dictionary takes a little work, but can be accomplished via a `loop` using `dict.items()` or in a dictionary comprehension.
+Safe swapping assumes that `dict` keys and values are both _hashable_.
+```python
color_reference = {'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
- 'Purple baseline': '#161748',
- 'Pink highlight': '#f95d9b',
- 'Bluewater lowlight': '#39a0ca',
- 'Bright Red': '#DE354C',
- 'Deep Red': '#932432',
- 'Pure Purple': '#3C1874',
- 'Purple Tinged Grey': '#283747',
- 'Cloud': '#F3F3F3'}
-
->>> reversed_color_reference = {}
+ 'Purple baseline': '#161748'}
+
+# Creating a new dictionary to hold the swapped entries.
+>>> swapped_color_reference = {}
+
+# Iterating through the dictionary, using values as keys.
>>> for key, value in color_reference.items():
-... reversed_color_reference[value] = key
+... swapped_color_reference[value] = key
->>> reversed_color_reference
+>>> swapped_color_reference
{'#8076a3': 'Purple Mountains Majesty',
'#f9c5bd': 'Misty Mountain Pink',
'#7c677f': 'Factory Stone Purple',
'#478559': 'Green Treeline',
- '#161748': 'Purple baseline',
- '#f95d9b': 'Pink highlight',
- '#39a0ca': 'Bluewater lowlight',
- '#DE354C': 'Bright Red',
- '#932432': 'Deep Red',
- '#3C1874': 'Pure Purple',
- '#283747': 'Purple Tinged Grey',
- '#F3F3F3': 'Cloud'}
-
-
->>> extended_color_reference = {'#8076a3': 'Purple Mountains Majesty',(128, 118, 163): 'Purple Mountains Majesty',
- (21, 28, 0, 36): 'Purple Mountains Majesty','#f9c5bd': 'Misty Mountain Pink',
- (249, 197, 189): 'Misty Mountain Pink',(0, 21, 24, 2): 'Misty Mountain Pink',
- '#7c677f': 'Factory Stone Purple',(124, 103, 127): 'Factory Stone Purple',
- (2, 19, 0, 50): 'Factory Stone Purple','#478559': 'Green Treeline',
- (71, 133, 89): 'Green Treeline',(47, 0, 33, 48): 'Green Treeline',
- '#161748': 'Purple baseline',(22, 23, 72): 'Purple baseline',
- (69, 68, 0, 72): 'Purple baseline','#f95d9b': 'Pink highlight',
- (249, 93, 155): 'Pink highlight',(0, 63, 38, 2): 'Pink highlight',
- '#39a0ca': 'Bluewater lowlight',(57, 160, 202): 'Bluewater lowlight',
- (72, 21, 0, 21): 'Bluewater lowlight','#DE354C': 'Bright Red',
- (222, 53, 76): 'Bright Red',(0, 76, 66, 13): 'Bright Red',
- '#932432': 'Deep Red',(147, 36, 50): 'Deep Red',
- (0, 76, 66, 42): 'Deep Red','#3C1874': 'Pure Purple',
- (60, 24, 116): 'Pure Purple',(48, 79, 0, 55): 'Pure Purple',
- '#283747': 'Purple Tinged Grey',(40, 55, 71): 'Purple Tinged Grey',
- (44, 23, 0, 72): 'Purple Tinged Grey','#F3F3F3': 'Cloud',
- (243, 243, 243): 'Cloud',(0, 0, 0, 5): 'Cloud'}
+ '#161748': 'Purple baseline'}
+
+# A dictionary comprehension can also be used to swap entries.
+>>> swapped = {value: key for key, value in
+ color_reference.items()}
+>>> swapped
+{'#8076a3': 'Purple Mountains Majesty',
+ '#f9c5bd': 'Misty Mountain Pink',
+ '#7c677f': 'Factory Stone Purple',
+ '#478559': 'Green Treeline',
+ '#161748': 'Purple baseline'}
+```
+
+If the values stored in the `dict` are not unique, extra checks become necessary before key and value swapping can happen:
+
+```python
+# Things become more complicated if there are duplicates in
+# potential key values.This dict is arranged by hex, RGB, and HSL
+# keys, but values repeat.
+>>> extended_colors = {'#8076a3': 'Purple Mountains Majesty',
+ (128, 118, 163): 'Purple Mountains Majesty',
+ (21, 28, 0, 36): 'Purple Mountains Majesty',
+ '#f9c5bd': 'Misty Mountain Pink',
+ (249, 197, 189): 'Misty Mountain Pink',
+ (0, 21, 24, 2): 'Misty Mountain Pink',
+ '#7c677f': 'Factory Stone Purple',
+ (124, 103, 127): 'Factory Stone Purple',
+ (2, 19, 0, 50): 'Factory Stone Purple',
+ '#478559': 'Green Treeline',
+ (71, 133, 89): 'Green Treeline',
+ (47, 0, 33, 48): 'Green Treeline'}
+
+# New empty dictionary for holding swapped entries.
>>> consolidated_colors = {}
+
+# Iterating over (key, value) pairs using .items()
>>> for key, value in extended_color_reference.items():
-... if value in consolidated_colors:
+... if value in consolidated_colors: #Check if key has already been created.
... consolidated_colors[value].append(key)
... else:
-... consolidated_colors[value] = [key]
+... consolidated_colors[value] = [key] #Create a value list with the former key in it.
>>> consolidated_colors
{'Purple Mountains Majesty': ['#8076a3', (128, 118, 163), (21, 28, 0, 36)],
'Misty Mountain Pink': ['#f9c5bd', (249, 197, 189), (0, 21, 24, 2)],
'Factory Stone Purple': ['#7c677f', (124, 103, 127), (2, 19, 0, 50)],
- 'Green Treeline': ['#478559', (71, 133, 89), (47, 0, 33, 48)],
- 'Purple baseline': ['#161748', (22, 23, 72), (69, 68, 0, 72)],
- 'Pink highlight': ['#f95d9b', (249, 93, 155), (0, 63, 38, 2)],
- 'Bluewater lowlight': ['#39a0ca', (57, 160, 202), (72, 21, 0, 21)],
- 'Bright Red': ['#DE354C', (222, 53, 76), (0, 76, 66, 13)],
- 'Deep Red': ['#932432', (147, 36, 50), (0, 76, 66, 42)],
- 'Pure Purple': ['#3C1874', (60, 24, 116), (48, 79, 0, 55)],
- 'Purple Tinged Grey': ['#283747', (40, 55, 71), (44, 23, 0, 72)],
- 'Cloud': ['#F3F3F3', (243, 243, 243), (0, 0, 0, 5)]}
-
+ 'Green Treeline': ['#478559', (71, 133, 89), (47, 0, 33, 48)]}
```
For a detailed explanation of dictionaries and methods for working with them, the [official tutorial][dicts-docs] and the [official library reference][mapping-types-dict] are excellent starting places.
-For more on sorting, see the [Sorting HOW TO][sorting-howto] in the python docs.
- [Real Python][how-to-dicts] and [Finxter][fi-dict-guide] also have very thorough articles on Python dictionaries.
-## Extending Dictionaries: The collections module
+[Real Python][how-to-dicts] and [Finxter][fi-dict-guide] also have very thorough articles on Python dictionaries.
-The [`collections`][collections-docs] module adds more functionality to Python's standard collection-based datatypes (`dictionary`, `set`, `list`, `tuple`).
-A popular `dict`-oriented member of this module is the [`Counter`][counter-dicts], which automatically counts items and returns them a `dict` with the items as keys and their counts as values.
-There is also the [`OrderedDict`][ordered-dicts-docs], which has methods specialized for re-arranging the order of a dictionary.
-Finally, there is the [`defaultdict`][default-dicts], a subclass of the built-in `dict` module that, based on a factory method, sets a default value if a key is not found when trying to retrieve or assign the value.
+For more on sorting, see the [Sorting HOW TO][sorting-howto] in the Python docs.
-[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
-[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
-[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
-[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
-[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
-[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
-[collections-docs]: https://docs.python.org/3/library/collections.html
-[counter-dicts]: https://docs.python.org/3/library/collections.html#collections.Counter
-[ordered-dicts-docs]: https://docs.python.org/3/library/collections.html#collections.OrderedDict
-[default-dicts]: https://docs.python.org/2/library/collections.html#collections.defaultdict
-[dict-views]: https://docs.python.org/3/library/stdtypes.html#dict-views
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
[dict-methods]: https://docs.python.org/3/library/stdtypes.html#dict
+[dict-views]: https://docs.python.org/3/library/stdtypes.html#dict-views
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
[fi-dict-guide]: https://blog.finxter.com/python-dictionary
+[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
+[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
+[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[sorting-howto]: https://docs.python.org/3/howto/sorting.html
+[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/concepts/dict-methods/introduction.md b/concepts/dict-methods/introduction.md
index 52868299b9d..8a732643845 100644
--- a/concepts/dict-methods/introduction.md
+++ b/concepts/dict-methods/introduction.md
@@ -1,16 +1,26 @@
# Dictionary Methods in Python
-A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a [hash table or hashmap][hashtable-wikipedia].
-In Python, it's considered a [mapping type][mapping-types-dict].
-`dicts` enable the retrieval of a value in constant time (on average), given the key.
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
-Compared to searching for a value within a list or array (_without knowing the index position_), a dictionary uses significantly more memory, but has very rapid retrieval.
-It's especially useful in scenarios where the collection of items is large and must be accessed/updated frequently.
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
+The `dict` class in Python provides many useful [methods][dict-methods], some of which are introduced in the concept exercise for dictionaries.
-The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
-Some are introduced in the concept exercise for `dicts`.
-This concept tackles a few more - along with some techniques for iterating through and manipulating `dicts`.
+This concept tackles a few more:
+- `dict.setdefault()` for automatically adding keys when needed.
+- `dict.fromkeys(iterable, )` for creating a new `dict` from any number of iterables.
+- `.keys()`, `.values()`, and `.items()` for convenient iterators.
+- `sorted(.items())`. for re-ordering entries in a `dict`.
+- `dict_one.update()` for updating one `dict` with overlapping values from another `dict`.
+- `dict | other_dict` and `dict |= other_dict` for merging or updating two `dict`s via operators.
+- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` for reversed views.
+- `.popitem()` for removing and returning a `key`, `value` pair.
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/concepts/dicts/about.md b/concepts/dicts/about.md
index fb43067efe2..305abc6c821 100644
--- a/concepts/dicts/about.md
+++ b/concepts/dicts/about.md
@@ -1,55 +1,287 @@
# About
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
-A dictionary (`dict`) is a [mapping type][mapping-types-dict] data structure that associates [hashable][term-hashable] `keys` to `values` -- known in other programming languages as a resizable [hash table or hashmap][hashtable-wikipedia].
- `Keys` can include `numbers`, `str`, `tuples` (of _immutable_ values), or `frozensets`, but must be hashable and unique across the dictionary.
- `keys` are _immutable_ - once added to a `dict`, they can only be removed, they cannot be updated.
- `values` can be of any or multiple data type(s) or structures, including other dictionaries, built-in types, custom types, or even objects like functions or classes.
- `values` associated with any `key` are _mutable_, and can be replaced, updated or altered as long as the `key` entry exists.
- Dictionaries enable the retrieval of a `value` in (on average) constant O(1) time, given the `key`.
- Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more space in memory, but has significantly more rapid retrieval.
- Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and/or updated frequently.
+`Keys` must be hashable and unique across the dictionary.
+Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
-## Dictionary creation
+`values` can be of any data type or structure.
+ Values can also nest _arbitrarily_, so they can include lists-of-lists, sub-dictionaries, and other custom or compound data structures.
-A simple `dict` can be declared using the literal form `{: , : }`:
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
- ```python
+## Dictionary Construction
+Dictionaries can be created in many different ways:
+ - Using the [`fromkeys()`][fromkeys] classmethod
+ - Creating [dictionary comprehensions][dict-comprehensions]
+ - Merging two dictionaries via unpacking (`**`)
+ - Merging dictionaries via the `|` (_update_) operator
+ - Using a loop to iteratively add entries to a previously created empty `dict`.
+Below are the two most straightforward methods of dictionary creation.
+### The `dict()` Class Constructor
+
+`dict()` can be used with any iterable of `key`, `value` pairs or with a series of `=` _arguments_:
+
+```python
+#Passing a list of key,value tuples.
+>>> wombat = dict([('name', 'Wombat'),('speed', 23),
+ ('land_animal', True)])
+{'name': 'Wombat', 'speed': 23, 'land_animal': True}
+
+
+#Using key=value arguments.
+>>> bear = dict(name="Black Bear", speed=40, land_animal=True)
+{'name': 'Black Bear', 'speed': 40, 'land_animal': True}
+```
+
+The [documentation on `dicts`][dicts-docs] outlines additional variations and options in constructor use.
+
+
+### Dictionary Literals
+
+A dictionary can also be directly entered as a _dictionary literal_, using curly brackets (`{}`) enclosing `key : value` pairs.
+Entries that are enclosed in the `{}` can also appear on separate lines:
+
+```python
+>>> whale = {"name": "Blue Whale",
+ "speed": 35,
+ "land_animal": False}
+{'name': 'Blue Whale', 'speed': 35, 'land_animal': False}
+
+>>> wombat = {'name': 'Wombat',
+ 'speed': 23,
+ 'land_animal': True,
+ 'color': 'Brindle'}
+
+>>> wombat
+{'name': 'Wombat', 'speed': 23, 'land_animal': True, 'color': 'Brindle'}
+```
+
+Dictionaries can be arbitrarily nested:
+
+```python
+animals = {
+ "Real" : {
+ "Winged" : {
+ "Sparrow" : {'name': 'sparrow','speed': 12, 'land_animal': True},
+ "Kestrel" : {'name': 'kestrel', 'speed': 15, 'land_animal': True}
+ },
+ "Legged" : {
+ "Wombat" : {'name': 'Wombat', 'speed': 23, 'land_animal': True},
+ "Black Bear": {'name': 'Black Bear', 'speed': 40, 'land_animal': True},
+ "Polecat" : {'name': 'Polecat', 'speed': 15, 'land_animal': True}
+ },
+ "Other" : {
+ "Whale" : {'name': 'Blue Whale', 'speed': 35, 'land_animal': False},
+ "Orca" : {'name': 'Orca', 'speed': 45, 'land_animal': False},
+ "Snake" : {'name': 'Python', 'speed': 25, 'land_animal': True}
+ }
+ },
+
+ "Imaginary": {
+ "Winged" : {
+ "Dragon" : {'name': 'Fire Dragon','speed': 100, 'land_animal': True},
+ "Phoenix" : {'name': 'Phoenix', 'speed': 1500, 'land_animal': True}
+ },
+ "Legged" : {
+ "Sphinx" : {'name': 'Sphinx','speed': 10, 'land_animal': True},
+ "Minotaur" : {'name': 'Minotaur', 'speed': 5, 'land_animal': True}
+ },
+ "Other" : {}
+ }
+ }
+```
+
+## Accessing Values in a `dict`
+
+You can access a `value` in a dictionary using a _key_ in square brackets.
+If a key does not exist, a `KeyError` is thrown:
+
+```python
+>>> bear["speed"]
+40
+
+>>> bear["color"]
+Traceback (most recent call last):
+ File "", line 1, in
+KeyError: 'color'
+```
+
+Accessing an entry via the `get(, )` method can avoid the `KeyError`:
+
+```python
+>>> bear.get("color", 'not found')
+'not found'
+```
+
+### Nested Dictionary Entries
+
+To access entries in nested dictionaries, use successive brackets.
+If a given key is missing, the usual KeyError will be thrown:
+
+```python
+#Using the animals nested dictionary.
+>>> animals["Real"]["winged"]["Kestrel"]["speed"]
+15
+
+>>> animals["Imaginary"]["winged"]["Kestrel"]["speed"]
+Traceback (most recent call last):
+ File "", line 1, in
+KeyError: 'Kestrel'
+```
+
+To avoid the `KeyError`, `.get()` can be used, but the calls to `.get()` must be _chained_:
+
+```python
+#Using the animals nested dictionary.
+#Note the use of parenthesis to enable placing the
+# .get() calls on separate lines.
+>>> (animals.get("Imaginary", {})
+ .get("Legged", {})
+ .get("Sphinx", {})
+ .get("Color", "I have no idea!"))
+'I have no idea!'
+```
+
+## Changing or Adding Dictionary Values
+
+You can change an entry `value` by assigning to its _key_:
+
+```python
+#Assigning the value "Grizzly Bear" to the name key.
+>>> bear["name"] = "Grizzly Bear"
+{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True}
+
+>>> whale["speed"] = 25
+{'name': 'Blue Whale', 'speed': 25, 'land_animal': False}
+```
+
+New `key`:`value` pairs can be _added_ in the same fashion:
+
+```python
+# Adding an new "color" key with a new "tawney" value.
+>>> bear["color"] = 'tawney'
+{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True, 'color': 'tawney'}
+
+>>> whale["blowholes"] = 1
+{'name': 'Blue Whale', 'speed': 25, 'land_animal': False, 'blowholes': 1}
+```
+
+
+## Removing (Pop-ing and del) Dictionary Entries
+
+You can use the `.pop()` method to delete a dictionary entry.
+`.pop()` removes the (`key`, `value`) pair and returns the `value` for use.
+Like `.get()`, `.pop()` accepts second argument (_`dict.pop(, )`_) that will be returned if the `key` is not found.
+This prevents a `KeyError` being raised:
+
+```python
+#Using .pop() removes both the key and value, returning the value.
+>>> bear.pop("name")
+'Grizzly Bear'
+
+
+#The "name" key is now removed from the dictionary.
+#Attempting .pop() a second time will throw a KeyError.
+>>> bear.pop("name")
+Traceback (most recent call last):
+ File "", line 1, in
+KeyError: 'name'
+
+
+#Using a default argument with .pop() will
+# prevent a KeyError from a missing key.
+>>> bear.pop("name", "Unknown")
+'Unknown'
```
- The dictionary constructor `dict(=, =)`, but there are many more ways of creating and initializing dictionaries including the use of a _dict comprehension_ or passing additional constructor parameters as illustrated in the [Python docs][mapping-types-dict].
+You can also use the `del` statement to remove a single or multiple entries.
+A `KeError` is raised if the entry to be removed is not found in the dictionary:
+```python
+>>> wombat = {'name': 'Wombat',
+ 'speed': 23,
+ 'land_animal': True,
+ 'color': 'Brindle',
+ 'talent': 'Singing',
+ 'size': 'small'}
+#Remove a single entry from the dictionary.
+>>> del wombat["color"]
+>>> wombat
+{'name': 'Wombat', 'speed': 23, 'land_animal': True, 'talent': 'Singing', 'size': 'small'}
-Inserting a new `key`:`value` pair can be done with `dict[key] = value` and the value can be retrieved by using `retrieved_value = dict[key]`.
-## Methods
+#Remove multiple entries from the dictionary.
+>>> del wombat["talent"], wombat["size"]
+>>> wombat
+{'name': 'Wombat', 'speed': 23, 'land_animal': True}
-`dicts` implement various methods to allow easy initialization, updating and viewing.
-Some useful `dict` methods:
+#Attempting a deletion of a non-existent key raises a KeyError
+>>> del wombat["number_of_legs"]
+Traceback (most recent call last):
+ File "", line 1, in
+KeyError: 'number_of_legs'
+```
+
+## Looping through/Iterating over a Dictionary
-- Retrieve a value "safely" from a dictionary by using the `.get(key, [default])` method. `.get(key, [default])` returns the value for the key **or** the _default value_ if the key is not found, instead of raising a `KeyError`. This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for key will be present.
-- Retrieve a value "safely" or insert a default _value_ if the key is not found using the `.setdefault(key, [default])` method. `setdefault(key, [default])` will insert the default value in the dictionary **only** if the key is not found, then it will retrieve either the **newly inserted** default value if the key was not found or the **unchanged** existing value if the key was found.
-- Return various _iterable_ views of your `dict` with `.keys()`, `.values()`, `.items()` (_an iterable of (key, value) `tuples`_).
+Looping through a dictionary using `for item in dict` or `while item` will iterate over only the _keys _ by default.
+You can access the _values_ within the same loop by using _square brackets_:
+
+```python
+>>> for key in bear:
+>>> print((key, bear[key])) #this prints a tuple of (key, value)
+('name', 'Black Bear')
+('speed', 40)
+('land_animal', True)
+```
+
+You can also use the `.items()` method, which returns (`key`, `value`) tuples:
+
+```python
+#dict.items() forms (key, value tuples) that can be
+# unpacked and iterated over.
+>>> for key, value in whale.items():
+>>> print(key, ":", value)
+name : Blue Whale
+speed : 25
+land_animal : False
+blowholes : 1
+```
+
+Likewise, `.keys()` will return the `keys` and `.values()` will return the `values`.
For a detailed explanation of dictionaries in Python, the [official documentation][dicts-docs] is an excellent starting place, or you can also check out the [W3-Schools][how-to-dicts] tutorial.
-## Extending Dictionaries: The collections module
-The [`collections`][collections-docs] module adds more functionality to Python's standard collection-based datatypes (`dictionary`, `set`, `list`, `tuple`). A popular `dict`-oriented member of this module is the [`Counter`][counter-dicts], which automatically counts items and returns them a `dict` with the items as keys and their counts as values. There is also the [`OrderedDict`][ordered-dicts-docs], which has methods specialized for re-arranging the order of a dictionary. Finally, there is the [`defaultdict`][default-dicts], a subclass of the built-in `dict` module that, based on a factory method, sets a default value if a key is not found when trying to retrieve or assign the value.
+## Extending Dictionary Functionality: The Collections Module
-[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
-[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
-[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
-[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
-[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+The [`collections`][collections-docs] module adds specialized functionality to Python's standard collection-based datatypes (`dictionary`, `set`, `list`, `tuple`).
+A popular `dict`-oriented member of this module is the [`Counter`][counter-dicts], which automatically counts items and returns them in a `dict` with the items as keys and their counts as values.
+There is also the [`OrderedDict`][ordered-dicts-docs], which has methods specialized for re-arranging the order of a dictionary.
+Finally, there is the [`defaultdict`][default-dicts], a subclass of the built-in `dict` module that, based on a factory method, sets a default value if a key is not found when trying to retrieve or assign to a dictionary entry.
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
[collections-docs]: https://docs.python.org/3/library/collections.html
[counter-dicts]: https://docs.python.org/3/library/collections.html#collections.Counter
-[ordered-dicts-docs]: https://docs.python.org/3/library/collections.html#collections.OrderedDict
[default-dicts]: https://docs.python.org/2/library/collections.html#collections.defaultdict
+[dict-comprehensions]: https://www.learnbyexample.org/python-dictionary-comprehension/
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
+[fromkeys]: https://www.w3schools.com/python/ref_dictionary_fromkeys.asp
+[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
+[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[ordered-dicts-docs]: https://docs.python.org/3/library/collections.html#collections.OrderedDict
+[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/concepts/dicts/introduction.md b/concepts/dicts/introduction.md
index 9a887ccfc65..367a89536fd 100644
--- a/concepts/dicts/introduction.md
+++ b/concepts/dicts/introduction.md
@@ -1,15 +1,22 @@
# Introduction
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
-A dictionary (`dict`) is a [mapping type][mapping-types-dict] data structure that associates [hashable][term-hashable] `keys` to `values` -- known in other programming languages as a resizable [hash table or hashmap][hashtable-wikipedia].
- `Keys` can include `numbers`, `str`, `tuples` (of _immutable_ values), or `frozensets`, but must be hashable and unique across the dictionary.
- `values` can be of any or multiple data type(s) or structures, including other dictionaries, built-in types, custom types, or even objects like functions or classes.
- Dictionaries enable the retrieval of a `value` in (on average) constant O(1) time, given the `key`.
- Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more space in memory, but has significantly more rapid retrieval.
- Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and/or updated frequently.
+`Keys` must be hashable and unique across the dictionary.
+Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
+`values` can be of any data type or structure.
+ Values can also nest _arbitrarily_, so they can include lists-of-lists, sub-dictionaries, and other custom or compound data structures.
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/config.json b/config.json
index e8c7f2f505f..f386ef39d6c 100644
--- a/config.json
+++ b/config.json
@@ -144,6 +144,14 @@
"prerequisites": ["loops", "lists", "tuples"],
"status": "beta"
},
+ {
+ "slug": "mecha-munch-management",
+ "name": "Mecha Munch Management",
+ "uuid": "5ac0c40c-4038-47b8-945b-8480e4a3f44c",
+ "concepts": ["dict-methods"],
+ "prerequisites": ["dicts"],
+ "status": "wip"
+ },
{
"slug": "locomotive-engineer",
"name": "Locomotive Engineer",
diff --git a/exercises/concept/inventory-management/.docs/instructions.md b/exercises/concept/inventory-management/.docs/instructions.md
index e1c1806c6ea..718b91d3e0a 100644
--- a/exercises/concept/inventory-management/.docs/instructions.md
+++ b/exercises/concept/inventory-management/.docs/instructions.md
@@ -4,13 +4,17 @@ In this exercise, you will be managing an inventory system.
The inventory should be organized by the item name and it should keep track of the number of items available.
-You will have to handle adding items to an inventory. Each time an item appears in a given list, increase the item's quantity by `1` in the inventory. Then, you will have to handle deleting items from an inventory.
+You will have to handle adding items to an inventory.
+Each time an item appears in a given list, the item's quantity should be increased by `1` in the inventory.
+You will also have to handle deleting items from an inventory by decreasing quantities by `1` when requested.
+
+Finally, you will need to implement a function that will return all the key-value pairs in a given inventory as a `list` of `tuples`.
-To finish, you will have to implement a function which returns all the key-value pairs in an inventory as a list of `tuples`.
## 1. Create an inventory based on a list
-Implement the `create_inventory()` function that creates an "inventory" from a list of items. It should return a `dict` containing each item name paired with their respective quantity.
+Implement the `create_inventory()` function that creates an "inventory" from an input list of items.
+It should return a `dict` containing each item name paired with their respective quantity.
```python
>>> create_inventory(["coal", "wood", "wood", "diamond", "diamond", "diamond"])
@@ -19,7 +23,7 @@ Implement the `create_inventory()` function that creates an "inventory" from a l
## 2. Add items from a list to an existing dictionary
-Implement the `add_items()` function that adds a list of items to an inventory:
+Implement the `add_items(, )` function that adds a list of items to the passed-in inventory:
```python
>>> add_items({"coal":1}, ["wood", "iron", "coal", "wood"])
@@ -28,23 +32,26 @@ Implement the `add_items()` function that adds a list of items to an inventory:
## 3. Decrement items from the inventory
-Implement the `decrement_items()` function that takes a `list` of items. The function should remove one from the available count in the inventory for each time an item appears on the `list`:
+Implement the `decrement_items(, )` function that takes a `list` of items.
+Your function should remove `1` from an item count for each time that item appears on the `list`:
```python
>>> decrement_items({"coal":3, "diamond":1, "iron":5}, ["diamond", "coal", "iron", "iron"])
{"coal":2, "diamond":0, "iron":3}
```
-Item counts in the inventory should not fall below 0. If the number of times an item appears on the list exceeds the count available, the quantity listed for that item should remain at 0 and additional requests for removing counts should be ignored.
+Item counts in the inventory should not be allowed to fall below 0.
+ If the number of times an item appears on the input `list` exceeds the count available, the quantity listed for that item should remain at 0.
+ Additional requests for removing counts should be ignored once the count falls to zero.
```python
>>> decrement_items({"coal":2, "wood":1, "diamond":2}, ["coal", "coal", "wood", "wood", "diamond"])
{"coal":0, "wood":0, "diamond":1}
```
-## 4. Remove an item entirely from the inventory
+## 4. Remove an entry entirely from the inventory
-Implement the `remove_item(, )` function that removes an item and its count entirely from an inventory:
+Implement the `remove_item(, )` function that removes an item and its count entirely from an inventory:
```python
>>> remove_item({"coal":2, "wood":1, "diamond":2}, "coal")
@@ -58,9 +65,10 @@ If the item is not found in the inventory, the function should return the origin
{"coal":2, "wood":1, "diamond":2}
```
-## 5. Return the inventory content
+## 5. Return the entire content of the inventory
-Implement the `list_inventory()` function that takes an inventory and returns a list of `(item, quantity)` tuples. The list should only include the available items (with a quantity greater than zero):
+Implement the `list_inventory()` function that takes an inventory and returns a list of `(item, quantity)` tuples.
+The list should only include the _available_ items (_with a quantity greater than zero_):
```python
>>> list_inventory({"coal":7, "wood":11, "diamond":2, "iron":7, "silver":0})
diff --git a/exercises/concept/inventory-management/.docs/introduction.md b/exercises/concept/inventory-management/.docs/introduction.md
index 1b4a15dc286..2b9ef011e1b 100644
--- a/exercises/concept/inventory-management/.docs/introduction.md
+++ b/exercises/concept/inventory-management/.docs/introduction.md
@@ -1,50 +1,79 @@
# Introduction
-A _**dictionary**_ is Python's primary mapping type that associates a _hashable key_ with a value. The lookup by key is more efficient than searching through an array, but does require more memory.
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
-## Dict construction
-Dictionaries can be created in various ways. Two simple options are the use the `dict()` class constructor or the dict literal declaration with key-value pairs.
+`Keys` must be hashable and unique across the dictionary.
+Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
-### Use the `dict()` constructor
+`values` can be of any data type or structure.
+ Values can also nest _arbitrarily_, so they can include lists-of-lists, sub-dictionaries, and other custom or compound data structures.
+
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
+
+
+## Dictionary Construction
+
+Dictionaries can be created in many ways.
+The two most straightforward are using the `dict()`constructor or declaring a `dict` _literal_.
+
+### The `dict()` Class Constructor
+
+`dict()` (_the constructor for the dictionary class_) can be used with any iterable of `key`, `value` pairs or with a series of `=` _arguments_:
```python
+#Passing a list of key,value tuples.
+>>> wombat = dict([('name', 'Wombat'),('speed', 23),('land_animal', True)])
+{'name': 'Wombat', 'speed': 23, 'land_animal': True}
+
+
+#Using key=value arguments.
>>> bear = dict(name="Black Bear", speed=40, land_animal=True)
{'name': 'Black Bear', 'speed': 40, 'land_animal': True}
```
-### Declare a _dict_ literal
+### Dictionary Literals
+
+A `dict` can also be directly entered as a _dictionary literal_, using curly brackets (`{}`) enclosing `key : value` pairs:
```python
>>> whale = {"name": "Blue Whale", "speed": 35, "land_animal": False}
{'name': 'Blue Whale', 'speed': 35, 'land_animal': False}
```
-With the dict literal declaration keep in mind that _keys_ are of _data types_ `str` and the colon `:` is used instead of an equal sign `=`.
+## Accessing Values in a Dictionary
-## Accessing values
-
-You can access an item in a dictionary using the _key_ of the value.
-
-### Using _square brackets_ after the dict object
+You can access an entry in a dictionary using a _key_ in square (`[]`) brackets.
+If a `key` does not exist n the `dict`, a `KeyError` is thrown:
```python
>>> bear["speed"]
40
+
+>>> bear["color"]
+Traceback (most recent call last):
+ File "", line 1, in
+KeyError: 'color'
```
-### Using `.get()`
+Accessing an entry via the `.get(, )` method can avoid the `KeyError`:
```python
->>> whale.get("name")
-'Blue Whale'
+>>> bear.get("color", 'not found')
+'not found'
```
-## Changing values
+## Changing or Adding Dictionary Values
-You can easily change a value of an item using its _key_.
+You can change an entry `value` by assigning to its _key_:
```python
+#Assigning the value "Grizzly Bear" to the name key.
>>> bear["name"] = "Grizzly Bear"
{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True}
@@ -52,29 +81,71 @@ You can easily change a value of an item using its _key_.
{'name': 'Blue Whale', 'speed': 25, 'land_animal': False}
```
-## Deleting values using keys
+New `key`:`value` pairs can be _added_ in the same fashion:
+
+```python
+# Adding an new "color" key with a new "tawney" value.
+>>> bear["color"] = 'tawney'
+{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True, 'color': 'tawney'}
-You can delete an item from a dictionary using `dict.pop()`. This will remove the (`key`, `value`) pair from the dictionary and return the `value` for use. `dict.pop()` accepts second argument, `default` that is returned if the `key` is not found (`dict.pop(, )`). Otherwise, a `KeyError` will be raised for any `key` that is missing.
+>>> whale["blowholes"] = 1
+{'name': 'Blue Whale', 'speed': 25, 'land_animal': False, 'blowholes': 1}
+```
+
+## Removing (Pop-ing) Dictionary Entries
+
+You can use the `.pop()` method to delete a dictionary entry.
+`.pop()` removes the (`key`, `value`) pair and returns the `value` for use.
+Like `.get()`, `.pop()` accepts second argument (_`dict.pop(, )`_) that will be returned if the `key` is not found.
+This prevents a `KeyError` being raised:
```python
+#Using .pop() removes both the key and value, returning the value.
>>> bear.pop("name")
'Grizzly Bear'
->>> bear.pop("name", "Unknown")
-'Unknown'
+
+
+#The "name" key is now removed from the dictionary.
+#Attempting .pop() a second time will throw a KeyError.
>>> bear.pop("name")
Traceback (most recent call last):
File "", line 1, in
KeyError: 'name'
+
+
+#Using a default argument with .pop() will prevent a KeyError from a missing key.
+>>> bear.pop("name", "Unknown")
+'Unknown'
```
-## Looping through a dictionary
+## Looping through/Iterating over a Dictionary
-Looping through a dictionary using `for item in dict` will iterate over the _keys_, but you can access the _values_ by using _square brackets_.
+Looping through a dictionary using `for item in dict` or `while item` will iterate over only the _keys _ by default.
+You can access the _values_ within the same loop by using _square brackets_:
```python
>>> for key in bear:
->>> (key, bear[key])
+>>> print((key, bear[key])) #this forms a tuple of (key, value) and prints it.
('name', 'Black Bear')
('speed', 40)
('land_animal', True)
```
+
+You can also use the `.items()` method, which returns (`key`, `value`) tuples automatically:
+
+```python
+#dict.items() forms (key, value tuples) that can be unpacked and iterated over.
+>>> for key, value in whale.items():
+>>> print(key, ":", value)
+name : Blue Whale
+speed : 25
+land_animal : False
+blowholes : 1
+```
+
+Likewise, the `.keys()` method will return `keys` and the `.values()` method will return the `values`.
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
+[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/exercises/concept/mecha-munch-management/.docs/hints.md b/exercises/concept/mecha-munch-management/.docs/hints.md
new file mode 100644
index 00000000000..9edd70abfe8
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.docs/hints.md
@@ -0,0 +1,56 @@
+# Hints
+
+## General
+
+Remember, this is an [MVP][mvp].
+That means you don't need to get too fancy with error handling or different "edge case" scenarios.
+It's OK to be simple and direct with the functions you are writing.
+
+The dictionary section of the [official tutorial][dicts-docs] and the mapping type [official library reference][mapping-types-dict] are excellent places to look for more help with all these methods.
+
+
+## 1. Add Item(s) to the Users Shopping Cart
+
+- You will need to iterate through each item in `items_to_add`.
+- You can avoid a `KeyError` when a key is missing by using a `dict` [method][set-default] that takes a _default value_ as one of its arguments.
+- It is also possible to accomplish the same thing manually in the `loop` by using some checking and error handling, but the `dict` method is easier.
+
+## 2. Read in Items Listed in the Users Notes App
+
+- Remember, Python's got a method for _everything_. This one is a _classmethod_ that's an easy way to [populate a `dict`][fromkeys] with keys.
+- This `dict` method returns a _new dictionary_, populated with default values. If no value is given, the default value will become `None`
+
+## 3. Update Recipe "Ideas" Section
+
+- Don't overthink this one! This can be solved in **one** `dict` method call.
+- The key word here is .... [_update_][update].
+
+## 4. Sort the Items in the User Cart
+
+- What method would you call to get an [iterable view of items][items] in the dictionary?
+- If you had a `list` or a `tuple`, what [`built-in`][builtins] function might you use to sort them?
+- The built-in function you want is the one that returns a _copy_, and doesn't mutate the original.
+
+## 5. Send User Shopping Cart to Store for Fulfillment
+
+- Having a fresh, empty dictionary here as the `fulfillment_cart` might be handy for adding in items.
+- `Looping` through the members of the cart might be the most direct way of accessing things here.
+- What method would you call to get an [iterable view of just the keys][keys] of the dictionary?
+- Remember that you can get the `value` of a given key by using `[]` syntax.
+- If you had a `list` or a `tuple`, what [`built-in`][builtins] function might you use to sort them?
+- Remember that the `built-in` function can take an optional `reversed=true` argument.
+
+## 6. Update the Store Inventory to Reflect what a User Has Ordered.
+
+- There is a method that will give you an iterable view of (`key`, `value`) pairs from the dictionary.
+- You can access an item in a _nested tuple_ using _bracket notation_: `[][]`
+- Don't forget to check if an inventory count falls to zero, you'll need to add in the "Out of Stock" message.
+
+[builtins]: https://docs.python.org/3/library/functions.html
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
+[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
+[items]: https://docs.python.org/3/library/stdtypes.html#dict.items
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[mvp]: https://en.wikipedia.org/wiki/Minimum_viable_product
+[set-default]: https://docs.python.org/3/library/stdtypes.html#dict.setdefault
+[update]: https://docs.python.org/3/library/stdtypes.html#dict.update
diff --git a/exercises/concept/mecha-munch-management/.docs/instructions.md b/exercises/concept/mecha-munch-management/.docs/instructions.md
new file mode 100644
index 00000000000..f5fa6f79012
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.docs/instructions.md
@@ -0,0 +1,129 @@
+# Instructions
+
+Mecha Munch™, a grocery shopping automation company has just hired you to work on their ordering app.
+Your team is tasked with building an MVP (_[minimum viable product][mvp]_) that manages all the basic shopping cart activities, allowing users to add, remove, and sort their grocery orders.
+Thankfully, a different team is handling all the money and check-out functions!
+
+## 1. Add Item(s) to the Users Shopping Cart
+
+The MVP should allow the user to add items to their shopping cart.
+This could be a single item or multiple items at once.
+Since this is an MVP, item quantity is indicated by _repeats_.
+If a user wants to add 2 Oranges, 'Oranges' will appear twice in the input iterable.
+If the user already has the item in their cart, the cart quantity should be increased by 1.
+If the item is _new_ to the cart, it should be added with a quantity of 1.
+
+Create the function `add_items(, )` that takes a cart dictionary and any list-like iterable of items to add as arguments.
+It should return a new/updated shopping cart dictionary for the user.
+
+```python
+>>> add_items({'Banana': 3, 'Apple': 2, 'Orange': 1},
+ ('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
+{'Banana': 4, 'Apple': 5, 'Orange': 2}
+
+>>> add_items({'Banana': 3, 'Apple': 2, 'Orange': 1},
+ ['Banana', 'Orange', 'Blueberries', 'Banana'])
+{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}
+```
+
+## 2. Read in Items Listed in the Users Notes App
+
+Uh-oh.
+Looks like the product team is engaging in [feature creep][feature creep].
+They want to add extra functionality to the MVP.
+The application now has to create a shopping cart by reading items off a users notes app.
+Convenient for the users, but slightly more work for the team.
+
+Create the function `read_notes()` that can take any list-like iterable as an argument.
+The function should parse the items and create a user shopping cart/dictionary.
+Each item should be added with a quantity of 1.
+The new user cart should then be returned.
+
+```python
+>>> read_notes(('Banana','Apple', 'Orange'))
+{'Banana': 1, 'Apple': 1, 'Orange': 1}
+
+>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
+{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}
+```
+
+## 3. Update Recipe "Ideas" Section
+
+The app has an "ideas" section that's filled with finished recipes from various cuisines.
+The user can select any one of these recipes and have all its ingredients added to their shopping cart automatically.
+The project manager has asked you create a way to edit these "ideas" recipes, since the content team keeps changing around ingredients and quantities.
+
+Create the function `update_recipes(, )` that takes an "ideas" dictionary and an iterable of recipe updates as arguments.
+The function should return the new/updated "ideas" dictionary.
+
+```python
+>>> update_recipes({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+ 'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+(('Banana Bread', {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),))
+...
+
+{'Banana Bread' : {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
+ 'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}
+
+>>> update_recipes({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+ 'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+ 'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
+[('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
+('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
+('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})])
+...
+
+{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+ 'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
+ 'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
+ 'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
+```
+
+## 4. Sort the Items in the User Cart
+
+Once a user has started a cart, the app allows them to sort their items alphabetically.
+This makes things easier to find, and helps when there are data-entry errors like having 'potatoes' and 'Potato' in the database.
+
+Create the function `sort_entries()` that takes a shopping cart/dictionary as an argument and returns a new, alphabetically sorted one.
+
+```python
+>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
+{'Apple': 2, 'Banana':3, 'Orange': 1}
+```
+
+## 5. Send User Shopping Cart to Store for Fulfillment
+
+The app needs to send a given users cart to the store for fulfillment.
+However, the shoppers in the store need to know which store isle the item can be found in and if the item needs refrigeration.
+So (_rather arbitrarily_) the "fulfillment cart" needs to be sorted in reverse alphabetical order with item quantities combined with location and refrigeration information.
+
+Create the function `send_to_store(, )` that takes a user shopping cart and a dictionary that has store isle number and a `True`/`False` for refrigeration needed for each item.
+The function should `return` a combined "fulfillment cart" that has (quantity, isle, and refrigeration) for each item the customer is ordering.
+Items should appear in _reverse_ alphabetical order.
+
+```python
+>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
+ {'Banana': ['Isle 5', False], 'Apple': ['Isle 4', False], 'Orange': ['Isle 4', False], 'Milk': ['Isle 2', True]})
+{'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True], 'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]}
+```
+
+## 6. Update the Store Inventory to Reflect what a User Has Ordered.
+
+The app can't just place customer orders endlessly.
+Eventually, the store is going to run out of various products.
+So your app MVP needs to update the store inventory every time a user sends their order to the store.
+Otherwise, customers will order products that aren't actually available.
+
+Create the function `update_store_inventory(, )` that takes a "fulfillment cart" and a store inventory.
+The function should reduce the store inventory amounts by the number "ordered" in the "fulfillment cart" and then return the updated store inventory.
+Where a store item count falls to 0, the count should be replaced by the message 'Out of Stock'.
+
+```python
+>>> update_store_inventory({'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True], 'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]},
+{'Banana': [15, 'Isle 5', False], 'Apple': [12, 'Isle 4', False], 'Orange': [1, 'Isle 4', False], 'Milk': [4, 'Isle 2', True]})
+
+{'Banana': [12, 'Isle 5', False], 'Apple': [10, 'Isle 4', False], 'Orange': ['Out of Stock', 'Isle 4', False], 'Milk': [2, 'Isle 2', True]}
+```
+
+[feature creep]: https://en.wikipedia.org/wiki/Feature_creep
+[mvp]: https://en.wikipedia.org/wiki/Minimum_viable_product
diff --git a/exercises/concept/mecha-munch-management/.docs/introduction.md b/exercises/concept/mecha-munch-management/.docs/introduction.md
new file mode 100644
index 00000000000..7088a08d98d
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.docs/introduction.md
@@ -0,0 +1,234 @@
+# Dictionary Methods in Python
+
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
+
+
+Given the `key`, dictionaries enable the retrieval of a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
+
+## Dictionary Methods
+
+The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
+Some were introduced in the concept for `dicts`.
+Here we cover a few more - along with some techniques for iterating through and manipulating dictionaries.
+
+### `setdefault()` for Error-Free Insertion
+
+The dictionary concept previously covered that `.get(key, )` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
+This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for `key` will be present.
+
+For a similarly "safe" (_without KeyError_) insertion operation, there is the `.setdefault(key, )` method.
+`setdefault(key, )` will return the `value` if the `key` is found in the dictionary.
+If the key is **not** found, it will _insert_ the (`key`, `default value`) pair and return the `default value` for use.
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+
+#Looking for the value associated with key "Rock Brown".
+#The key does not exist, so it is added with the default value, and the value is returned.
+>>> palette.setdefault('Rock Brown', '#694605')
+'#694605'
+
+#The (key, default value) pair has now been added to the dictionary.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
+```
+
+### Use `fromkeys()` to Populate a Dictionary
+
+To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(iterable, )`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
+All `values` will be set to the `default value` provided:
+
+```python
+>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
+
+{'Grassy Green': 'fill in hex color here',
+ 'Purple Mountains Majesty': 'fill in hex color here',
+ 'Misty Mountain Pink': 'fill in hex color here'}
+```
+
+### Iterating Over Entries in a Dictionary
+
+The `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views] of a dictionary.
+
+These views can be used for looping over entries without altering them.
+They are also _dynamic_ -- when underlying dictionary data changes, the associated view object will reflect the change:
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd'}
+
+#Using .keys() returns a list of keys.
+>>> palette_I.keys()
+dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
+
+#Using .values() returns a list of values.
+>>> palette_I.values()
+dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
+
+#Using .items() returns a list of (key, value) tuples.
+>>> palette_I.items()
+dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
+
+#Views are dynamic. Changing values in the dict changes all of the associated views.
+>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
+>>> palette_I['Deep Red'] = '#932432'
+
+>>> palette_I.values()
+dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
+
+>>> palette_I.keys()
+dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
+
+>>> palette_I.items()
+dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
+```
+
+### More on `.keys()`, `.values()`, and `.items()`
+
+In Python 3.7+, `dicts` preserve the order in which entries are inserted allowing First-in, First-out (_`FIFO`_), iteration using `.keys()`, `.values()`, or `.items()`.
+In Python 3.8+, views are also _reversible_.
+This allows keys, values, or (`key`, `value`) pairs to be iterated over in Last-in, First-out (`LIFO`) order by using `reversed(.keys())`, `reversed(.values())`, or `reversed(.items())`:
+
+```python
+>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+
+#Iterating in insertion order
+>>> for item in palette_II.items():
+... print(item)
+...
+('Factory Stone Purple', '#7c677f')
+('Green Treeline', '#478559')
+('Purple baseline', '#161748')
+
+
+#Iterating in the reverse direction.
+>>> for item in reversed(palette_II.items()):
+... print (item)
+...
+('Purple baseline', '#161748')
+('Green Treeline', '#478559')
+('Factory Stone Purple', '#7c677f')
+```
+
+### Sorting a Dictionary
+
+Dictionaries do not have a built-in sorting method.
+However, it is possible to sort a `dict` _view_ using the built-in function `sorted()` with `.items()`.
+The sorted view can then be used to create a new dictionary.
+Like iteration, the default sort is over dictionary `keys`.
+
+```python
+# Default ordering for a dictionary is last in, first out (LIFO).
+>>> color_palette = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd',
+ 'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple baseline': '#161748'}
+
+
+# The default sort order for a dictionary uses the keys.
+>>> sorted_palette = dict(sorted(color_palette.items()))
+>>> sorted_palette
+{'Factory Stone Purple': '#7c677f',
+ 'Grassy Green': '#9bc400',
+ 'Green Treeline': '#478559',
+ 'Misty Mountain Pink': '#f9c5bd',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Purple baseline': '#161748'}
+```
+
+### Combining Dictionaries with `.update()`
+
+`.update()` can be used to _combine_ two dictionaries.
+This method will take the (`key`,`value`) pairs of `` and write them into ``:
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_II = {'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple Baseline': '#161748'}
+
+>>> palette_I.update(palette_II)
+
+#Note that new items from palette_II are added.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
+```
+
+Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`:
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd',
+ 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
+ 'Misty Mountain Pink': (249, 197, 189)}
+>>> palette_I.update(palette_III)
+
+#Overlapping values in palette_I are replaced with values from palette_III
+>>> palette_I
+{'Grassy Green': (155, 196, 0),
+ 'Purple Mountains Majesty': (128, 118, 163),
+ 'Misty Mountain Pink': (249, 197, 189),
+ 'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+```
+
+### Merging and Updating Dictionaries Via the Union (`|`) Operators
+
+Python 3.9 introduces a different means of merging `dicts`: the `union` operators.
+`dict_one | dict_two` will create a **new dictionary**, made up of the (`key`, `value`) pairs of `dict_one` and `dict_two`.
+When both dictionaries share keys, `dict_two` values take precedence.
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+>>> new_dict = palette_I | palette_II
+>>> new_dict
+...
+{'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd',
+ 'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple baseline': '#161748'}
+```
+
+`dict_one |= other` behaves similar to `.update()`, but in this case, `other` can be either a `dict` or an iterable of (`key`, `value`) pairs:
+
+```python
+>>> palette_III = {'Grassy Green': (155, 196, 0),
+ 'Purple Mountains Majesty': (128, 118, 163),
+ 'Misty Mountain Pink': (249, 197, 189)}
+>>> new_dict |= palette_III
+>>> new_dict
+...
+{'Grassy Green': (155, 196, 0),
+'Purple Mountains Majesty': (128, 118, 163),
+'Misty Mountain Pink': (249, 197, 189),
+'Factory Stone Purple': '#7c677f',
+'Green Treeline': '#478559',
+'Purple baseline': '#161748'}
+```
+
+For a detailed explanation of dictionaries and methods for working with them, the [official tutorial][dicts-docs] and the [official library reference][mapping-types-dict] are excellent starting places.
+
+[Real Python][how-to-dicts] and [Finxter][fi-dict-guide] also have very thorough articles on Python dictionaries.
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
+[dict-methods]: https://docs.python.org/3/library/stdtypes.html#dict
+[dict-views]: https://docs.python.org/3/library/stdtypes.html#dict-views
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
+[fi-dict-guide]: https://blog.finxter.com/python-dictionary
+[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
+[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
+[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/exercises/concept/mecha-munch-management/.meta/config.json b/exercises/concept/mecha-munch-management/.meta/config.json
new file mode 100644
index 00000000000..f09d0f29537
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.meta/config.json
@@ -0,0 +1,21 @@
+{
+ "authors": [
+ "meatball133",
+ "BethanyG"
+ ],
+ "contributors": [
+ ],
+ "files": {
+ "solution": [
+ "dict_methods.py"
+ ],
+ "test": [
+ "dict_methods_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "gross-store",
+ "blurb": "Learn about dictionary methods by building a shopping cart MVP for the Mecha Munch grocery app."
+}
diff --git a/exercises/concept/mecha-munch-management/.meta/design.md b/exercises/concept/mecha-munch-management/.meta/design.md
new file mode 100644
index 00000000000..4c3c52a0d95
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.meta/design.md
@@ -0,0 +1,59 @@
+## Learning objectives
+
+Cover useful `dict` methods and a few techniques for operating on/manipulating `dicts`.
+
+- `dict.setdefault()` for automatically adding keys when needed.
+- `dict.fromkeys(iterable, )` for creating a new `dict` from any number of iterables.
+- `dict.keys()`, `dict.values()`, and `dict.items()` for convenient iterators.
+- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` for reversed views.
+- `sorted()` with `dict.items()`. for re-ordering entries in a `dict`.
+- `dict_one.update()` for updating one `dict` with overlapping values from another `dict`.
+- `dict | other_dict` and `dict |= other_dict` for merging or updating two `dict`s via operators.
+- `dict.popitem()` for removing and returning a key, value pair.
+
+- Working more with the `dict` views `items()` , `keys()` or `values()`. (e.g, by sorting information using `sorted()` or by swapping `keys` and `values`, etc.)
+- Knowing that Dictionaries can be _nested_, _-- e.g._ ' a dictionary of dictionaries'.
+- Considerations when `updating()` or using `union` with dictionaries.
+
+## Out of scope
+
+Please take a look at the `dicts` concept exercise [design.md file](https://github.com/exercism/python/edit/main/exercises/concept/inventory-management/.meta/design.md) for `dict` features taught thus far.
+While those methods can be used for solutions to this exercise, it isn't necessary to cover them again in detail. Additionally, the following is out of scope:
+
+- Dictionary comprehensions
+- Built-in functions as they relate to this data structure (*e.g.* `len()`, or `enumerate()`
+- Considerations of Mutability
+- `copy()` vs `deepcopy()`
+- Memory and performance characteristics.
+- Related `collections` module with `Counter()` and `defaultdict()`
+
+## Concepts
+
+- `dicts`
+- `dict-methods`
+
+## Prerequisites
+
+These are the concepts/concept exercises the student needs to complete/understand before solving this concept exercise.
+
+- `basics`
+- `bools`
+- `conditionals`
+- `comparisons`
+- `dicts`
+- `lists`
+- `loops`
+- `numbers`
+- `strings`
+- `tuples`
+
+
+## Resources to refer to
+
+- [Python docs: Tutorial - Dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries)
+- [Python docs: Mapping Type `dict`](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict)
+- [Real Python: Dicts](https://realpython.com/python-dicts/)
+- [Digital Ocean: Understanding dictionaries in python 3](https://www.digitalocean.com/community/tutorials/understanding-dictionaries-in-python-3)
+- [Stack Overflow: exchanging keys with values in a `dict` in Python](https://stackoverflow.com/questions/1031851/how-do-i-exchange-keys-with-values-in-a-dictionary)
+- [kite: how to sort a dictionary by key in python](https://www.kite.com/python/answers/how-to-sort-a-dictionary-by-key-in-python)
+- [medium: 16 Python Dictionary Tips](https://medium.com/python-in-plain-english/16-intermediate-level-python-dictionary-tips-tricks-and-shortcuts-1376859e1adc) _**note:** this is a good resource for ideas and writing this exericse, but is a subscription-based service, so not the best for linking to_
\ No newline at end of file
diff --git a/exercises/concept/mecha-munch-management/.meta/exemplar.py b/exercises/concept/mecha-munch-management/.meta/exemplar.py
new file mode 100644
index 00000000000..0390944dbbd
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.meta/exemplar.py
@@ -0,0 +1,79 @@
+"""Functions to manage a users shopping cart items."""
+
+
+def add_item(current_cart, items_to_add):
+ """Add items to shopping cart.
+
+ :param current_cart: dict - the current shopping cart.
+ :param items_to_add: iterable - items to add to the cart.
+ :return: dict - the updated user cart dictionary.
+ """
+
+ for item in items_to_add:
+ current_cart.setdefault(item, 0)
+ current_cart[item] += 1
+
+ return current_cart
+
+
+def read_notes(notes):
+ """Create user cart from an iterable notes entry.
+
+ :param notes: iterable of items to add to cart.
+ :return: dict - a user shopping cart dictionary.
+ """
+
+ return dict.fromkeys(notes, 1)
+
+
+def update_recipes(ideas, recipe_updates):
+ """Update the recipe ideas dictionary.
+
+ :param ideas: dict - The "recipe ideas" dict.
+ :param recipe_updates: dict - dictionary with updates for the ideas section.
+ :return: dict - updated "recipe ideas" dict.
+ """
+
+ ideas.update(recipe_updates)
+ return ideas
+
+
+def sort_entries(cart):
+ """Sort a users shopping cart in alphabetically order.
+
+ :param cart: dict - a users shopping cart dictionary.
+ :return: dict - users shopping cart sorted in alphabetical order.
+ """
+
+ return dict(sorted(cart.items()))
+
+
+def send_to_store(cart, isle_mapping):
+ """Combine users order to isle and refrigeration information.
+
+ :param cart: dict - users shopping cart dictionary.
+ :param isle_mapping: dict - isle and refrigeration information dictionary.
+ :return: dict - fulfillment dictionary ready to send to store.
+ """
+ fulfillment_cart = {}
+
+ for key in cart.keys():
+ fulfillment_cart[key] = [cart[key]] + isle_mapping[key]
+
+ return dict(sorted(fulfillment_cart.items(), reverse=True))
+
+
+def update_store_inventory(fulfillment_cart, store_inventory):
+ """Update store inventory levels with user order.
+
+ :param fulfillment cart: dict - fulfillment cart to send to store.
+ :param store_inventory: dict - store available inventory
+ :return: dict - store_inventory updated.
+ """
+
+ for key, values in fulfillment_cart.items():
+ store_inventory[key][0] = store_inventory[key][0] - values[0]
+ if store_inventory[key][0] == 0:
+ store_inventory[key][0] = 'Out of Stock'
+
+ return store_inventory
diff --git a/exercises/concept/mecha-munch-management/dict_methods.py b/exercises/concept/mecha-munch-management/dict_methods.py
new file mode 100644
index 00000000000..d443c8bca54
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/dict_methods.py
@@ -0,0 +1,65 @@
+"""Functions to manage a users shopping cart items."""
+
+
+def add_item(current_cart, items_to_add):
+ """Add items to shopping cart.
+
+ :param current_cart: dict - the current shopping cart.
+ :param items_to_add: iterable - items to add to the cart.
+ :return: dict - the updated user cart dictionary.
+ """
+
+ pass
+
+
+def read_notes(notes):
+ """Create user cart from an iterable notes entry.
+
+ :param notes: iterable of items to add to cart.
+ :return: dict - a user shopping cart dictionary.
+ """
+
+ pass
+
+
+def update_recipes(ideas, recipe_updates):
+ """Update the recipe ideas dictionary.
+
+ :param ideas: dict - The "recipe ideas" dict.
+ :param recipe_updates: dict - dictionary with updates for the ideas section.
+ :return: dict - updated "recipe ideas" dict.
+ """
+
+ pass
+
+
+def sort_entries(cart):
+ """Sort a users shopping cart in alphabetically order.
+
+ :param cart: dict - a users shopping cart dictionary.
+ :return: dict - users shopping cart sorted in alphabetical order.
+ """
+
+ pass
+
+
+def send_to_store(cart, isle_mapping):
+ """Combine users order to isle and refrigeration information.
+
+ :param cart: dict - users shopping cart dictionary.
+ :param isle_mapping: dict - isle and refrigeration information dictionary.
+ :return: dict - fulfillment dictionary ready to send to store.
+ """
+
+ pass
+
+
+def update_store_inventory(fulfillment_cart, store_inventory):
+ """Update store inventory levels with user order.
+
+ :param fulfillment cart: dict - fulfillment cart to send to store.
+ :param store_inventory: dict - store available inventory
+ :return: dict - store_inventory updated.
+ """
+
+ pass
diff --git a/exercises/concept/mecha-munch-management/dict_methods_test.py b/exercises/concept/mecha-munch-management/dict_methods_test.py
new file mode 100644
index 00000000000..2f5828d615f
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/dict_methods_test.py
@@ -0,0 +1,161 @@
+import unittest
+import pytest
+from dict_methods import (add_item,
+ read_notes,
+ update_recipes,
+ sort_entries,
+ send_to_store,
+ update_store_inventory)
+
+
+class MechaMunchManagementTest(unittest.TestCase):
+
+ @pytest.mark.task(taskno=1)
+ def test_add_item(self):
+ input_data = [
+ ({'Apple': 1, 'Banana': 4 }, ('Apple', 'Banana', 'Orange')),
+ ({'Orange': 1, 'Raspberry': 1, 'Blueberries': 10}, ['Raspberry', 'Blueberries', 'Raspberry']),
+ ({'Broccoli': 1, 'Banana': 1}, ('Broccoli', 'Kiwi', 'Kiwi', 'Kiwi', 'Melon', 'Apple', 'Banana', 'Banana'))
+ ]
+
+ output_data = [{'Apple': 2, 'Banana': 5, 'Orange': 1},
+ {'Orange': 1, 'Raspberry': 3, 'Blueberries': 11},
+ {'Broccoli': 2, 'Banana': 3, 'Kiwi': 3, 'Melon': 1, 'Apple': 1}]
+
+ 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):
+ error_msg=f'Expected: {output_data} but got a different shopping cart.'
+ self.assertEqual(add_item(input_data[0], input_data[1]), output_data, msg=error_msg)
+
+
+ @pytest.mark.task(taskno=2)
+ def test_read_notes(self):
+ input_data = [('Apple', "Banana"), ('Orange', 'Raspberry', 'Blueberries'),
+ ['Broccoli', 'Kiwi', 'Melon', 'Apple', 'Banana']]
+
+ output_data = [{'Apple': 1, 'Banana': 1}, {'Orange': 1, 'Raspberry': 1, 'Blueberries': 1},
+ {'Broccoli': 1, 'Kiwi': 1, 'Melon': 1, 'Apple': 1, 'Banana': 1}]
+
+ 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):
+ error_msg=f'Expected: {output_data} but got a different shopping cart.'
+ self.assertEqual(read_notes(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=3)
+ def test_update_recipes(self):
+ input_data = [
+ ({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+ 'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+ (('Banana Bread', {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),)),
+
+ ({'Apple Pie': {'Apple': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+ 'Blueberry Pie': {'Blueberries': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+ (('Blueberry Pie', {'Blueberries': 2, 'Pie Crust': 1, 'Cream Custard': 1}),
+ ('Apple Pie', {'Apple': 1, 'Pie Crust': 1, 'Cream Custard': 1}))),
+
+ ({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+ 'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+ 'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
+ (('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
+ ('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
+ ('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})))
+ ]
+
+ output_data = [
+ {'Banana Bread': {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
+ 'Raspberry Pie': {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+ {'Apple Pie': {'Apple': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+ 'Blueberry Pie': {'Blueberries': 2, 'Pie Crust': 1, 'Cream Custard': 1}},
+ {'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+ 'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
+ 'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
+ 'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
+ ]
+
+ 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):
+ error_msg=f'Expected: {output_data} but got a different ideas instead.'
+ self.assertEqual(update_recipes(input_data[0], input_data[1]), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=4)
+ def test_sort_entries(self):
+ input_data = [
+ {'Banana': 4, 'Apple': 2, 'Orange': 1, 'Pear': 12},
+ {'Apple': 3, 'Orange': 5, 'Banana': 1, 'Avocado': 2},
+ {'Orange': 3, 'Banana': 2, 'Apple': 1},
+ {'Apple': 2, 'Raspberry': 2, 'Blueberries': 5, 'Broccoli' : 2, 'Kiwi': 1, 'Melon': 4}
+ ]
+
+ output_data = [
+ {'Apple': 2, 'Banana': 4, 'Orange': 1, 'Pear': 12},
+ {'Avocado': 2, 'Apple': 3, 'Banana': 1, 'Orange': 5},
+ {'Apple': 1, 'Orange': 3, 'Banana': 2},
+ {'Apple' : 2, 'Blueberries': 5, 'Broccoli': 2, 'Kiwi': 1, 'Melon': 4, 'Raspberry': 2}
+ ]
+
+ 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):
+ error_msg=f'Expected: {output_data} but got a different sorted list instead.'
+ self.assertEqual(sort_entries(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=5)
+ def test_send_to_store(self):
+ input_data = [
+ ({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
+ {'Banana': ['Isle 5', False], 'Apple': ['Isle 4', False], 'Orange': ['Isle 4', False], 'Milk': ['Isle 2', True]}),
+
+ ({'Kiwi': 3, 'Juice': 5, 'Yoghurt': 2, 'Milk': 5},
+ {'Kiwi': ['Isle 6', False], 'Juice': ['Isle 5', False], 'Yoghurt': ['Isle 2', True], 'Milk': ['Isle 2', True]}),
+
+ ({'Apple': 2, 'Raspberry': 2, 'Blueberries': 5, 'Broccoli' : 2, 'Kiwi': 1, 'Melon': 4},
+ {'Apple': ['Isle 1', False], 'Raspberry': ['Isle 6', False], 'Blueberries': ['Isle 6', False],
+ 'Broccoli': ['Isle 3', False], 'Kiwi': ['Isle 6', False], 'Melon': ['Isle 6', False]})
+ ]
+
+ output_data = [
+ {'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True], 'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]},
+ {'Juice': [5, 'Isle 5', False], 'Yoghurt': [2, 'Isle 2', True], 'Milk': [5, 'Isle 2', True], 'Kiwi': [3, 'Isle 6', False]},
+ {'Kiwi': [1, 'Isle 6', False], 'Melon': [4, 'Isle 6', False], 'Apple': [2, 'Isle 1', False],
+ 'Raspberry': [2, 'Isle 6', False], 'Blueberries': [5, 'Isle 6', False], 'Broccoli': [2, 'Isle 3', False]}
+ ]
+
+ 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):
+ error_msg=f'Expected: {output_data} but got a different fulfillment_cart instead.'
+ self.assertEqual(send_to_store(input_data[0], input_data[1]), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=6)
+ def test_update_store_inventory(self):
+ input_data = [
+ ({'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True],
+ 'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]},
+ {'Banana': [15, 'Isle 5', False], 'Apple': [12, 'Isle 4', False],
+ 'Orange': [1, 'Isle 4', False], 'Milk': [4, 'Isle 2', True]}),
+
+ ({'Kiwi': [3, 'Isle 6', False]},{'Kiwi': [3, 'Isle 6', False], 'Juice': [5, 'Isle 5', False],
+ 'Yoghurt': [2, 'Isle 2', True], 'Milk': [5, 'Isle 2', True]}),
+
+ ({'Kiwi': [1, 'Isle 6', False], 'Melon': [4, 'Isle 6', False], 'Apple': [2, 'Isle 1', False],
+ 'Raspberry': [2, 'Isle 6', False], 'Blueberries': [5, 'Isle 6', False],
+ 'Broccoli': [1, 'Isle 3', False]},
+ {'Apple': [2, 'Isle 1', False], 'Raspberry': [5, 'Isle 6', False],
+ 'Blueberries': [10, 'Isle 6', False], 'Broccoli': [4, 'Isle 3', False],
+ 'Kiwi': [1, 'Isle 6', False], 'Melon': [8, 'Isle 6', False]})
+ ]
+
+ output_data = [
+ {'Banana': [12, 'Isle 5', False], 'Apple': [10, 'Isle 4', False],
+ 'Orange': ['Out of Stock', 'Isle 4', False], 'Milk': [2, 'Isle 2', True]},
+
+ {'Juice': [5, 'Isle 5', False], 'Yoghurt': [2, 'Isle 2', True],
+ 'Milk': [5, 'Isle 2', True], 'Kiwi': ["Out of Stock", 'Isle 6', False]},
+
+ {'Kiwi': ['Out of Stock', 'Isle 6', False], 'Melon': [4, 'Isle 6', False],
+ 'Apple': ['Out of Stock', 'Isle 1', False], 'Raspberry': [3, 'Isle 6', False],
+ 'Blueberries': [5, 'Isle 6', False], 'Broccoli': [3, 'Isle 3', False]}
+ ]
+
+ 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):
+ error_msg=f'Expected: {output_data} but got a different store inventory instead.'
+ self.assertEqual(update_store_inventory(input_data[0], input_data[1]), output_data, msg=error_msg)
From 4206810a2c6f5ace847592d6307e82577ff2ed88 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 5 Jul 2023 06:15:01 -0700
Subject: [PATCH 092/126] Concept tidy-up and typo fixes.
---
concepts/dict-methods/about.md | 79 ++++++++-----------
concepts/dict-methods/introduction.md | 27 ++-----
concepts/dicts/about.md | 57 +++++++------
concepts/dicts/introduction.md | 2 +
.../.docs/introduction.md | 57 ++++++-------
5 files changed, 97 insertions(+), 125 deletions(-)
diff --git a/concepts/dict-methods/about.md b/concepts/dict-methods/about.md
index 20625b1c4f5..d910d3e9168 100644
--- a/concepts/dict-methods/about.md
+++ b/concepts/dict-methods/about.md
@@ -1,29 +1,20 @@
# Dictionary Methods in Python
-A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
-Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
-As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
-
-Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
-Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
-Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
-
-## Dictionary Methods
-
The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
Some were introduced in the concept for `dicts`.
Here we cover a few more - along with some techniques for iterating through and manipulating dictionaries.
-- `dict.setdefault()` for automatically adding keys without error.
-- `dict.fromkeys(iterable, )` for creating a new `dict` from any number of iterables.
-- `.keys()`, `.values()`, and `.items()` for convenient iterators.
-- `sorted(.items())`. for re-ordering entries in a `dict`.
-- `dict_one.update()` for updating one `dict` with overlapping values from another `dict`.
-- `dict | other_dict` and `dict |= other_dict` for merging or updating two `dict`s via operators.
-- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` for reversed views.
-- `.popitem()` for removing and returning a `key`, `value` pair.
+- `dict.setdefault()` automatically adds keys without throwing a KeyError.
+- `dict.fromkeys(iterable, )` creates a new `dict` from any number of iterables.
+- `.keys()`, `.values()`, and `.items()` provide convenient iterators.
+- `sorted(.items())`. can easily re-order entries in a `dict`.
+- `dict_one.update()` updates one `dict` with overlapping values from another `dict`.
+- `dict | other_dict` and `dict |= other_dict` merges or updates two `dict`s via operators.
+- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` produce _reversed_ views.
+- `.popitem()` removes and returns a `key`, `value` pair.
-### `setdefault()` for Error-Free Insertion
+
+## `setdefault()` for Error-Free Insertion
The dictionary concept previously covered that `.get(key, )` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for `key` will be present.
@@ -35,18 +26,17 @@ If the key is **not** found, it will _insert_ the (`key`, `default value`) pair
```python
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
-#Looking for the value associated with key "Rock Brown".
-#The key does not exist, so it is added with the default
-# value, and the value is returned.
+# Looking for the value associated with key "Rock Brown".The key does not exist,
+# so it is added with the default value, and the value is returned.
>>> palette.setdefault('Rock Brown', '#694605')
'#694605'
-#The (key, default value) pair has now been added to the dictionary.
+# The (key, default value) pair has now been added to the dictionary.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
```
-### Use `fromkeys()` to Populate a Dictionary
+## `fromkeys()` to Populate a Dictionary from an Iterable
To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(iterable, )`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
All `values` will be set to the `default value` provided:
@@ -59,7 +49,7 @@ All `values` will be set to the `default value` provided:
'Misty Mountain Pink': 'fill in hex color here'}
```
-### Removing and Returning a (key, value) Pair With `.popitem()`
+## Remove and Return a (key, value) Pair With `.popitem()`
`.popitem()` removes & returns a single (`key`, `value`) pair from a dictionary.
Pairs are returned in Last-in-First-out (`LIFO`) order.
@@ -78,7 +68,7 @@ If the dictionary is empty, calling `popitem()` will raise a `KeyError`:
>>> palette_I.popitem()
('Grassy Green', '#9bc400')
-#All (key, value) pairs have been removed.
+# All (key, value) pairs have been removed.
>>> palette_I.popitem()
Traceback (most recent call last):
@@ -88,30 +78,31 @@ Traceback (most recent call last):
KeyError: 'popitem(): dictionary is empty'
```
-### Iterating Over Entries in a Dictionary
+## Iterating Over Entries in a Dictionary Via Views
The `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views] of a dictionary.
-These views can be used for looping over entries without altering them.
-They are also _dynamic_ -- when underlying dictionary data changes, the associated view object will reflect the change:
+
+These views can be used to easily loop over entries without altering them.
+Views are also _dynamic_ -- when underlying dictionary data changes, the associated `view object` will reflect the change:
```python
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
-#Using .keys() returns a list of keys.
+# Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
-#Using .values() returns a list of values.
+# Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
-#Using .items() returns a list of (key, value) tuples.
+# Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
-#Views are dynamic. Changing values in the dict
+# Views are dynamic. Changing values in the dict
# changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
>>> palette_I['Deep Red'] = '#932432'
@@ -126,11 +117,12 @@ dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'D
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
```
-### More on `.keys()`, `.values()`, and `.items()`
+## More on `.keys()`, `.values()`, and `.items()`
+
+In Python 3.7+, `dicts` preserve the order in which entries are inserted allowing First-in, First-out (_`FIFO`_), iteration when using `.keys()`, `.values()`, or `.items()`.
-In Python 3.7+, `dicts` preserve the order in which entries are inserted allowing First-in, First-out (_`FIFO`_), iteration using `.keys()`, `.values()`, or `.items()`.
In Python 3.8+, views are also _reversible_.
-This allows keys, values, or (key, value) pairs to be iterated over in Last-in, First-out (`LIFO`) order by using `reversed(.keys())`, `reversed(.values())`, or `reversed(.items())`:
+This allows keys, values, or (`key`, `value`) pairs to be iterated over in Last-in, First-out (`LIFO`) order by using `reversed(.keys())`, `reversed(.values())`, or `reversed(.items())`:
```python
>>> palette_II = {'Factory Stone Purple': '#7c677f',
@@ -151,7 +143,7 @@ This allows keys, values, or (key, value) pairs to be iterated over in Last-in,
('Factory Stone Purple', '#7c677f')
```
-### Combining Dictionaries with `.update()`
+## Combine Dictionaries with `.update()`
`