From 52fd4f3f8c07f179e91263627f54d435b7614b87 Mon Sep 17 00:00:00 2001
From: John
Date: Mon, 11 Jul 2022 04:54:11 +0800
Subject: [PATCH 001/932] Update about.md
corrected the result for `cat_words.split(',')` at line 143
---
concepts/strings/about.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/strings/about.md b/concepts/strings/about.md
index e9da24237d4..895bad45b42 100644
--- a/concepts/strings/about.md
+++ b/concepts/strings/about.md
@@ -140,7 +140,7 @@ Using `.split()` without any arguments will split the string on whitespace.
>>> cat_words = "feline, four-footed, ferocious, furry"
>>> cat_words.split(',')
...
-['feline, four-footed, ferocious, furry']
+['feline', ' four-footed', ' ferocious', ' furry']
>>> colors = """red,
From b3d6216cfd5567cf1c964d2b264b323d51928467 Mon Sep 17 00:00:00 2001
From: Aleksandar Ratesic
Date: Fri, 8 Jul 2022 16:44:10 +0200
Subject: [PATCH 002/932] Improve docs for Decorators concept
---
concepts/decorators/.meta/config.json | 6 +++---
concepts/decorators/introduction.md | 8 +++++++-
concepts/decorators/links.json | 24 ++++++++++++++++--------
3 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/concepts/decorators/.meta/config.json b/concepts/decorators/.meta/config.json
index 9b9e8da5a9b..a776cb16d89 100644
--- a/concepts/decorators/.meta/config.json
+++ b/concepts/decorators/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "cmccandless"],
- "contributors": []
+ "blurb": "Decorators are higher-order functions that take other functions as arguments to extend/modify their behavior and return the modified functions. They can be applied to classes as well, in which case they will take a class as an argument instead of a function.",
+ "authors": ["BethanyG", "bobahop", "kotp", "mathstrains21"],
+ "contributors": ["velaco"]
}
diff --git a/concepts/decorators/introduction.md b/concepts/decorators/introduction.md
index fcde74642ca..e17128a3df8 100644
--- a/concepts/decorators/introduction.md
+++ b/concepts/decorators/introduction.md
@@ -1,2 +1,8 @@
-#TODO: Add introduction for this concept.
+# Introduction
+Functions are first-class objects in Python, which means they can also be passed as arguments to other functions.
+Decorators are [higher-order functions][hofunc] that take another function as an argument and return it after extending or modifying its behavior.
+Decorators are defined in the same way as any other function, but they are applied on the line above the functions they are decorating using the `@` symbol before their names (`@my_decorator`). While they can take multiple arguments, decorators must take _at least_ the function they are decorating as an argument.
+They can also be applied to class methods and even whole classes for the same reason - to extend them or modify their behaviors.
+
+[hofunc]:https://en.wikipedia.org/wiki/Higher-order_function
diff --git a/concepts/decorators/links.json b/concepts/decorators/links.json
index eb5fb7c38a5..76a770b5aa1 100644
--- a/concepts/decorators/links.json
+++ b/concepts/decorators/links.json
@@ -1,18 +1,26 @@
[
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://peps.python.org/pep-0318/",
+ "description": "PEP 318 – Decorators for Functions and Methods"
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://peps.python.org/pep-3129/",
+ "description": "PEP 3129 – Class Decorators"
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://www.geeksforgeeks.org/first-class-functions-python/",
+ "description": "First Class Functions in Python"
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://www.geeksforgeeks.org/higher-order-functions-in-python/",
+ "description": "Higher Order Functions in Python"
+ },
+ {
+ "url": "https://realpython.com/primer-on-python-decorators/",
+ "description": "Primer on Python Decorators"
+ },
+ {
+ "url": "https://www.geeksforgeeks.org/chain-multiple-decorators-in-python/",
+ "description": "Chain Multiple Decorators in Python"
}
]
From c38530e911f5c86e4dd064029e0b629584415608 Mon Sep 17 00:00:00 2001
From: Aleksandar Ratesic
Date: Mon, 11 Jul 2022 17:03:07 +0200
Subject: [PATCH 003/932] Remove class decorators from concept docs.
---
concepts/decorators/.meta/config.json | 2 +-
concepts/decorators/introduction.md | 1 -
concepts/decorators/links.json | 4 ----
3 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/concepts/decorators/.meta/config.json b/concepts/decorators/.meta/config.json
index a776cb16d89..5f459dbce98 100644
--- a/concepts/decorators/.meta/config.json
+++ b/concepts/decorators/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "Decorators are higher-order functions that take other functions as arguments to extend/modify their behavior and return the modified functions. They can be applied to classes as well, in which case they will take a class as an argument instead of a function.",
+ "blurb": "Decorators are higher-order functions that take other functions as arguments to extend/modify their behavior and return the modified functions.",
"authors": ["BethanyG", "bobahop", "kotp", "mathstrains21"],
"contributors": ["velaco"]
}
diff --git a/concepts/decorators/introduction.md b/concepts/decorators/introduction.md
index e17128a3df8..5bfc4720f20 100644
--- a/concepts/decorators/introduction.md
+++ b/concepts/decorators/introduction.md
@@ -3,6 +3,5 @@
Functions are first-class objects in Python, which means they can also be passed as arguments to other functions.
Decorators are [higher-order functions][hofunc] that take another function as an argument and return it after extending or modifying its behavior.
Decorators are defined in the same way as any other function, but they are applied on the line above the functions they are decorating using the `@` symbol before their names (`@my_decorator`). While they can take multiple arguments, decorators must take _at least_ the function they are decorating as an argument.
-They can also be applied to class methods and even whole classes for the same reason - to extend them or modify their behaviors.
[hofunc]:https://en.wikipedia.org/wiki/Higher-order_function
diff --git a/concepts/decorators/links.json b/concepts/decorators/links.json
index 76a770b5aa1..dfaa663c436 100644
--- a/concepts/decorators/links.json
+++ b/concepts/decorators/links.json
@@ -3,10 +3,6 @@
"url": "https://peps.python.org/pep-0318/",
"description": "PEP 318 – Decorators for Functions and Methods"
},
- {
- "url": "https://peps.python.org/pep-3129/",
- "description": "PEP 3129 – Class Decorators"
- },
{
"url": "https://www.geeksforgeeks.org/first-class-functions-python/",
"description": "First Class Functions in Python"
From ff3a9117c5a674b3aabc76d211aabbb2aa2d36a3 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 11 Jul 2022 13:31:13 -0700
Subject: [PATCH 004/932] Update concepts/decorators/.meta/config.json
---
concepts/decorators/.meta/config.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/concepts/decorators/.meta/config.json b/concepts/decorators/.meta/config.json
index 5f459dbce98..57692af26e6 100644
--- a/concepts/decorators/.meta/config.json
+++ b/concepts/decorators/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "Decorators are higher-order functions that take other functions as arguments to extend/modify their behavior and return the modified functions.",
- "authors": ["BethanyG", "bobahop", "kotp", "mathstrains21"],
- "contributors": ["velaco"]
+ "authors": ["BethanyG", "bobahop", "kotp", "mathstrains21", "velaco"],
+ "contributors": []
}
From 609ae237b65d86773e338e0b0f3abda8565aeeda Mon Sep 17 00:00:00 2001
From: ThomasWard <22226719+tward333@users.noreply.github.com>
Date: Wed, 13 Jul 2022 20:48:42 -0400
Subject: [PATCH 005/932] Update sets.py
Update helper text to maintain consistency of constant names between docs and sets_categories_data.py
---
exercises/concept/cater-waiter/sets.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/cater-waiter/sets.py b/exercises/concept/cater-waiter/sets.py
index 9c1680c37fa..b0202e6a5fb 100644
--- a/exercises/concept/cater-waiter/sets.py
+++ b/exercises/concept/cater-waiter/sets.py
@@ -99,12 +99,12 @@ def singleton_ingredients(dishes, intersection):
"""Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes).
:param dishes: list - of ingredient sets.
- :param intersection: constant - can be one of `_INTERSECTION` constants imported from `sets_categories_data.py`.
+ :param intersection: constant - can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`.
:return: set - containing singleton ingredients.
Each dish is represented by a `set` of its ingredients.
- Each `_INTERSECTION` is an `intersection` of all dishes in the category. `` can be any one of:
+ Each `_INTERSECTIONS` is an `intersection` of all dishes in the category. `` can be any one of:
(VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE).
The function should return a `set` of ingredients that only appear in a single dish.
From 539018d192ccdcf0751116d1a932280034feed96 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 22 Jul 2022 03:02:34 -0700
Subject: [PATCH 006/932] Synced new test cases for triangle and regenerated
test file.
---
exercises/practice/triangle/.meta/tests.toml | 57 ++++++++++++--------
exercises/practice/triangle/triangle_test.py | 8 ++-
2 files changed, 42 insertions(+), 23 deletions(-)
diff --git a/exercises/practice/triangle/.meta/tests.toml b/exercises/practice/triangle/.meta/tests.toml
index 59107059ccd..7db091648dc 100644
--- a/exercises/practice/triangle/.meta/tests.toml
+++ b/exercises/practice/triangle/.meta/tests.toml
@@ -1,60 +1,73 @@
-# 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.
[8b2c43ac-7257-43f9-b552-7631a91988af]
-description = "all sides are equal"
+description = "equilateral triangle -> all sides are equal"
[33eb6f87-0498-4ccf-9573-7f8c3ce92b7b]
-description = "any side is unequal"
+description = "equilateral triangle -> any side is unequal"
[c6585b7d-a8c0-4ad8-8a34-e21d36f7ad87]
-description = "no sides are equal"
+description = "equilateral triangle -> no sides are equal"
[16e8ceb0-eadb-46d1-b892-c50327479251]
-description = "all zero sides is not a triangle"
+description = "equilateral triangle -> all zero sides is not a triangle"
[3022f537-b8e5-4cc1-8f12-fd775827a00c]
-description = "sides may be floats"
+description = "equilateral triangle -> sides may be floats"
[cbc612dc-d75a-4c1c-87fc-e2d5edd70b71]
-description = "last two sides are equal"
+description = "isosceles triangle -> last two sides are equal"
[e388ce93-f25e-4daf-b977-4b7ede992217]
-description = "first two sides are equal"
+description = "isosceles triangle -> first two sides are equal"
[d2080b79-4523-4c3f-9d42-2da6e81ab30f]
-description = "first and last sides are equal"
+description = "isosceles triangle -> first and last sides are equal"
[8d71e185-2bd7-4841-b7e1-71689a5491d8]
-description = "equilateral triangles are also isosceles"
+description = "isosceles triangle -> equilateral triangles are also isosceles"
[840ed5f8-366f-43c5-ac69-8f05e6f10bbb]
-description = "no sides are equal"
+description = "isosceles triangle -> no sides are equal"
[2eba0cfb-6c65-4c40-8146-30b608905eae]
-description = "first triangle inequality violation"
+description = "isosceles triangle -> first triangle inequality violation"
[278469cb-ac6b-41f0-81d4-66d9b828f8ac]
-description = "second triangle inequality violation"
+description = "isosceles triangle -> second triangle inequality violation"
[90efb0c7-72bb-4514-b320-3a3892e278ff]
-description = "third triangle inequality violation"
+description = "isosceles triangle -> third triangle inequality violation"
[adb4ee20-532f-43dc-8d31-e9271b7ef2bc]
-description = "sides may be floats"
+description = "isosceles triangle -> sides may be floats"
[e8b5f09c-ec2e-47c1-abec-f35095733afb]
-description = "no sides are equal"
+description = "scalene triangle -> no sides are equal"
[2510001f-b44d-4d18-9872-2303e7977dc1]
-description = "all sides are equal"
+description = "scalene triangle -> all sides are equal"
[c6e15a92-90d9-4fb3-90a2-eef64f8d3e1e]
-description = "two sides are equal"
+description = "scalene triangle -> first and second sides are equal"
+
+[3da23a91-a166-419a-9abf-baf4868fd985]
+description = "scalene triangle -> first and third sides are equal"
+
+[b6a75d98-1fef-4c42-8e9a-9db854ba0a4d]
+description = "scalene triangle -> second and third sides are equal"
[70ad5154-0033-48b7-af2c-b8d739cd9fdc]
-description = "may not violate triangle inequality"
+description = "scalene triangle -> may not violate triangle inequality"
[26d9d59d-f8f1-40d3-ad58-ae4d54123d7d]
-description = "sides may be floats"
+description = "scalene triangle -> sides may be floats"
diff --git a/exercises/practice/triangle/triangle_test.py b/exercises/practice/triangle/triangle_test.py
index f95617b4775..2de48d3372e 100644
--- a/exercises/practice/triangle/triangle_test.py
+++ b/exercises/practice/triangle/triangle_test.py
@@ -62,9 +62,15 @@ def test_no_sides_are_equal(self):
def test_all_sides_are_equal(self):
self.assertIs(scalene([4, 4, 4]), False)
- def test_two_sides_are_equal(self):
+ def test_first_and_second_sides_are_equal(self):
self.assertIs(scalene([4, 4, 3]), False)
+ def test_first_and_third_sides_are_equal(self):
+ self.assertIs(scalene([3, 4, 3]), False)
+
+ def test_second_and_third_sides_are_equal(self):
+ self.assertIs(scalene([4, 3, 3]), False)
+
def test_may_not_violate_triangle_inequality(self):
self.assertIs(scalene([7, 3, 2]), False)
From 82fc7d11196bc26cb3d9f03b14983097332b6b9b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sun, 22 May 2022 10:05:33 -0500
Subject: [PATCH 007/932] Update config.json
---
docs/config.json | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/docs/config.json b/docs/config.json
index 22e6ee298b0..03048741926 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -14,6 +14,20 @@
"title": "How to learn Python",
"blurb": "An overview of how to get started from scratch with Python."
},
+ {
+ "uuid": "7a2e1a4f-1fa8-4327-b700-5af101fcdc89",
+ "slug": "test-driven-development",
+ "path": "docs/TDD.md",
+ "title": "How to use Exercism tests",
+ "blurb": "An overview of how to use Excercism tests."
+ },
+ {
+ "uuid": "3829fdff-47ac-4283-ae47-a5db1dbce956",
+ "slug": "traceback-reading",
+ "path": "docs/TRACEBACKS.md",
+ "title": "How to read tracebacks",
+ "blurb": "An overview of how to read Python tracebacks for debugging."
+ },
{
"uuid": "8666f259-de7d-4928-ae6f-15ff6fe6bb74",
"slug": "tests",
From d90ca9bed606885846a25ea801ab8b871c0f82d4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sun, 22 May 2022 10:06:54 -0500
Subject: [PATCH 008/932] Create TRACEBACKS.md
---
docs/TRACEBACKS.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 docs/TRACEBACKS.md
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
new file mode 100644
index 00000000000..464090415c4
--- /dev/null
+++ b/docs/TRACEBACKS.md
@@ -0,0 +1 @@
+# TODO
From f2e304f9eb3bf5160d9b3c4f146efeef3a1ad24c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sun, 22 May 2022 10:07:47 -0500
Subject: [PATCH 009/932] Create TDD.md
---
docs/TDD.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 docs/TDD.md
diff --git a/docs/TDD.md b/docs/TDD.md
new file mode 100644
index 00000000000..464090415c4
--- /dev/null
+++ b/docs/TDD.md
@@ -0,0 +1 @@
+# TODO
From 008185ba18cf3bbdff6fc91563bf2599fb3c18a0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 28 May 2022 15:25:42 -0500
Subject: [PATCH 010/932] Update TRACEBACKS.md
Saving work. More to come...
---
docs/TRACEBACKS.md | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 464090415c4..063a39c2a1f 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -1 +1,23 @@
-# TODO
+# Reading and Troubleshooting Stack Traces in Python
+
+## Frame Object
+
+A frame object holds the local variables and arguments passed to a function.
+A frame object is created when a function is called and is destroyed when that function returns.
+When function `B` is called within function `A`, function `B`'s values are put into a frame object, which is then placed on top of function `A`'s frame object on the call stack.
+
+## Call Stack
+
+The call stack is a collection of frame objects for the currently active functions.
+If function `A` has called function `B` and function `B` has called function `C`, then the frame objects for all three functions will be on the call stack.
+Once function `C` returns, then its frame object will be popped off the stack and only the frame objects for functions `A` and `B` will remain on the call stack.
+
+## Traceback
+
+A Traceback is a report of all the frame objects on the stack for a specific time.
+When a Python program encounters an unhandled exception, it will print the exception message and a Traceback.
+The Traceback will show where the exception was raised and what functions were called leading up to it.
+
+## How to Read a Traceback
+
+TODO: examples of some common errors, their tracebacks, and how to read them...
From 7522767f5a028476e2f3bfbf77471b14e46f951d Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 31 May 2022 06:21:44 -0500
Subject: [PATCH 011/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 063a39c2a1f..5d5065339db 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -20,4 +20,37 @@ The Traceback will show where the exception was raised and what functions were c
## How to Read a Traceback
-TODO: examples of some common errors, their tracebacks, and how to read them...
+`ValueError` is a common exception.
+
+Following is an example of `ValueError` resulting from trying to assign two variables on the left from only one value on the right:
+
+```python
+>>> first, second = [1]
+Traceback (most recent call last):
+File , line 1, in
+ first, second = [1]
+ValueError: not enough values to unpack (expected 2, got 1)
+
+```
+
+It says that the most recent call is last, so the place to start is with the exception at the bottom of the trace.
+From there it can be traced back to the start.
+If we place the offending line in a function and then call the function we see a longer trace:
+
+```python
+>>> def my_func():
+... first, second = [1]
+...
+>>> my_func()
+Traceback (most recent call last):
+ File , line 5, in
+ my_func()
+ File , line 2, in my_func
+ first, second = [1]
+ValueError: not enough values to unpack (expected 2, got 1)
+```
+
+Working backwards from the bottom, we see that the call where the exception happened is on line 2 in `my_func`.
+We got there by calling `my_func` on line 5.
+
+TODO: print, assert, logging and debugging tools
From c91796eb4c8f73633d36b6299664614835e4292c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 31 May 2022 07:04:19 -0500
Subject: [PATCH 012/932] Update TDD.md
---
docs/TDD.md | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 76 insertions(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 464090415c4..cdd2ad07750 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -1 +1,76 @@
-# TODO
+# Test-Driven Development
+
+## Troubleshooting a Failed Test on Exercism in the Web Editor
+
+When one or more tests fail for a Python solution, the corresponding task(s) will not have a green background.
+The first failing task area will be expanded and its header will look something like
+
+```
+Task 1 Extract coordinates -
+```
+
+Clicking on the minus sign will collapse the task, so we can look at other tasks, but for now we will stick with this task.
+
+Underneath will be an expanded Test area that will look something like
+
+```
+ Test 1 ⌄
+FAILED TisburyTreasure > get coordinate
+```
+
+where `Tisbury Treasure` indicates the exercise, and `get_coordinate` indicates the failing function or method.
+
+`Test 1` is usually going to be a kind of template with a code section for setting up the tests.
+It does not have information on the specific test(s) that failed.
+It will say toward the bottom that
+
+```
+One or more variations of this test failed. Details can be found under each [variant#].
+```
+
+Clicking the `⌄` will collapse the test.
+
+
+Beneath will be a collapsed test that looks something like:
+
+
+```
+ Test 2 >
+FAILED TisburyTreasure > get coordinate [variation #1] (item=
+ ("Scrimshaw Whale's Tooth", '2A'), result='2A')
+
+```
+
+How it looks will vary depending on the width set for the right-hand pane.
+Clicking the `>` will expand the test.
+Input data and expected result data will likely be shown in a code section.
+The data may be for all of the tests for this task.
+At the bottom, in the `Test Failure` section, is the specific reason why this rest failed.
+It may look something like this:
+
+```
+AssertionError: ['2A'] != '2A'
+```
+
+In this particular case, it indicates that the returned value of `['2A']` did not equal the expected value of `'2A'`.
+
+If we look at the code for `get_coordinate` we see it is implemented like so
+
+```python
+return [record[1]]
+```
+
+If we remove the list brackets (e.g. `return record[1]`) and run the tests again, the tests for Task 1 will pass.
+
+If one or more tasks remain failing, then the above process is repeated with each one until all tests pass.
+
+Sometimes, the expected datat and returned data are too large to all be placed in the `Test Failure` section.
+It may look something like this:
+
+```
+AssertionError: '("Sc[67 chars]\')\n\n(\'Brass Spyglass\', \'Abandoned Lighth[952 chars]')\n' != '("Sc[67 chars]\')\n(\'Brass Spyglass\', \'Abandoned Lighthou[928 chars]')\n'
+Diff is 970 characters long. Set self.maxDiff to None to see it.
+```
+
+There may still be enough data to see what the problem is.
+In the case above, there are two line feeds returned (e.g. `\n\n(\'Brass Spyglass`) when only one is expected (e.g. `\n(\'Brass Spyglass`).
From 826a083dd78819091b51c46f569c67a109944c77 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 1 Jun 2022 10:52:55 -0500
Subject: [PATCH 013/932] Update TDD.md
---
docs/TDD.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/docs/TDD.md b/docs/TDD.md
index cdd2ad07750..476e581eff4 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -1,5 +1,62 @@
# Test-Driven Development
+Test-driven development ([TDD][TDD]) refers to a style of programming where tests are written as artifacts to drive the implementation of the program design in code.
+
+One or more tests (particularly [unit tests][unit tests]) are written before coding.
+The tests are intended to cover one aspect of the program's behavior, which may be focused on a single function or method.
+The writing of the tests is a way to transfrom the program requirements and general architecture into an implementation-specific design.
+The tests are run, and they should fail, because the code hasn't been implemented.
+The code is implemented and the tests are run again.
+If the tests pass, then either implementing the behavior is done or perhaps more necessary tests remain to be created.
+If the tests don't pass, then the code is debugged and the tests are run again.
+The cycle of testing and coding is repeated until all of the necessary tests pass, at which point the implementation for that aspect of the program's behavior is done... for now.
+
+## Refactoring
+
+[Refactoring][refactoring] is the rewriting of code to improve its design.
+It is not simply rewriting code to fix bugs.
+It is sometimes called "refactoring" to modify code to get it to pass the tests.
+Although modifying the code _may_ include improving the design as a way to pass the tests, simply debugging is not necessarily improving the _design_ of the code, and so is not necessarily _refactoring_.
+
+Following is an example of debugging without refactoring:
+
+```python
+
+# A function intended to return x added to y.
+# x and y are bad parameter names, but we ignore that for now.
+def add(x, y):
+ # used multiply operator by mistake. It fails the tests.
+ return x * y
+
+# Function corrected. It passes the tests. It has been debugged, but not refactored.
+def add(x, y):
+ return x + y
+
+```
+
+
+Following is an example of refactoring, and then debugging:
+
+```python
+
+# Function name and parameter names are modified to something more meaningful. This is refactoring.
+def lot_inventory(old_cars, new_cars):
+ # Introduced multiply operator by mistake. It fails the tests. This is why we test.
+ return old_cars * new_cars
+
+# Function corrected. It passes the tests. This is debugging.
+def lot_inventory(old_cars, new_cars):
+ return old_cars + new_cars
+
+```
+
+## TDD and Python on Exercism
+
+Exercism's Python track utilizes TDD methodology in its exercises.
+Unit tests are already written.
+The tests may be viewed by the student to gather a more detailed understanding of what a passing solution should provide.
+A solution stub may be provided to the student.
+
## Troubleshooting a Failed Test on Exercism in the Web Editor
When one or more tests fail for a Python solution, the corresponding task(s) will not have a green background.
@@ -74,3 +131,19 @@ Diff is 970 characters long. Set self.maxDiff to None to see it.
There may still be enough data to see what the problem is.
In the case above, there are two line feeds returned (e.g. `\n\n(\'Brass Spyglass`) when only one is expected (e.g. `\n(\'Brass Spyglass`).
+
+## After all the tests pass
+
+Congratulations!
+All the tests have passed.
+What next?
+The solution could be published right away.
+Or, now that the code works, if you would like to refactor it for any reason, you can modify the code and submit another iteration.
+If you think the code could be better, but you don't know how, you can request mentoring for the solution.
+If a mentor is available with ideas for other approaches to a solution, they may contact you to share those ideas.
+You could allow comments when publishing your solution, and other students may take the opportunity to make comments or ask questions.
+
+
+[refactoring]: https://www.agilealliance.org/glossary/refactoring
+[TDD]: https://www.agilealliance.org/glossary/tdd
+[unit tests]: https://www.agilealliance.org/glossary/unit-test
From ede514118f460254e0d4cc9b60a369402c13e9c7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 1 Jun 2022 10:57:08 -0500
Subject: [PATCH 014/932] Update TDD.md
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 476e581eff4..28ed0efc0c3 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -4,7 +4,7 @@ Test-driven development ([TDD][TDD]) refers to a style of programming where test
One or more tests (particularly [unit tests][unit tests]) are written before coding.
The tests are intended to cover one aspect of the program's behavior, which may be focused on a single function or method.
-The writing of the tests is a way to transfrom the program requirements and general architecture into an implementation-specific design.
+The writing of the tests is a way to transform the program requirements and general architecture into an implementation-specific design.
The tests are run, and they should fail, because the code hasn't been implemented.
The code is implemented and the tests are run again.
If the tests pass, then either implementing the behavior is done or perhaps more necessary tests remain to be created.
From 294254bfac516fb20fd19e21e5ac938ca0da937c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 1 Jun 2022 11:01:49 -0500
Subject: [PATCH 015/932] Update TDD.md
---
docs/TDD.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 28ed0efc0c3..10f893687ee 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -102,7 +102,7 @@ How it looks will vary depending on the width set for the right-hand pane.
Clicking the `>` will expand the test.
Input data and expected result data will likely be shown in a code section.
The data may be for all of the tests for this task.
-At the bottom, in the `Test Failure` section, is the specific reason why this rest failed.
+At the bottom, in the `Test Failure` section, is the specific reason why this test failed.
It may look something like this:
```
@@ -121,7 +121,7 @@ If we remove the list brackets (e.g. `return record[1]`) and run the tests again
If one or more tasks remain failing, then the above process is repeated with each one until all tests pass.
-Sometimes, the expected datat and returned data are too large to all be placed in the `Test Failure` section.
+Sometimes, the expected data and returned data are too large to all be placed in the `Test Failure` section.
It may look something like this:
```
From 01d7332a9254455407ddbce5bcd6378ac6ecf9d4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 2 Jun 2022 08:09:03 -0500
Subject: [PATCH 016/932] Update TDD.md
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 10f893687ee..10200a5c140 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -1,6 +1,6 @@
# Test-Driven Development
-Test-driven development ([TDD][TDD]) refers to a style of programming where tests are written as artifacts to drive the implementation of the program design in code.
+Test-driven development ([TDD][TDD]) refers to a style of programming where tests are written as artifacts to guide the implementation of the program design in code.
One or more tests (particularly [unit tests][unit tests]) are written before coding.
The tests are intended to cover one aspect of the program's behavior, which may be focused on a single function or method.
From 97eaf7996f8398b5f8853141e497ebfd48ef4e56 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 4 Jun 2022 08:07:58 -0500
Subject: [PATCH 017/932] Update TRACEBACKS.md
Saving work. More to come...
---
docs/TRACEBACKS.md | 39 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 5d5065339db..412815ae16f 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -53,4 +53,41 @@ ValueError: not enough values to unpack (expected 2, got 1)
Working backwards from the bottom, we see that the call where the exception happened is on line 2 in `my_func`.
We got there by calling `my_func` on line 5.
-TODO: print, assert, logging and debugging tools
+## Using the print function
+
+Sometimes an error is not being raised, but a value is not what is expected.
+This can be especially perplexing if the value is the result of a chain of calculations.
+In such a situation it can be helpful to look at the value at each step to see which step is the one that isn't behaving as expected.
+The [print][print] function can be used for printing the value to the console.
+Following is an example of a function that doesn't return the value expected
+
+```python
+# the intent is to only pass an int to this function and get an int back
+def halve_and_quadruple(num):
+ return (num / 2) * 4
+```
+
+When the function is passed `5`, the expected value is `8`, but it it returns `10.0`.
+To troubleshoot, the calculating is broken up so that the value can be inspected at every step.
+
+
+```python
+# the intent is to only pass an int to this function and get an int back
+def halve_and_quadruple(num):
+ # verify the number in is what is expected
+ # prints 5
+ print(num)
+ # we want the int divided by an int to be an int
+ # but this prints 2.5! We've found our mistake.
+ print(num / 2)
+ # this makes sense, since 2.5 x 4 = 10.0
+ print((num / 2) * 4)
+ return (num / 2) * 4
+```
+
+What the `print` calls revealed is that we used `/` when we should have used `//`, the [floor divison operator][floor divison operator].
+
+TODO: assert, logging and debugging tools
+
+[floor divison operator]: https://www.codingem.com/python-floor-division
+[print]: https://www.w3schools.com/python/ref_func_print.asp
From 0207fbdf88632e657076d1e2b53b121ec358f912 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 4 Jun 2022 08:08:42 -0500
Subject: [PATCH 018/932] Update TRACEBACKS.md
Saving work. More to come...
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 412815ae16f..d0f7099c0c8 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -53,7 +53,7 @@ ValueError: not enough values to unpack (expected 2, got 1)
Working backwards from the bottom, we see that the call where the exception happened is on line 2 in `my_func`.
We got there by calling `my_func` on line 5.
-## Using the print function
+## Using the `print` function
Sometimes an error is not being raised, but a value is not what is expected.
This can be especially perplexing if the value is the result of a chain of calculations.
From 0beb4bd825cae85b9eb23a35b84f979d8bc00df2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 4 Jun 2022 13:42:58 -0500
Subject: [PATCH 019/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 47 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 46 insertions(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index d0f7099c0c8..d6ae760d5b4 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -48,6 +48,7 @@ Traceback (most recent call last):
File , line 2, in my_func
first, second = [1]
ValueError: not enough values to unpack (expected 2, got 1)
+
```
Working backwards from the bottom, we see that the call where the exception happened is on line 2 in `my_func`.
@@ -83,11 +84,55 @@ def halve_and_quadruple(num):
# this makes sense, since 2.5 x 4 = 10.0
print((num / 2) * 4)
return (num / 2) * 4
+
```
What the `print` calls revealed is that we used `/` when we should have used `//`, the [floor divison operator][floor divison operator].
-TODO: assert, logging and debugging tools
+## Logging
+
+[Logging][logging] can be used similarly to `print`, but it is more powerful.
+Logging can print to the console, but it can also be configured to print to a file.
+
+Following is a an example of logging printed to the console:
+
+```python
+
+>>> import logging
+...
+... # configures minimum logging level as INFO
+... logging.basicConfig(level=logging.INFO)
+
+... def halve_and_quadruple(num):
+... # prints INFO:root: num == 5
+... logging.info(f" num == {num}")
+... return (num // 2) * 4
+...
+>>> print(halve_and_quadruple(5))
+
+```
+
+The level is configured as `INFO` because the default level is `WARNING`.
+For a persistent log, the logger can be configured to use a file, like so:
+
+```python
+
+>>> import logging
+...
+... # configures the output file name to example.log, and the minimum logging level as INFO
+... logging.basicConfig(filename='example.log', level=logging.INFO)
+...
+... def halve_and_quadruple(num):
+... # prints INFO:root: num == 5 to the example.log file
+... logging.info(f" num == {num}")
+... return (num // 2) * 4
+...
+>>> print(halve_and_quadruple(5))
+
+```
+
+TODO: assert and debugging tools
[floor divison operator]: https://www.codingem.com/python-floor-division
+[logging]: https://docs.python.org/3/howto/logging.html
[print]: https://www.w3schools.com/python/ref_func_print.asp
From f89f9699750d853c195871f4012c0f7e869702ba Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 4 Jun 2022 14:29:05 -0500
Subject: [PATCH 020/932] Update TDD.md
---
docs/TDD.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/docs/TDD.md b/docs/TDD.md
index 10200a5c140..6af7d82e162 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -143,7 +143,76 @@ If you think the code could be better, but you don't know how, you can request m
If a mentor is available with ideas for other approaches to a solution, they may contact you to share those ideas.
You could allow comments when publishing your solution, and other students may take the opportunity to make comments or ask questions.
+## Optimizing for Performance
+
+Although "Premature optimization is the root of all evil" (a saying attributed to both Tony Hoare and Donald Knuth), there does come a time when the solution works and it is desired to make it as performant as possible.
+One of those times may be when the solution passes some tests but times out for others.
+It can be helpful to know exactly how much time a piece of code is taking.
+The [timeit][timeit] module can be used to time the execution of code down to very small durations.
+The `timeit` function can take up to five arguments.
+The `stmt` parameter defines the actual code to be run and timed.
+The `stmt` code will be run for a specifed `number` of iterations.
+The `setup` parameter defines the code that is run only once to prepare for running the `stmt` code.
+The `timer` parameter allows for passing in a different `Timer` from the default.
+The default argument for the `timer` parameter is [perf_counter][perf_counter], which should suffice.
+The `number` parameter defines the number of times the `stmt` code will be run.
+The default argument for the `number` parameter is `1_000_000`.
+The `globals` parameter specifies a namespace in which to execute the code.
+The default argument for the `globals` parameter is `None`.
+
+Following is an example of using `timeit` to see how long it takes to determine if a sentence contains all English vowels:
+```python
+
+import timeit
+
+# run one million times
+loops = 1_000_000
+
+# first positional argument is for stmt
+# second positional argument is for setup
+# third (named) argument is for number
+print(timeit.timeit("""has_all_vowels('Another piggy digs up the truffles.')""",
+ """
+
+VOWELS = "AEIOU"
+
+def has_all_vowels(sentence):
+ return all(letter in sentence.casefold() for letter in VOWELS)
+""", number=loops) / loops)
+
+```
+
+Running the code a million times took an average `4.965089999896008e-07` seconds per call (about `497` nanoseconds per call.)
+
+The following example is to see if taking the `casefold` call out of the list comprehension saves any time:
+
+```python
+
+import timeit
+
+loops = 1_000_000
+
+print(timeit.timeit("""has_all_vowels('Another piggy digs up the truffles.')""",
+ """
+
+VOWELS = "AEIOU"
+
+def has_all_vowels(sentence):
+ sentence = sentence.casefold()
+ return all(letter in sentence for letter in VOWELS)
+""", number=loops) / loops)
+
+```
+
+Running the code a million times took an average `4.923898000270128e-07` seconds per call (about `492` nanoseconds per call.)
+So, taking the `casefold` out of the list comprehension saved about `5` nanoseconds per call, or about `5` milliseconds total for a million calls.
+
+[cProfile][cprofile] can also be used to profile code; however, it is not as granular, since it only goes down to millisecond durations.
+
+[cprofile]: https://www.machinelearningplus.com/python/cprofile-how-to-profile-your-python-code/
+[perf_counter]: https://docs.python.org/3/library/time.html#time.perf_counter
[refactoring]: https://www.agilealliance.org/glossary/refactoring
[TDD]: https://www.agilealliance.org/glossary/tdd
+[timeit]: https://docs.python.org/3/library/timeit.html
[unit tests]: https://www.agilealliance.org/glossary/unit-test
From 89698335bb4bce5759f0e483ceda8595452cbaa6 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 4 Jun 2022 14:33:59 -0500
Subject: [PATCH 021/932] Update TDD.md
---
docs/TDD.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/TDD.md b/docs/TDD.md
index 6af7d82e162..153b8fc85e4 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -153,6 +153,8 @@ The `timeit` function can take up to five arguments.
The `stmt` parameter defines the actual code to be run and timed.
The `stmt` code will be run for a specifed `number` of iterations.
The `setup` parameter defines the code that is run only once to prepare for running the `stmt` code.
+The time for the `setup` code to run is included in the overall time.
+The more iterations the `stmt` code is run, the less the `setup` time should count per iteration.
The `timer` parameter allows for passing in a different `Timer` from the default.
The default argument for the `timer` parameter is [perf_counter][perf_counter], which should suffice.
The `number` parameter defines the number of times the `stmt` code will be run.
From 8d6f1b5690bfb172680d04e224139edfc9251eef Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 9 Jun 2022 06:57:13 -0500
Subject: [PATCH 022/932] Update TRACEBACKS.md
Saving work. More to come...
---
docs/TRACEBACKS.md | 64 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index d6ae760d5b4..fe5ecc684e2 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -131,8 +131,70 @@ For a persistent log, the logger can be configured to use a file, like so:
```
-TODO: assert and debugging tools
+## assert
+[`assert`][assert] is a statement which should always evaluate to `True` unless there is a bug in the program.
+When an `assert` evaluates to `False` it will raise an [`AssertionError`][AssertionError].
+The Traceback for the `AssertionError` can include an optional message that is part of the `assert` definition.
+Although a message is optional, it is good practice to always include one in the `assert` definition.
+
+Following is an example of using `assert`:
+
+```python
+>>> def int_division(dividend, divisor):
+... assert divisor != 0, "divisor must not be 0"
+... return dividend // divisor
+...
+>>> print(int_division(2, 1))
+2
+>>> print(int_division(2, 0))
+Traceback (most recent call last):
+ File , line 7, in
+ print(int_division(2, 0))
+ ^^^^^^^^^^^^^^^^^^
+ File , line 2, in int_division
+ assert divisor != 0, "divisor must not be 0"
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+AssertionError: divisor must not be 0
+
+```
+
+If we start reading the Traceback at the bottom (as we should) we quickly see the problem is that `0` should not be passsed as the `divisor`.
+
+`assert` can also be used to test that a value is of the expected type:
+
+```python
+>>> import numbers
+...
+...
+... def int_division(dividend, divisor):
+... assert divisor != 0, "divisor must not be 0"
+... assert isinstance(divisor, numbers.Number), "divisor must be a number"
+... return dividend // divisor
+...
+>>> print(int_division(2, 1))
+2
+>>> print(int_division(2, '0'))
+Traceback (most recent call last):
+ File , line 11, in
+ print(int_division(2, '0'))
+ ^^^^^^^^^^^^^^^^^^^^
+ File , line 6, in int_division
+ assert isinstance(divisor, numbers.Number), "divisor must be a number"
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+AssertionError: divisor must be a number
+```
+
+Once a bug is identified, it should be considered to replace the `assert` with regular error handling.
+This is because all `assert` statements can disabled through running Python with the `-O` or `-OO` options, or from setting the `PYTHONOPTIMIZE` environment variable to `1` or `2`.
+Setting `PYTHONOPTIMIZE` to `1` is equivalent to running Python with the `-O` option, which disables assertions.
+Setting `PYTHONOPTIMIZE` to `2` is equivalent to running Python with the `-OO` option, which both disables assertions and removes docstrings from the bytcode.
+Reducing bytecode is one way to make the code run faster.
+
+TODO: debugging tools
+
+[assert]: https://realpython.com/python-assert-statement/
+[AssertionError]: https://www.geeksforgeeks.org/python-assertion-error/
[floor divison operator]: https://www.codingem.com/python-floor-division
[logging]: https://docs.python.org/3/howto/logging.html
[print]: https://www.w3schools.com/python/ref_func_print.asp
From 45822c21380a4b36054dbd900c1fb89e7de90577 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 14 Jun 2022 07:28:42 -0500
Subject: [PATCH 023/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index fe5ecc684e2..3e89bcb330a 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -191,8 +191,6 @@ Setting `PYTHONOPTIMIZE` to `1` is equivalent to running Python with the `-O` op
Setting `PYTHONOPTIMIZE` to `2` is equivalent to running Python with the `-OO` option, which both disables assertions and removes docstrings from the bytcode.
Reducing bytecode is one way to make the code run faster.
-TODO: debugging tools
-
[assert]: https://realpython.com/python-assert-statement/
[AssertionError]: https://www.geeksforgeeks.org/python-assertion-error/
[floor divison operator]: https://www.codingem.com/python-floor-division
From 3805ffa15d003af8d454c83c49dd1715729bf770 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 14 Jun 2022 10:59:38 -0700
Subject: [PATCH 024/932] Title Changes to Avoid Confusion
---
docs/config.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/config.json b/docs/config.json
index 03048741926..ccaea6ac247 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -18,8 +18,8 @@
"uuid": "7a2e1a4f-1fa8-4327-b700-5af101fcdc89",
"slug": "test-driven-development",
"path": "docs/TDD.md",
- "title": "How to use Exercism tests",
- "blurb": "An overview of how to use Excercism tests."
+ "title": "Test Driven Development",
+ "blurb": "An overview of Test Driven Development."
},
{
"uuid": "3829fdff-47ac-4283-ae47-a5db1dbce956",
From 6f934cf44dc0ad3b6395c9398d6907b3a63511de Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:39:47 -0500
Subject: [PATCH 025/932] Update docs/TDD.md
Co-authored-by: Victor Goff
---
docs/TDD.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 153b8fc85e4..0ea8043a5f7 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -31,7 +31,6 @@ def add(x, y):
# Function corrected. It passes the tests. It has been debugged, but not refactored.
def add(x, y):
return x + y
-
```
From e97525d783c26d00590d84dc7611eeb1e4423c4b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:40:24 -0500
Subject: [PATCH 026/932] Update docs/TDD.md
Co-authored-by: Victor Goff
---
docs/TDD.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 0ea8043a5f7..d9e1d7f4642 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -77,8 +77,8 @@ FAILED TisburyTreasure > get coordinate
where `Tisbury Treasure` indicates the exercise, and `get_coordinate` indicates the failing function or method.
`Test 1` is usually going to be a kind of template with a code section for setting up the tests.
-It does not have information on the specific test(s) that failed.
-It will say toward the bottom that
+It does not have information on the specific test(s) that failed.
+It will say toward the bottom that
```
One or more variations of this test failed. Details can be found under each [variant#].
From 8796e9cc64a522a0d83c060750e1e0f14cce6504 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:41:28 -0500
Subject: [PATCH 027/932] Update docs/TDD.md
Co-authored-by: Victor Goff
---
docs/TDD.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index d9e1d7f4642..b38a87d5973 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -86,10 +86,8 @@ One or more variations of this test failed. Details can be found under each [var
Clicking the `⌄` will collapse the test.
-
Beneath will be a collapsed test that looks something like:
-
```
Test 2 >
FAILED TisburyTreasure > get coordinate [variation #1] (item=
From b1a530ca7aa46e7baee9c0d017b86dd1137c94c2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:48:33 -0500
Subject: [PATCH 028/932] Update docs/TDD.md
Co-authored-by: Victor Goff
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index b38a87d5973..5ffa453b395 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -159,7 +159,7 @@ The default argument for the `number` parameter is `1_000_000`.
The `globals` parameter specifies a namespace in which to execute the code.
The default argument for the `globals` parameter is `None`.
-Following is an example of using `timeit` to see how long it takes to determine if a sentence contains all English vowels:
+The following is an example of using `timeit` to see how long it takes to determine if a sentence contains all English vowels:
```python
From 6858b10de69cd5a6d62cfd5a5c17a0a380ba84a7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:49:00 -0500
Subject: [PATCH 029/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 3e89bcb330a..bf08a43c47f 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -40,7 +40,7 @@ If we place the offending line in a function and then call the function we see a
```python
>>> def my_func():
... first, second = [1]
-...
+...
>>> my_func()
Traceback (most recent call last):
File , line 5, in
From f70a796665a306ffc374705796826dbc445f1fc1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:49:11 -0500
Subject: [PATCH 030/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index bf08a43c47f..493f4d8b6e6 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -60,7 +60,7 @@ Sometimes an error is not being raised, but a value is not what is expected.
This can be especially perplexing if the value is the result of a chain of calculations.
In such a situation it can be helpful to look at the value at each step to see which step is the one that isn't behaving as expected.
The [print][print] function can be used for printing the value to the console.
-Following is an example of a function that doesn't return the value expected
+The following is an example of a function that doesn't return the value expected:
```python
# the intent is to only pass an int to this function and get an int back
From 099b74f17be351cb5a7860226e93e7bf9191b18f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:49:30 -0500
Subject: [PATCH 031/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 493f4d8b6e6..e667d137b57 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -99,7 +99,7 @@ Following is a an example of logging printed to the console:
```python
>>> import logging
-...
+...
... # configures minimum logging level as INFO
... logging.basicConfig(level=logging.INFO)
@@ -107,7 +107,7 @@ Following is a an example of logging printed to the console:
... # prints INFO:root: num == 5
... logging.info(f" num == {num}")
... return (num // 2) * 4
-...
+...
>>> print(halve_and_quadruple(5))
```
From cf23171f7a43ef72c12cfcd7d355c8b7d4c6d6f1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 19:49:45 -0500
Subject: [PATCH 032/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index e667d137b57..7fe758477fc 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -118,15 +118,15 @@ For a persistent log, the logger can be configured to use a file, like so:
```python
>>> import logging
-...
+...
... # configures the output file name to example.log, and the minimum logging level as INFO
... logging.basicConfig(filename='example.log', level=logging.INFO)
-...
+...
... def halve_and_quadruple(num):
... # prints INFO:root: num == 5 to the example.log file
... logging.info(f" num == {num}")
... return (num // 2) * 4
-...
+...
>>> print(halve_and_quadruple(5))
```
From fdbe31c413ffc70a19eec928b2150ffa07a4a7c2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:11:25 -0500
Subject: [PATCH 033/932] Update TRACEBACKS.md
Attempted to give better and more accurate reasons for logging.
---
docs/TRACEBACKS.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 7fe758477fc..5b2565b69e3 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -92,7 +92,9 @@ What the `print` calls revealed is that we used `/` when we should have used `//
## Logging
[Logging][logging] can be used similarly to `print`, but it is more powerful.
-Logging can print to the console, but it can also be configured to print to a file.
+What is logged can be configured by the logging severity (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'.)
+A call to the `logging.error` function can pass `True` to the `exc_info` parameter, which will additionally log the stack trace.
+By configuring multiple handlers, logging can print to more than one place with the same logging function.
Following is a an example of logging printed to the console:
From 3691f1b65291a6761e077cbacf4aab5272fde065 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:20:30 -0500
Subject: [PATCH 034/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 5ffa453b395..c192865616f 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -1,6 +1,6 @@
# Test-Driven Development
-Test-driven development ([TDD][TDD]) refers to a style of programming where tests are written as artifacts to guide the implementation of the program design in code.
+Test-driven development ([TDD][TDD]) refers to a style of programming where tests are written to guide the implementation of the program design in code.
One or more tests (particularly [unit tests][unit tests]) are written before coding.
The tests are intended to cover one aspect of the program's behavior, which may be focused on a single function or method.
From 0faaaffd971547e8593c6330c53ace3e73e74a13 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:20:46 -0500
Subject: [PATCH 035/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index c192865616f..d56a86ed2cc 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -21,7 +21,6 @@ Although modifying the code _may_ include improving the design as a way to pass
Following is an example of debugging without refactoring:
```python
-
# A function intended to return x added to y.
# x and y are bad parameter names, but we ignore that for now.
def add(x, y):
From 20209d18c70b233ee4078d5e127cb0ed87376f5f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:22:28 -0500
Subject: [PATCH 036/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index d56a86ed2cc..2a706db9dae 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -52,7 +52,7 @@ def lot_inventory(old_cars, new_cars):
Exercism's Python track utilizes TDD methodology in its exercises.
Unit tests are already written.
-The tests may be viewed by the student to gather a more detailed understanding of what a passing solution should provide.
+The tests may be viewed by the student to gather a more detailed understanding of what is required for a solution to pass.
A solution stub may be provided to the student.
## Troubleshooting a Failed Test on Exercism in the Web Editor
From a31d51bb107d17a02b17f0a8bc766b26e653e327 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:23:04 -0500
Subject: [PATCH 037/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 2a706db9dae..11c499f1294 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -110,7 +110,8 @@ In this particular case, it indicates that the returned value of `['2A']` did no
If we look at the code for `get_coordinate` we see it is implemented like so
```python
-return [record[1]]
+def get_coordinate(record):
+ return [record[1]]
```
If we remove the list brackets (e.g. `return record[1]`) and run the tests again, the tests for Task 1 will pass.
From 82ad226a9c74fa48b2f7ac300aaa79300798be7c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:24:19 -0500
Subject: [PATCH 038/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 11c499f1294..b6907da3a9a 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -129,7 +129,7 @@ Diff is 970 characters long. Set self.maxDiff to None to see it.
There may still be enough data to see what the problem is.
In the case above, there are two line feeds returned (e.g. `\n\n(\'Brass Spyglass`) when only one is expected (e.g. `\n(\'Brass Spyglass`).
-## After all the tests pass
+## After All the Tests Pass
Congratulations!
All the tests have passed.
From 415a92bafd00581eef05cace4caf78659151f87c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:24:35 -0500
Subject: [PATCH 039/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index b6907da3a9a..d188c78afc3 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -202,8 +202,6 @@ def has_all_vowels(sentence):
return all(letter in sentence for letter in VOWELS)
""", number=loops) / loops)
-```
-
Running the code a million times took an average `4.923898000270128e-07` seconds per call (about `492` nanoseconds per call.)
So, taking the `casefold` out of the list comprehension saved about `5` nanoseconds per call, or about `5` milliseconds total for a million calls.
From da9bfb0b90b67e66928dc5fea2f875b049ad4f82 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:25:16 -0500
Subject: [PATCH 040/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 5b2565b69e3..cf063f4d18c 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -71,7 +71,6 @@ def halve_and_quadruple(num):
When the function is passed `5`, the expected value is `8`, but it it returns `10.0`.
To troubleshoot, the calculating is broken up so that the value can be inspected at every step.
-
```python
# the intent is to only pass an int to this function and get an int back
def halve_and_quadruple(num):
From 6d6c7ad38e05970667e73d91203cbe5409a277d9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:27:11 -0500
Subject: [PATCH 041/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index d188c78afc3..e0d24062b2e 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -182,7 +182,7 @@ def has_all_vowels(sentence):
```
-Running the code a million times took an average `4.965089999896008e-07` seconds per call (about `497` nanoseconds per call.)
+Running the code a million times took an average `4.965089999896008e-07` seconds per call (about `497` nanoseconds per call).
The following example is to see if taking the `casefold` call out of the list comprehension saves any time:
From 1ad29ee099aeffcebf97e05f3cad4258817c43e7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:27:23 -0500
Subject: [PATCH 042/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index e0d24062b2e..b238ecfd2c8 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -180,8 +180,6 @@ def has_all_vowels(sentence):
return all(letter in sentence.casefold() for letter in VOWELS)
""", number=loops) / loops)
-```
-
Running the code a million times took an average `4.965089999896008e-07` seconds per call (about `497` nanoseconds per call).
The following example is to see if taking the `casefold` call out of the list comprehension saves any time:
From 6058cec5734f3825ba4a9b76504bec1c15645818 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:27:51 -0500
Subject: [PATCH 043/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index b238ecfd2c8..b1b658ac0db 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -137,8 +137,8 @@ What next?
The solution could be published right away.
Or, now that the code works, if you would like to refactor it for any reason, you can modify the code and submit another iteration.
If you think the code could be better, but you don't know how, you can request mentoring for the solution.
-If a mentor is available with ideas for other approaches to a solution, they may contact you to share those ideas.
-You could allow comments when publishing your solution, and other students may take the opportunity to make comments or ask questions.
+If a mentor is available, they may contact you with ideas for other approaches to a solution.
+When publishing your solution, you can allow comments and other students may take the opportunity to post comments or ask questions.
## Optimizing for Performance
From 8dd274bd8aea35c2a5a61882b774e7130d50c4cf Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:28:17 -0500
Subject: [PATCH 044/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index b1b658ac0db..d0c5a95108b 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -142,7 +142,7 @@ When publishing your solution, you can allow comments and other students may tak
## Optimizing for Performance
-Although "Premature optimization is the root of all evil" (a saying attributed to both Tony Hoare and Donald Knuth), there does come a time when the solution works and it is desired to make it as performant as possible.
+Although "Premature optimization is the root of all evil" (a saying attributed to both Tony Hoare and Donald Knuth), there does come a time when, even though the solution works, it is desired to improve the solution's performance.
One of those times may be when the solution passes some tests but times out for others.
It can be helpful to know exactly how much time a piece of code is taking.
The [timeit][timeit] module can be used to time the execution of code down to very small durations.
From e0b0ad9a6fc58347715b9881faf940ef51c0fe19 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:28:39 -0500
Subject: [PATCH 045/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index d0c5a95108b..3e26de04328 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -146,7 +146,7 @@ Although "Premature optimization is the root of all evil" (a saying attributed t
One of those times may be when the solution passes some tests but times out for others.
It can be helpful to know exactly how much time a piece of code is taking.
The [timeit][timeit] module can be used to time the execution of code down to very small durations.
-The `timeit` function can take up to five arguments.
+The `timeit` function can take up to five arguments: `timeit.timeit(stmt='pass', setup='pass', timer=, number=1000000, globals=None)`.
The `stmt` parameter defines the actual code to be run and timed.
The `stmt` code will be run for a specifed `number` of iterations.
The `setup` parameter defines the code that is run only once to prepare for running the `stmt` code.
From 098bbe060cac405358178ff8a065758689e2f491 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:29:00 -0500
Subject: [PATCH 046/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 3e26de04328..23c693e26b8 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -148,7 +148,7 @@ It can be helpful to know exactly how much time a piece of code is taking.
The [timeit][timeit] module can be used to time the execution of code down to very small durations.
The `timeit` function can take up to five arguments: `timeit.timeit(stmt='pass', setup='pass', timer=, number=1000000, globals=None)`.
The `stmt` parameter defines the actual code to be run and timed.
-The `stmt` code will be run for a specifed `number` of iterations.
+The `number` parameter determines how many times the `stmt` code will be run.
The `setup` parameter defines the code that is run only once to prepare for running the `stmt` code.
The time for the `setup` code to run is included in the overall time.
The more iterations the `stmt` code is run, the less the `setup` time should count per iteration.
From eb67ae8fd9c2e5b46d8893f13bc7d2327ede74a6 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:29:20 -0500
Subject: [PATCH 047/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 23c693e26b8..6d7c30f702c 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -152,7 +152,7 @@ The `number` parameter determines how many times the `stmt` code will be run.
The `setup` parameter defines the code that is run only once to prepare for running the `stmt` code.
The time for the `setup` code to run is included in the overall time.
The more iterations the `stmt` code is run, the less the `setup` time should count per iteration.
-The `timer` parameter allows for passing in a different `Timer` from the default.
+The `timer` parameter allows for passing in a different `Timer` than the default.
The default argument for the `timer` parameter is [perf_counter][perf_counter], which should suffice.
The `number` parameter defines the number of times the `stmt` code will be run.
The default argument for the `number` parameter is `1_000_000`.
From e57d8dcd81d732bd808b83cda81d87bd9c4c97c6 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:29:38 -0500
Subject: [PATCH 048/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 6d7c30f702c..042076c7350 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -151,7 +151,7 @@ The `stmt` parameter defines the actual code to be run and timed.
The `number` parameter determines how many times the `stmt` code will be run.
The `setup` parameter defines the code that is run only once to prepare for running the `stmt` code.
The time for the `setup` code to run is included in the overall time.
-The more iterations the `stmt` code is run, the less the `setup` time should count per iteration.
+The more iterations the `stmt` code is run, the less the `setup` time will count per iteration.
The `timer` parameter allows for passing in a different `Timer` than the default.
The default argument for the `timer` parameter is [perf_counter][perf_counter], which should suffice.
The `number` parameter defines the number of times the `stmt` code will be run.
From 211af777b8125d7f18f98996b1103c9a8b360de3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:30:15 -0500
Subject: [PATCH 049/932] Update docs/TDD.md
Co-authored-by: Isaac Good
---
docs/TDD.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 042076c7350..a4af65b2169 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -153,7 +153,7 @@ The `setup` parameter defines the code that is run only once to prepare for runn
The time for the `setup` code to run is included in the overall time.
The more iterations the `stmt` code is run, the less the `setup` time will count per iteration.
The `timer` parameter allows for passing in a different `Timer` than the default.
-The default argument for the `timer` parameter is [perf_counter][perf_counter], which should suffice.
+The default argument for the `timer` parameter is [perf_counter][perf_counter], which should suffice for most cases.
The `number` parameter defines the number of times the `stmt` code will be run.
The default argument for the `number` parameter is `1_000_000`.
The `globals` parameter specifies a namespace in which to execute the code.
From 216bdb95c62aa1a3ae147faad7c129dd89cc663f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:34:11 -0500
Subject: [PATCH 050/932] Update TDD.md
Took out duplicate sentence.
---
docs/TDD.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index a4af65b2169..275df21a18c 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -154,7 +154,6 @@ The time for the `setup` code to run is included in the overall time.
The more iterations the `stmt` code is run, the less the `setup` time will count per iteration.
The `timer` parameter allows for passing in a different `Timer` than the default.
The default argument for the `timer` parameter is [perf_counter][perf_counter], which should suffice for most cases.
-The `number` parameter defines the number of times the `stmt` code will be run.
The default argument for the `number` parameter is `1_000_000`.
The `globals` parameter specifies a namespace in which to execute the code.
The default argument for the `globals` parameter is `None`.
From 74f803a8d8492a2030e8193300534c15c2d72e30 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:35:08 -0500
Subject: [PATCH 051/932] Update TRACEBACKS.md
Logging "writes" instead of "prints".
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index cf063f4d18c..861c475c665 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -93,7 +93,7 @@ What the `print` calls revealed is that we used `/` when we should have used `//
[Logging][logging] can be used similarly to `print`, but it is more powerful.
What is logged can be configured by the logging severity (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'.)
A call to the `logging.error` function can pass `True` to the `exc_info` parameter, which will additionally log the stack trace.
-By configuring multiple handlers, logging can print to more than one place with the same logging function.
+By configuring multiple handlers, logging can write to more than one place with the same logging function.
Following is a an example of logging printed to the console:
From 89800c109b099d4a579fe72ca66b183537c17601 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:44:55 -0500
Subject: [PATCH 052/932] Update docs/TRACEBACKS.md
Co-authored-by: Isaac Good
---
docs/TRACEBACKS.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 861c475c665..7d317eec2a8 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -2,8 +2,8 @@
## Frame Object
-A frame object holds the local variables and arguments passed to a function.
-A frame object is created when a function is called and is destroyed when that function returns.
+When a function is called, a frame object is created to hold the local variables and arguments passed to a function.
+When the function is returns, the frame object is destroyed.
When function `B` is called within function `A`, function `B`'s values are put into a frame object, which is then placed on top of function `A`'s frame object on the call stack.
## Call Stack
From 4553756a1204ba0e01d49eac3de9a5e025f7d612 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:45:32 -0500
Subject: [PATCH 053/932] Update docs/TRACEBACKS.md
Co-authored-by: Isaac Good
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 7d317eec2a8..c3e9ccbe9a8 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -14,7 +14,7 @@ Once function `C` returns, then its frame object will be popped off the stack an
## Traceback
-A Traceback is a report of all the frame objects on the stack for a specific time.
+A Traceback is a report of all the frame objects on the stack at a specific time.
When a Python program encounters an unhandled exception, it will print the exception message and a Traceback.
The Traceback will show where the exception was raised and what functions were called leading up to it.
From 798e6c60a41e3f817fd2ad242092905533b6274b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:48:39 -0500
Subject: [PATCH 054/932] Update docs/TRACEBACKS.md
Co-authored-by: Isaac Good
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index c3e9ccbe9a8..d389e6b68e4 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -136,7 +136,7 @@ For a persistent log, the logger can be configured to use a file, like so:
[`assert`][assert] is a statement which should always evaluate to `True` unless there is a bug in the program.
When an `assert` evaluates to `False` it will raise an [`AssertionError`][AssertionError].
-The Traceback for the `AssertionError` can include an optional message that is part of the `assert` definition.
+The Traceback for the `AssertionError` can include an optional message that is part of the `assert` statement.
Although a message is optional, it is good practice to always include one in the `assert` definition.
Following is an example of using `assert`:
From 41ecb317491cafc48d3c800b2c5e304d32331e18 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 21 Jul 2022 20:53:43 -0500
Subject: [PATCH 055/932] Update docs/TRACEBACKS.md
Co-authored-by: Isaac Good
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index d389e6b68e4..a520da958c5 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -186,7 +186,7 @@ Traceback (most recent call last):
AssertionError: divisor must be a number
```
-Once a bug is identified, it should be considered to replace the `assert` with regular error handling.
+Once a bug is identified, consider replacing the `assert` with regular error handling.
This is because all `assert` statements can disabled through running Python with the `-O` or `-OO` options, or from setting the `PYTHONOPTIMIZE` environment variable to `1` or `2`.
Setting `PYTHONOPTIMIZE` to `1` is equivalent to running Python with the `-O` option, which disables assertions.
Setting `PYTHONOPTIMIZE` to `2` is equivalent to running Python with the `-OO` option, which both disables assertions and removes docstrings from the bytcode.
From 1ced3a3ac9bc0f70b14f1d34c23919f92335da00 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:30:01 -0500
Subject: [PATCH 056/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index a520da958c5..c61e4674576 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -187,7 +187,7 @@ AssertionError: divisor must be a number
```
Once a bug is identified, consider replacing the `assert` with regular error handling.
-This is because all `assert` statements can disabled through running Python with the `-O` or `-OO` options, or from setting the `PYTHONOPTIMIZE` environment variable to `1` or `2`.
+This is because all `assert` statements can be disabled through running Python with the `-O` or `-OO` options, or from setting the `PYTHONOPTIMIZE` environment variable to `1` or `2`.
Setting `PYTHONOPTIMIZE` to `1` is equivalent to running Python with the `-O` option, which disables assertions.
Setting `PYTHONOPTIMIZE` to `2` is equivalent to running Python with the `-OO` option, which both disables assertions and removes docstrings from the bytcode.
Reducing bytecode is one way to make the code run faster.
From 594b8e33d89ad1167a3b6791bc96b9517d3506a3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:30:31 -0500
Subject: [PATCH 057/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index c61e4674576..bc763be0bd5 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -186,7 +186,7 @@ Traceback (most recent call last):
AssertionError: divisor must be a number
```
-Once a bug is identified, consider replacing the `assert` with regular error handling.
+Once a bug is identified, consider replacing the `assert` with error handling.
This is because all `assert` statements can be disabled through running Python with the `-O` or `-OO` options, or from setting the `PYTHONOPTIMIZE` environment variable to `1` or `2`.
Setting `PYTHONOPTIMIZE` to `1` is equivalent to running Python with the `-O` option, which disables assertions.
Setting `PYTHONOPTIMIZE` to `2` is equivalent to running Python with the `-OO` option, which both disables assertions and removes docstrings from the bytcode.
From 12c9ae0fd2863dd0464675be07ed65534f6e9ed5 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:31:09 -0500
Subject: [PATCH 058/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index bc763be0bd5..4efb4ce2186 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -172,7 +172,7 @@ If we start reading the Traceback at the bottom (as we should) we quickly see th
... assert divisor != 0, "divisor must not be 0"
... assert isinstance(divisor, numbers.Number), "divisor must be a number"
... return dividend // divisor
-...
+...
>>> print(int_division(2, 1))
2
>>> print(int_division(2, '0'))
From 33bc36e47191c74448a1635cb1704551528381dc Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:31:26 -0500
Subject: [PATCH 059/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 4efb4ce2186..b46becfcfac 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -166,8 +166,8 @@ If we start reading the Traceback at the bottom (as we should) we quickly see th
```python
>>> import numbers
-...
-...
+...
+...
... def int_division(dividend, divisor):
... assert divisor != 0, "divisor must not be 0"
... assert isinstance(divisor, numbers.Number), "divisor must be a number"
From 6057f11ca43c202270667f794ed54e58155c2566 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:31:44 -0500
Subject: [PATCH 060/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index b46becfcfac..0fee61c10c9 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -145,7 +145,7 @@ Following is an example of using `assert`:
>>> def int_division(dividend, divisor):
... assert divisor != 0, "divisor must not be 0"
... return dividend // divisor
-...
+...
>>> print(int_division(2, 1))
2
>>> print(int_division(2, 0))
From cbce6d8d75b4b8efd1f6781772633c47ab993665 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:32:04 -0500
Subject: [PATCH 061/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 0fee61c10c9..a255a158b59 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -139,7 +139,7 @@ When an `assert` evaluates to `False` it will raise an [`AssertionError`][Assert
The Traceback for the `AssertionError` can include an optional message that is part of the `assert` statement.
Although a message is optional, it is good practice to always include one in the `assert` definition.
-Following is an example of using `assert`:
+The following is an example of using `assert`:
```python
>>> def int_division(dividend, divisor):
From 94f50e8986b3a5f1310512223326420c31697d3a Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:32:23 -0500
Subject: [PATCH 062/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index a255a158b59..d1090ef0569 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -95,7 +95,7 @@ What is logged can be configured by the logging severity (e.g., 'DEBUG', 'INFO',
A call to the `logging.error` function can pass `True` to the `exc_info` parameter, which will additionally log the stack trace.
By configuring multiple handlers, logging can write to more than one place with the same logging function.
-Following is a an example of logging printed to the console:
+Following is an example of logging printed to the console:
```python
From 13e11689c99c508aaca1b819fa74c323f562e384 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 04:32:37 -0500
Subject: [PATCH 063/932] Update docs/TRACEBACKS.md
Co-authored-by: Victor Goff
---
docs/TRACEBACKS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index d1090ef0569..2e873a2c74b 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -22,7 +22,7 @@ The Traceback will show where the exception was raised and what functions were c
`ValueError` is a common exception.
-Following is an example of `ValueError` resulting from trying to assign two variables on the left from only one value on the right:
+The following is an example of `ValueError` resulting from trying to assign two variables on the left from only one value on the right:
```python
>>> first, second = [1]
From e310d7789db7a2398d71e97ef8ac4ab1ea8243ea Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 06:06:44 -0500
Subject: [PATCH 064/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 2e873a2c74b..d003b0bb59a 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -33,8 +33,8 @@ ValueError: not enough values to unpack (expected 2, got 1)
```
-It says that the most recent call is last, so the place to start is with the exception at the bottom of the trace.
-From there it can be traced back to the start.
+Tracebacks are organized with the most recent call is last, so the place to start reading the Traceback is with the exception at the bottom.
+By reading upwards from there, we can see how that statement was reached.
If we place the offending line in a function and then call the function we see a longer trace:
```python
@@ -114,7 +114,7 @@ Following is an example of logging printed to the console:
```
The level is configured as `INFO` because the default level is `WARNING`.
-For a persistent log, the logger can be configured to use a file, like so:
+For a persistent log, the logger can be configured to write to a file, like so:
```python
From 9f38f4513c8beeeaaec5c363ec5af672cc0588d3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 06:11:33 -0500
Subject: [PATCH 065/932] Update TRACEBACKS.md
Expanded int to integer
---
docs/TRACEBACKS.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index d003b0bb59a..bbcf71e7c0d 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -63,7 +63,7 @@ The [print][print] function can be used for printing the value to the console.
The following is an example of a function that doesn't return the value expected:
```python
-# the intent is to only pass an int to this function and get an int back
+# the intent is to pass an integer to this function and get an integer back
def halve_and_quadruple(num):
return (num / 2) * 4
```
@@ -72,12 +72,12 @@ When the function is passed `5`, the expected value is `8`, but it it returns `1
To troubleshoot, the calculating is broken up so that the value can be inspected at every step.
```python
-# the intent is to only pass an int to this function and get an int back
+# the intent is to pass an integer to this function and get an integer back
def halve_and_quadruple(num):
# verify the number in is what is expected
# prints 5
print(num)
- # we want the int divided by an int to be an int
+ # we want the int divided by an integer to be an integer
# but this prints 2.5! We've found our mistake.
print(num / 2)
# this makes sense, since 2.5 x 4 = 10.0
From e396542286c0006c4b1b3af8f56fb8918cda3564 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 06:14:21 -0500
Subject: [PATCH 066/932] Update docs/TRACEBACKS.md
Co-authored-by: Isaac Good
---
docs/TRACEBACKS.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index bbcf71e7c0d..96f0d003f29 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -84,8 +84,6 @@ def halve_and_quadruple(num):
print((num / 2) * 4)
return (num / 2) * 4
-```
-
What the `print` calls revealed is that we used `/` when we should have used `//`, the [floor divison operator][floor divison operator].
## Logging
From 748d0c192244cf99975f0fec25ece48e1f8e7ac4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 06:15:26 -0500
Subject: [PATCH 067/932] Update docs/TRACEBACKS.md
Co-authored-by: Isaac Good
---
docs/TRACEBACKS.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 96f0d003f29..6d6c27822c4 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -98,11 +98,11 @@ Following is an example of logging printed to the console:
```python
>>> import logging
-...
-... # configures minimum logging level as INFO
-... logging.basicConfig(level=logging.INFO)
-
-... def halve_and_quadruple(num):
+>>>
+>>> # configures minimum logging level as INFO
+>>> logging.basicConfig(level=logging.INFO)
+>>>
+>>> def halve_and_quadruple(num):
... # prints INFO:root: num == 5
... logging.info(f" num == {num}")
... return (num // 2) * 4
From 29b2bd0bf42a38a8a4bdb0a3e343dada3dc53026 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 06:20:46 -0500
Subject: [PATCH 068/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 6d6c27822c4..e4a407e1a61 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -115,11 +115,10 @@ The level is configured as `INFO` because the default level is `WARNING`.
For a persistent log, the logger can be configured to write to a file, like so:
```python
-
>>> import logging
...
-... # configures the output file name to example.log, and the minimum logging level as INFO
-... logging.basicConfig(filename='example.log', level=logging.INFO)
+>>> # configures the output file name to example.log, and the minimum logging level as INFO
+>>> logging.basicConfig(filename='example.log', level=logging.INFO)
...
... def halve_and_quadruple(num):
... # prints INFO:root: num == 5 to the example.log file
@@ -127,7 +126,6 @@ For a persistent log, the logger can be configured to write to a file, like so:
... return (num // 2) * 4
...
>>> print(halve_and_quadruple(5))
-
```
## assert
From ee9120ce129b7d7195952933c0b1ae291616ceae Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 22 Jul 2022 07:34:48 -0500
Subject: [PATCH 069/932] Update TDD.md
Added code block endings whose omission caused the links to not parse. Slipped by my critique fatigue.
---
docs/TDD.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/docs/TDD.md b/docs/TDD.md
index 275df21a18c..02bb98308c3 100644
--- a/docs/TDD.md
+++ b/docs/TDD.md
@@ -18,7 +18,7 @@ It is not simply rewriting code to fix bugs.
It is sometimes called "refactoring" to modify code to get it to pass the tests.
Although modifying the code _may_ include improving the design as a way to pass the tests, simply debugging is not necessarily improving the _design_ of the code, and so is not necessarily _refactoring_.
-Following is an example of debugging without refactoring:
+The following is an example of debugging without refactoring:
```python
# A function intended to return x added to y.
@@ -33,7 +33,7 @@ def add(x, y):
```
-Following is an example of refactoring, and then debugging:
+The following is an example of refactoring, and then debugging:
```python
@@ -178,6 +178,7 @@ VOWELS = "AEIOU"
def has_all_vowels(sentence):
return all(letter in sentence.casefold() for letter in VOWELS)
""", number=loops) / loops)
+```
Running the code a million times took an average `4.965089999896008e-07` seconds per call (about `497` nanoseconds per call).
@@ -198,6 +199,7 @@ def has_all_vowels(sentence):
sentence = sentence.casefold()
return all(letter in sentence for letter in VOWELS)
""", number=loops) / loops)
+```
Running the code a million times took an average `4.923898000270128e-07` seconds per call (about `492` nanoseconds per call.)
So, taking the `casefold` out of the list comprehension saved about `5` nanoseconds per call, or about `5` milliseconds total for a million calls.
From ae9f2ccfeca913ac96aabcdbdf8aacf8273b6ed6 Mon Sep 17 00:00:00 2001
From: MultiP-Dev
Date: Sun, 24 Jul 2022 15:23:01 +0200
Subject: [PATCH 070/932] [Ghost Gobble Arcade Game] Fixed docstring
Changed the touching_power_pellet param docstring on the score() function to accurately reflect the instructions and the param name itself. Changed from "does the player have an active power pellet" to "is the player touching a power pellet".
---
exercises/concept/ghost-gobble-arcade-game/arcade_game.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/ghost-gobble-arcade-game/arcade_game.py b/exercises/concept/ghost-gobble-arcade-game/arcade_game.py
index 479109bbcaf..c9807d23207 100644
--- a/exercises/concept/ghost-gobble-arcade-game/arcade_game.py
+++ b/exercises/concept/ghost-gobble-arcade-game/arcade_game.py
@@ -15,7 +15,7 @@ def eat_ghost(power_pellet_active, touching_ghost):
def score(touching_power_pellet, touching_dot):
"""Verify that Pac-Man has scored when a power pellet or dot has been eaten.
- :param touching_power_pellet: bool - does the player have an active power pellet?
+ :param touching_power_pellet: bool - is the player touching a power pellet?
:param touching_dot: bool - is the player touching a dot?
:return: bool - has the player scored or not?
"""
From 280e43e2bdbc3495df1cf51cb6ef59c0c6c69cfa Mon Sep 17 00:00:00 2001
From: MultiP-Dev
Date: Sun, 24 Jul 2022 15:35:36 +0200
Subject: [PATCH 071/932] [Ghost Gobble Arcade Game] Fixed docstring
Incorporate the changes made in arcade_game.py into .meta/exemplar.py:
Changed the touching_power_pellet param docstring on the score() function to accurately reflect the instructions and the param name itself. Changed from "does the player have an active power pellet" to "is the player touching a power pellet".
---
exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py b/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py
index 710364d3b3b..1adb3e4579c 100644
--- a/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py
+++ b/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py
@@ -15,7 +15,7 @@ def eat_ghost(power_pellet_active, touching_ghost):
def score(touching_power_pellet, touching_dot):
"""Verify that Pac-Man has scored when a power pellet or dot has been eaten.
- :param touching_power_pellet: bool - does the player have an active power pellet?
+ :param touching_power_pellet: bool - is the player touching a power pellet?
:param touching_dot: bool - is the player touching a dot?
:return: bool - has the player scored or not?
"""
From ed724ad18ddb779dd2b2a70a68eb64d9e3639f05 Mon Sep 17 00:00:00 2001
From: MultiP-Dev
Date: Sun, 24 Jul 2022 15:44:44 +0200
Subject: [PATCH 072/932] [Python Bools Concept] Small typo fix
Fixed a typo on the file python/concepts/bools/about.md. Changed from "Pythons" to "Python's".
---
concepts/bools/about.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/bools/about.md b/concepts/bools/about.md
index e5480837913..4b697270659 100644
--- a/concepts/bools/about.md
+++ b/concepts/bools/about.md
@@ -29,7 +29,7 @@ False
True
```
-All `boolean operators` are considered lower precedence than Pythons [`comparison operators`][comparisons], such as `==`, `>`, `<`, `is` and `is not`.
+All `boolean operators` are considered lower precedence than Python's [`comparison operators`][comparisons], such as `==`, `>`, `<`, `is` and `is not`.
## Type Coercion and Truthiness
From c4e7b3f67641eddf2cece9bbcd52986e227ae85f Mon Sep 17 00:00:00 2001
From: unfirth
Date: Mon, 25 Jul 2022 11:57:48 -0400
Subject: [PATCH 073/932] Added a period to the introduction.md
---
exercises/concept/card-games/.docs/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/card-games/.docs/introduction.md b/exercises/concept/card-games/.docs/introduction.md
index a40eb230b1d..16502460249 100644
--- a/exercises/concept/card-games/.docs/introduction.md
+++ b/exercises/concept/card-games/.docs/introduction.md
@@ -3,7 +3,7 @@
A [`list`][list] is a mutable collection of items in _sequence_.
Like most collections (_see the built-ins [`tuple`][tuple], [`dict`][dict] and [`set`][set]_), lists can hold reference to any (or multiple) data type(s) - including other lists.
Like any [sequence][sequence type], items can be accessed via `0-based index` number from the left and `-1-base index` from the right.
-Lists can be copied in whole or in part via [slice notation][slice notation] or `.copy()`
+Lists can be copied in whole or in part via [slice notation][slice notation] or `.copy()`.
Lists support both [common][common sequence operations] and [mutable][mutable sequence operations] sequence operations such as `min()`/`max()`, `.index()`, `.append()` and `.reverse()`.
List elements can be iterated over using the `for item in ` construct.
From 7a587c4fb7f6d07d6c69f06846792930cbe08f61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Santos?=
Date: Sun, 31 Jul 2022 11:45:30 +0100
Subject: [PATCH 074/932] blackjack: Add test with ace as first card for
'value_of_ace' function
---
exercises/concept/black-jack/black_jack_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/black-jack/black_jack_test.py b/exercises/concept/black-jack/black_jack_test.py
index ba3005cad69..79072f95da3 100644
--- a/exercises/concept/black-jack/black_jack_test.py
+++ b/exercises/concept/black-jack/black_jack_test.py
@@ -51,7 +51,7 @@ def test_value_of_ace(self):
('2', '3', 11), ('3', '6', 11), ('5', '2', 11),
('8', '2', 11), ('5', '5', 11), ('Q', 'A', 1),
('10', '2', 1), ('7', '8', 1), ('J', '9', 1),
- ('K', 'K', 1), ('2', 'A', 1)]
+ ('K', 'K', 1), ('2', 'A', 1), ('A', '2', 1)]
for variant, (card_one, card_two, ace_value) in enumerate(data, 1):
with self.subTest(f'variation #{variant}', card_one=card_one, card_two=card_two, ace_value=ace_value):
From 48e47c1f6af8050e135747461085d713a3231164 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 9 Aug 2022 21:39:26 -0700
Subject: [PATCH 075/932] Added Missing Close to Code Fence
Needed to close the Python code fence for the first block under `Representing Your Class`.
---
exercises/practice/clock/.docs/instructions.append.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/clock/.docs/instructions.append.md b/exercises/practice/clock/.docs/instructions.append.md
index 76fb934b61b..f030dfa3db6 100644
--- a/exercises/practice/clock/.docs/instructions.append.md
+++ b/exercises/practice/clock/.docs/instructions.append.md
@@ -12,6 +12,7 @@ For example, if you were to create a new `datetime.datetime` object in the Pytho
>>> new_date = datetime(2022, 5, 4)
>>> new_date
datetime.datetime(2022, 5, 4, 0, 0)
+```
Your Clock `class` will create a custom `object` that handles times without dates.
One important aspect of this `class` will be how it is represented as a _string_.
From f789c074a35eb57ca810b9e83d6932ae8a90d595 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 13 Aug 2022 19:06:38 -0700
Subject: [PATCH 076/932] [Python Version Upgrade]: First Pass at Upgrading
Track to use `python:3.10.6-slim` (#3158)
* First pass at upgrading track to python:3.10.6-slim.
Adjusted GH workflow to use Python 3.10.6 for the CI setup.
Edited pylintrc to omit depreicated check for whitespace.
Checked and pinned library versions for requirements.txt and requirements-generator.txt
Re-tested all exercises to see if they'd explode.
* pylint 2.14.5 needs astroid 2.12.
* Upgraded JinJa2 to latest, pinned astroid to <=2.12.0.
* Dropping support for python 3.6, adding python 3.10.6 to version matrix.
* Pinned version of JinJa2 to 3.1.2
---
.github/workflows/ci-workflow.yml | 6 +++---
pylintrc | 2 +-
requirements-generator.txt | 4 ++--
requirements.txt | 10 +++++-----
4 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml
index e0c3e31a230..fe8ff089a67 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.8
+ python-version: 3.10.6
- name: Download & Install dependencies
run: |
@@ -53,7 +53,7 @@ jobs:
needs: housekeeping
strategy:
matrix:
- python-version: [3.6, 3.7, 3.8, 3.9]
+ python-version: [3.7, 3.8, 3.9, 3.10.6]
steps:
- uses: actions/checkout@v3
@@ -66,7 +66,7 @@ jobs:
run: pip install dataclasses
- name: Install pytest
- run: pip install pytest~=7.0.1
+ run: pip install pytest~=7.1.2
- name: Check exercises
run: |
diff --git a/pylintrc b/pylintrc
index 98f0c5904d0..285f630a404 100644
--- a/pylintrc
+++ b/pylintrc
@@ -231,7 +231,7 @@ single-line-if-stmt=yes
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
-no-space-check=
+# no-space-check=
# Maximum number of lines in a module
max-module-lines=99999
diff --git a/requirements-generator.txt b/requirements-generator.txt
index 0cef6fc5d16..44391b2d663 100644
--- a/requirements-generator.txt
+++ b/requirements-generator.txt
@@ -1,6 +1,6 @@
black==22.3.0
flake8==3.7.8
-Jinja2==2.10.1
+Jinja2~=3.1.2
python-dateutil==2.8.1
markupsafe==2.0.1
-tomli
\ No newline at end of file
+tomli~=2.0.1
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 0c53800e91f..d8556f8d612 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
-astroid>=2.6.5
-flake8==4.0.1
-pylint>=2.9.2
-yapf>=0.31.0
-tomli
\ No newline at end of file
+astroid<=2.12.0
+flake8~=5.0.4
+pylint~=2.14.5
+yapf~=0.32.0
+tomli~=2.0.1
\ No newline at end of file
From 9d42937c73a60626a96460f3ac407ca376942e6d Mon Sep 17 00:00:00 2001
From: Kai Aragaki <7370516+KaiAragaki@users.noreply.github.com>
Date: Sun, 14 Aug 2022 12:38:42 -0400
Subject: [PATCH 077/932] Correct PEP 515 underscore rules
---
concepts/numbers/introduction.md | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md
index a53df50380c..c72139289a7 100644
--- a/concepts/numbers/introduction.md
+++ b/concepts/numbers/introduction.md
@@ -84,10 +84,9 @@ As of version 3.6, Python supports the use of underscores in numerical literals
35000000.0
```
-Rules for using underscores as outlined in [pep 515][pep 515] are as follows:
-* Only one consecutive underscore allowed, and only between digits.
-* Multiple consecutive underscores allowed, but only between digits.
-* Multiple consecutive underscores allowed, in most positions except for the start of the literal, or special positions like after a decimal point.
+The rules for underscores are outline in [pep 515][pep 515] under 'Literal Grammar' are quite dense, but essentially boil down to:
+* Underscores can only be between two digits (not at beginning or ends of numbers, or next to signs (+/-) or decimals points)
+* No consecutive underscores
[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic#:~:text=In%20computer%20science%2C%20arbitrary%2Dprecision,memory%20of%20the%20host%20system.
[numeric-type-docs]: https://docs.python.org/3/library/stdtypes.html#typesnumeric
@@ -95,4 +94,4 @@ Rules for using underscores as outlined in [pep 515][pep 515] are as follows:
[`float()` built in]: https://docs.python.org/3/library/functions.html#float
[0.30000000000000004.com]: https://0.30000000000000004.com/
[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
-[pep 515]: https://www.python.org/dev/peps/pep-0515/
\ No newline at end of file
+[pep 515]: https://www.python.org/dev/peps/pep-0515/
From 651cbff0e39625d0fb238ca2008429e2967263dd Mon Sep 17 00:00:00 2001
From: Kai Aragaki <7370516+KaiAragaki@users.noreply.github.com>
Date: Sun, 14 Aug 2022 12:48:35 -0400
Subject: [PATCH 078/932] Adding contribution from #3159
---
concepts/numbers/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json
index 01fa3445406..583a8284a81 100644
--- a/concepts/numbers/.meta/config.json
+++ b/concepts/numbers/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "There are three different types of built-in numbers: integers (\"int\"), floating-point (\"float\"), and complex (\"complex\"). Ints have arbitrary precision and floats typically have 15 decimal places of precision -- but both Int and float precision vary by host system. Complex numbers have a real and an imaginary part - each represented by floats.",
"authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007"],
- "contributors": ["BethanyG"]
+ "contributors": ["BethanyG", "KaiAragaki"]
}
From 44e7230b9c8d7d42afa0052539cdd5e029687604 Mon Sep 17 00:00:00 2001
From: Rob Van Dam
Date: Wed, 17 Aug 2022 12:15:47 -0600
Subject: [PATCH 079/932] Fix minor typo
---
exercises/concept/cater-waiter/sets_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/cater-waiter/sets_test.py b/exercises/concept/cater-waiter/sets_test.py
index dd2ee05e41a..7ba2c2dc4f3 100644
--- a/exercises/concept/cater-waiter/sets_test.py
+++ b/exercises/concept/cater-waiter/sets_test.py
@@ -70,7 +70,7 @@ def test_categorize_dish(self):
for variant, (item, result) in enumerate(test_data, start=1):
with self.subTest(f"variation #{variant}", inputs="all recipes list", results="categorized dishes"):
- error_message = f"Exptected category {result} for {item[0]}, but got a different category instead."
+ error_message = f"Expected category {result} for {item[0]}, but got a different category instead."
self.assertEqual(categorize_dish(item[1], item[2]), (result), msg=error_message)
@pytest.mark.task(taskno=4)
From 04831cd02b85575226b138d6236e371c321ad4bc Mon Sep 17 00:00:00 2001
From: Aleksandr Shtaub <91491081+aleksandr-shtaub@users.noreply.github.com>
Date: Mon, 22 Aug 2022 21:40:31 +0300
Subject: [PATCH 080/932] Fix typo in `powershell`
---
exercises/shared/.docs/tests.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/shared/.docs/tests.md b/exercises/shared/.docs/tests.md
index d0735cbfd69..5d1c9b9959d 100644
--- a/exercises/shared/.docs/tests.md
+++ b/exercises/shared/.docs/tests.md
@@ -37,7 +37,7 @@ $ python3 -m pytest -o markers=task {exercise_test.py}
```
Windows
-```pwowershell
+```powershell
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
From 8973fefd884cfd4cff566184f9fe1ae8d5d5ba50 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 28 Aug 2022 14:42:38 -0700
Subject: [PATCH 081/932] [Linting]: Update .pylintrc to Reflect Pylint 2.6++
Deprecations (#3167)
As of Pylint `2.6++`, there have been rule deprecations and a few Python feature deprecations.
When upgrading our tooling to use Python `3.10.6`, we forgot some of these deprecations in this `.pylintrc` file and they will toss errors with the version of Pylint we are using.
This edit should fix that problem.
---
pylintrc | 30 +++++++++++-------------------
1 file changed, 11 insertions(+), 19 deletions(-)
diff --git a/pylintrc b/pylintrc
index 285f630a404..745e6f039af 100644
--- a/pylintrc
+++ b/pylintrc
@@ -57,51 +57,43 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
-# inconsistent-return-statements,
+# As of Pylint 2.6+, the following options are not supported, so they're commented out:
+#
+# misplaced-comparison-constant
+# relative-import
+# input-builtin
+# inconsistent-return-statements
+# no-absolute-import
+# raising-string
+# round-builtin
+
disable=arguments-differ,
attribute-defined-outside-init,
- duplicate-code,
- filter-builtin-not-iterating,
fixme,
global-statement,
implicit-str-concat-in-sequence,
import-error,
import-self,
- input-builtin,
locally-disabled,
- misplaced-comparison-constant,
- missing-class-docstring,
- missing-function-docstring,
- missing-module-docstring,
- no-absolute-import,
no-else-break,
no-else-continue,
no-else-raise,
no-else-return,
no-member,
no-name-in-module,
- no-self-use,
- raising-string,
- relative-import,
- round-builtin,
signature-differs,
suppressed-message,
- too-few-public-methods,
- too-many-ancestors,
- too-many-arguments,
too-many-boolean-expressions,
too-many-branches,
- too-many-instance-attributes,
too-many-locals,
- too-many-nested-blocks,
too-many-public-methods,
too-many-return-statements,
too-many-statements,
unnecessary-pass,
unused-argument,
- unused-import,
useless-suppression
+
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
From 16ad9f1dd622030d8f02968faf45db700e80631f Mon Sep 17 00:00:00 2001
From: Pranas Ziaukas
Date: Thu, 25 Aug 2022 14:22:09 +0300
Subject: [PATCH 082/932] [Currency Exchange] Improve the `int`/`float` usage
consistency
---
.../concept/currency-exchange/.docs/hints.md | 7 +++-
.../currency-exchange/.docs/instructions.md | 32 ++++++---------
.../currency-exchange/.meta/config.json | 1 +
.../currency-exchange/.meta/exemplar.py | 40 +++++++++----------
.../concept/currency-exchange/exchange.py | 14 +++----
.../currency-exchange/exchange_test.py | 35 +++++++---------
6 files changed, 59 insertions(+), 70 deletions(-)
diff --git a/exercises/concept/currency-exchange/.docs/hints.md b/exercises/concept/currency-exchange/.docs/hints.md
index 95906ef164a..11baf246262 100644
--- a/exercises/concept/currency-exchange/.docs/hints.md
+++ b/exercises/concept/currency-exchange/.docs/hints.md
@@ -24,7 +24,12 @@
**Note:** The `//` operator also does floor division. But, if the operand has `float`, the result is still `float`.
-## 5. Calculate value after exchange
+## 5. Calculate leftover after exchanging into bills
+
+- You need to find the remainder of `budget` that does not equal a whole `denomination`.
+- Modulo operator `%` finds the remainder.
+
+## 6. Calculate value after exchange
- You need to calculate `spread` percent of `exchange_rate` using multiplication operator and add it to `exchange_rate` to get the exchanged currency.
- The actual rate needs to be computed. Remember to add exchange rate and exchange fee.
diff --git a/exercises/concept/currency-exchange/.docs/instructions.md b/exercises/concept/currency-exchange/.docs/instructions.md
index 8e41ec0ca7a..e760e62bc89 100644
--- a/exercises/concept/currency-exchange/.docs/instructions.md
+++ b/exercises/concept/currency-exchange/.docs/instructions.md
@@ -62,7 +62,19 @@ Effectively, you are rounding _down_ to the nearest whole bill/denomination.
25
```
-## 5. Calculate value after exchange
+## 5. Calculate leftover after exchanging into bills
+
+Create the `get_leftover_of_bills()` function, taking `budget` and `denomination`.
+
+This function should return the _leftover amount_ that cannot be exchanged from your _budget_ given the denomination of bills.
+It is very important to know how much exactly the booth gets to keep.
+
+```python
+>>> get_leftover_of_bills(127.5, 20)
+7.5
+```
+
+## 6. Calculate value after exchange
Create the `exchangeable_value()` function, taking `budget`, `exchange_rate`, `spread`, and `denomination`.
@@ -81,21 +93,3 @@ Remember that the currency *denomination* is a whole number, and cannot be sub-d
>>> exchangeable_value(127.25, 1.20, 10, 5)
95
```
-
-## 6. Calculate non-exchangeable value
-
-Create the `non_exchangeable_value()` function, taking `budget`, `exchange_rate`, `spread`, and `denomination`.
-
-This function should return the value that is *not* exchangeable due to the *denomination* of the bills.
-Remember - this booth gets to keep the change _in addition_ to charging an exchange fee.
-Start by calculating the value you would receive if you were able to keep subdivided bills, then subtract the amount you would receive in whole bills.
-Both amounts should take the spread, or the exchange fee into account.
-
-**Note:** Returned value should be `int` type.
-
-```python
->>> non_exchangeable_value(127.25, 1.20, 10, 20)
-16
->>> non_exchangeable_value(127.25, 1.20, 10, 5)
-1
-```
diff --git a/exercises/concept/currency-exchange/.meta/config.json b/exercises/concept/currency-exchange/.meta/config.json
index 11c09f84941..e002690297c 100644
--- a/exercises/concept/currency-exchange/.meta/config.json
+++ b/exercises/concept/currency-exchange/.meta/config.json
@@ -2,6 +2,7 @@
"blurb": "Learn about numbers by solving Chandler's currency exchange conundrums.",
"icon": "hyperia-forex",
"authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007", "J08K"],
+ "contributors": ["pranasziaukas"],
"files": {
"solution": ["exchange.py"],
"test": ["exchange_test.py"],
diff --git a/exercises/concept/currency-exchange/.meta/exemplar.py b/exercises/concept/currency-exchange/.meta/exemplar.py
index fc1b004bbb6..c894f2c0453 100644
--- a/exercises/concept/currency-exchange/.meta/exemplar.py
+++ b/exercises/concept/currency-exchange/.meta/exemplar.py
@@ -13,7 +13,7 @@ def get_change(budget, exchanging_value):
"""
:param budget: float - amount of money you own.
- :param exchanging_value: int - amount of your money you want to exchange now.
+ :param exchanging_value: float - amount of your money you want to exchange now.
:return: float - amount left of your starting currency after exchanging.
"""
@@ -24,11 +24,11 @@ def get_value_of_bills(denomination, number_of_bills):
"""
:param denomination: int - the value of a bill.
- :param number_of_bills: int amount of bills you received.
+ :param number_of_bills: int - amount of bills you received.
:return: int - total value of bills you now have.
"""
- return number_of_bills * denomination
+ return denomination * number_of_bills
def get_number_of_bills(budget, denomination):
@@ -39,36 +39,32 @@ def get_number_of_bills(budget, denomination):
:return: int - number of bills after exchanging all your money.
"""
- return int(budget / denomination)
+ return int(budget) // denomination
-def exchangeable_value(budget, exchange_rate, spread, denomination):
+def get_leftover_of_bills(budget, denomination):
"""
- :param budget: float - the amount of your money you are planning to exchange.
- :param exchange_rate: float - the unit value of the foreign currency.
- :param spread: int - percentage that is taken as an exchange fee.
+ :param budget: float - the amount of money you are planning to exchange.
:param denomination: int - the value of a single bill.
- :return: int - maximum value you can get.
+ :return: float - the leftover amount that cannot be exchanged given the current denomination.
"""
- exchange_fee = (exchange_rate / 100) * spread
- actual_rate = exchange_rate + exchange_fee
- exchangeable_amount = int((budget / actual_rate) / denomination)
- return exchangeable_amount * denomination
+ return budget % denomination
-def non_exchangeable_value(budget, exchange_rate, spread, denomination):
+def exchangeable_value(budget, exchange_rate, spread, denomination):
"""
- :param budget: float - amount of money you are planning to exchange.
- :param exchange_rate: float - unit value of the foreign currency.
- :param spread: int - the percentage taken as an exchange fee.
- :param denomination: int - the value of a single bill.
- :return: int - the value that cannot be exchanged, due to the denomination.
+ :param budget: float - the amount of your money you are planning to exchange.
+ :param exchange_rate: float - the unit value of the foreign currency.
+ :param spread: int - percentage that is taken as an exchange fee.
+ :param denomination: int - the value of a single bill.
+ :return: int - maximum value you can get.
"""
exchange_fee = (exchange_rate / 100) * spread
- actual_rate = exchange_rate + exchange_fee
- non_exchangeable_amount = int((budget / actual_rate) % denomination)
- return non_exchangeable_amount
+ exchange_value = exchange_money(budget, exchange_rate + exchange_fee)
+ number_of_bills = get_number_of_bills(exchange_value, denomination)
+ value_of_bills = get_value_of_bills(denomination, number_of_bills)
+ return value_of_bills
diff --git a/exercises/concept/currency-exchange/exchange.py b/exercises/concept/currency-exchange/exchange.py
index 29019e8c6b9..5b54fea4aad 100644
--- a/exercises/concept/currency-exchange/exchange.py
+++ b/exercises/concept/currency-exchange/exchange.py
@@ -13,7 +13,7 @@ def get_change(budget, exchanging_value):
"""
:param budget: float - amount of money you own.
- :param exchanging_value: int - amount of your money you want to exchange now.
+ :param exchanging_value: float - amount of your money you want to exchange now.
:return: float - amount left of your starting currency after exchanging.
"""
@@ -42,27 +42,25 @@ def get_number_of_bills(budget, denomination):
pass
-def exchangeable_value(budget, exchange_rate, spread, denomination):
+def get_leftover_of_bills(budget, denomination):
"""
- :param budget: float - the amount of your money you are planning to exchange.
- :param exchange_rate: float - the unit value of the foreign currency.
- :param spread: int - percentage that is taken as an exchange fee.
+ :param budget: float - the amount of money you are planning to exchange.
:param denomination: int - the value of a single bill.
- :return: int - maximum value you can get.
+ :return: float - the leftover amount that cannot be exchanged given the current denomination.
"""
pass
-def non_exchangeable_value(budget, exchange_rate, spread, denomination):
+def exchangeable_value(budget, exchange_rate, spread, denomination):
"""
:param budget: float - the amount of your money you are planning to exchange.
:param exchange_rate: float - the unit value of the foreign currency.
:param spread: int - percentage that is taken as an exchange fee.
:param denomination: int - the value of a single bill.
- :return: int non-exchangeable value.
+ :return: int - maximum value you can get.
"""
pass
diff --git a/exercises/concept/currency-exchange/exchange_test.py b/exercises/concept/currency-exchange/exchange_test.py
index 0eb24a25b25..694c82d68c6 100644
--- a/exercises/concept/currency-exchange/exchange_test.py
+++ b/exercises/concept/currency-exchange/exchange_test.py
@@ -5,20 +5,20 @@
get_change,
get_value_of_bills,
get_number_of_bills,
- exchangeable_value,
- non_exchangeable_value)
+ get_leftover_of_bills,
+ exchangeable_value)
class CurrencyExchangeTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_exchange_money(self):
- input_data = [(100000, 0.84), (700000, 10.1)]
- output_data = [119047, 69306]
+ input_data = [(100000, 0.8), (700000, 10.0)]
+ output_data = [125000, 70000]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
- self.assertEqual(int(exchange_money(input_data[0], input_data[1])), output_data)
+ self.assertAlmostEqual(exchange_money(input_data[0], input_data[1]), output_data)
@pytest.mark.task(taskno=2)
def test_get_change(self):
@@ -27,7 +27,7 @@ def test_get_change(self):
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
- self.assertEqual(get_change(input_data[0], input_data[1]), output_data)
+ self.assertAlmostEqual(get_change(input_data[0], input_data[1]), output_data)
@pytest.mark.task(taskno=3)
def test_get_value_of_bills(self):
@@ -48,6 +48,15 @@ def test_get_number_of_bills(self):
self.assertEqual(get_number_of_bills(input_data[0], input_data[1]), output_data)
@pytest.mark.task(taskno=5)
+ def test_get_leftover_of_bills(self):
+ input_data = [(10.1, 10), (654321.0, 5), (3.14, 2)]
+ output_data = [0.1, 1.0, 1.14]
+
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ self.assertAlmostEqual(get_leftover_of_bills(input_data[0], input_data[1]), output_data)
+
+ @pytest.mark.task(taskno=6)
def test_exchangeable_value(self):
inputs = [
(100000, 10.61, 10, 1),
@@ -61,17 +70,3 @@ def test_exchangeable_value(self):
for variant, (inputs, output_data) in enumerate(zip(inputs, output_data), start=1):
with self.subTest(f"variation #{variant}", inputs=inputs, output_data=output_data):
self.assertEqual(exchangeable_value(inputs[0], inputs[1], inputs[2], inputs[3]), output_data)
-
- @pytest.mark.task(taskno=6)
- def test_non_exchangeable_value(self):
- inputs = [
- (100000, 10.61, 10, 1),
- (1500, 0.84, 25, 40),
- (425.33, 0.0009, 30, 700),
- (12000, 0.0096, 10, 50)]
-
- output_data = [0, 28, 229, 13]
-
- for variant, (inputs, output_data) in enumerate(zip(inputs, output_data), start=1):
- with self.subTest(f"variation #{variant}", inputs=inputs, output_data=output_data):
- self.assertEqual(non_exchangeable_value(inputs[0], inputs[1], inputs[2], inputs[3]), output_data)
From c29f12fa40c8633cfc565e1b28aa3916917554f0 Mon Sep 17 00:00:00 2001
From: Pranas Ziaukas
Date: Mon, 29 Aug 2022 17:49:37 +0300
Subject: [PATCH 083/932] [Currency Exchange] Minor syntax changes
---
exercises/concept/currency-exchange/.docs/hints.md | 2 +-
exercises/concept/currency-exchange/.docs/instructions.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/currency-exchange/.docs/hints.md b/exercises/concept/currency-exchange/.docs/hints.md
index 11baf246262..b54f0d345f0 100644
--- a/exercises/concept/currency-exchange/.docs/hints.md
+++ b/exercises/concept/currency-exchange/.docs/hints.md
@@ -27,7 +27,7 @@
## 5. Calculate leftover after exchanging into bills
- You need to find the remainder of `budget` that does not equal a whole `denomination`.
-- Modulo operator `%` finds the remainder.
+- The Modulo operator `%` can help find the remainder.
## 6. Calculate value after exchange
diff --git a/exercises/concept/currency-exchange/.docs/instructions.md b/exercises/concept/currency-exchange/.docs/instructions.md
index e760e62bc89..03f7e6a1e3b 100644
--- a/exercises/concept/currency-exchange/.docs/instructions.md
+++ b/exercises/concept/currency-exchange/.docs/instructions.md
@@ -67,7 +67,7 @@ Effectively, you are rounding _down_ to the nearest whole bill/denomination.
Create the `get_leftover_of_bills()` function, taking `budget` and `denomination`.
This function should return the _leftover amount_ that cannot be exchanged from your _budget_ given the denomination of bills.
-It is very important to know how much exactly the booth gets to keep.
+It is very important to know exactly how much the booth gets to keep.
```python
>>> get_leftover_of_bills(127.5, 20)
From d5a4dd9c45c23ccef179972cf965e03830a0df22 Mon Sep 17 00:00:00 2001
From: Aleksandr Shtaub <91491081+aleksandr-shtaub@users.noreply.github.com>
Date: Thu, 1 Sep 2022 21:20:09 +0300
Subject: [PATCH 084/932] Replace `assertIs` assertion with `assertEqual`
---
.../perfect-numbers/.meta/template.j2 | 2 +-
.../perfect-numbers/perfect_numbers_test.py | 22 +++++++++----------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/exercises/practice/perfect-numbers/.meta/template.j2 b/exercises/practice/perfect-numbers/.meta/template.j2
index f7bdd525c73..c92e39ca402 100644
--- a/exercises/practice/perfect-numbers/.meta/template.j2
+++ b/exercises/practice/perfect-numbers/.meta/template.j2
@@ -8,7 +8,7 @@
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
{% else %}
- self.assertIs(
+ self.assertEqual(
{{ case["property"] | to_snake }}({{input["number"]}}),
"{{ case["expected"] }}"
)
diff --git a/exercises/practice/perfect-numbers/perfect_numbers_test.py b/exercises/practice/perfect-numbers/perfect_numbers_test.py
index 783f3c6ad54..786f92630ef 100644
--- a/exercises/practice/perfect-numbers/perfect_numbers_test.py
+++ b/exercises/practice/perfect-numbers/perfect_numbers_test.py
@@ -9,41 +9,41 @@
class PerfectNumbersTest(unittest.TestCase):
def test_smallest_perfect_number_is_classified_correctly(self):
- self.assertIs(classify(6), "perfect")
+ self.assertEqual(classify(6), "perfect")
def test_medium_perfect_number_is_classified_correctly(self):
- self.assertIs(classify(28), "perfect")
+ self.assertEqual(classify(28), "perfect")
def test_large_perfect_number_is_classified_correctly(self):
- self.assertIs(classify(33550336), "perfect")
+ self.assertEqual(classify(33550336), "perfect")
class AbundantNumbersTest(unittest.TestCase):
def test_smallest_abundant_number_is_classified_correctly(self):
- self.assertIs(classify(12), "abundant")
+ self.assertEqual(classify(12), "abundant")
def test_medium_abundant_number_is_classified_correctly(self):
- self.assertIs(classify(30), "abundant")
+ self.assertEqual(classify(30), "abundant")
def test_large_abundant_number_is_classified_correctly(self):
- self.assertIs(classify(33550335), "abundant")
+ self.assertEqual(classify(33550335), "abundant")
class DeficientNumbersTest(unittest.TestCase):
def test_smallest_prime_deficient_number_is_classified_correctly(self):
- self.assertIs(classify(2), "deficient")
+ self.assertEqual(classify(2), "deficient")
def test_smallest_non_prime_deficient_number_is_classified_correctly(self):
- self.assertIs(classify(4), "deficient")
+ self.assertEqual(classify(4), "deficient")
def test_medium_deficient_number_is_classified_correctly(self):
- self.assertIs(classify(32), "deficient")
+ self.assertEqual(classify(32), "deficient")
def test_large_deficient_number_is_classified_correctly(self):
- self.assertIs(classify(33550337), "deficient")
+ self.assertEqual(classify(33550337), "deficient")
def test_edge_case_no_factors_other_than_itself_is_classified_correctly(self):
- self.assertIs(classify(1), "deficient")
+ self.assertEqual(classify(1), "deficient")
class InvalidInputsTest(unittest.TestCase):
From a52a00ecb55898dd0754299d29815ac6ae68a177 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 9 Sep 2022 06:15:42 -0700
Subject: [PATCH 085/932] Add Prerequisite to Gigasecond
Added "classes" as a prerequisite for `Gigasecond`, so that it will not unlock until a student has completed all the existing learning exercises for the track.
Hopefully, this avoids confusion and frustration as expressed by user @GKotfis in gitter:
---
config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config.json b/config.json
index 416478aecbf..20c3aee6e0e 100644
--- a/config.json
+++ b/config.json
@@ -431,7 +431,7 @@
"name": "Gigasecond",
"uuid": "22606e91-57f3-44cf-ab2d-94f6ee6402e8",
"practices": [],
- "prerequisites": [],
+ "prerequisites": ["classes"],
"difficulty": 1
},
{
From b3368949af941199cf479bbf1900eecaf32a2110 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Sep 2022 23:08:19 +0000
Subject: [PATCH 086/932] Bump actions/stale from 5 to 6
Bumps [actions/stale](https://github.com/actions/stale) from 5 to 6.
- [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/v5...v6)
---
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 3da81f8df36..9e4ecd1d5c9 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@v5
+ - uses: actions/stale@v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
From 26419c4ae055a0e786b0090420a5e7281d883081 Mon Sep 17 00:00:00 2001
From: Kemal Soylu
Date: Sat, 24 Sep 2022 00:09:25 +0300
Subject: [PATCH 087/932] added missing comma in the example
---
exercises/concept/inventory-management/.docs/instructions.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/inventory-management/.docs/instructions.md b/exercises/concept/inventory-management/.docs/instructions.md
index f3fd749973e..e1c1806c6ea 100644
--- a/exercises/concept/inventory-management/.docs/instructions.md
+++ b/exercises/concept/inventory-management/.docs/instructions.md
@@ -14,7 +14,7 @@ Implement the `create_inventory()` function that creates an "inventory" from a l
```python
>>> create_inventory(["coal", "wood", "wood", "diamond", "diamond", "diamond"])
-{"coal":1, "wood":2 "diamond":3}
+{"coal":1, "wood":2, "diamond":3}
```
## 2. Add items from a list to an existing dictionary
From 7e61fc60b84378f4918787858e727ff2a8c6aa1c Mon Sep 17 00:00:00 2001
From: Graham Ashton
Date: Tue, 27 Sep 2022 11:37:50 +0100
Subject: [PATCH 088/932] Fix bad assertion in DiffieHellmanTest
The original code passed a generator expression into the assertion,
which was converted into a list prior to being evaluated for truthiness.
If all the values in the generator are False, the assertion will check
whether `[False, False, False, ...]` is truthy, which it is.
I can't see how this test can ever have failed.
I've used `subTest()`, having seen it used in other exercises.
---
exercises/practice/diffie-hellman/.meta/template.j2 | 9 +++++++--
exercises/practice/diffie-hellman/diffie_hellman_test.py | 9 +++++++--
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/diffie-hellman/.meta/template.j2 b/exercises/practice/diffie-hellman/.meta/template.j2
index d35df8df290..864b5111d20 100644
--- a/exercises/practice/diffie-hellman/.meta/template.j2
+++ b/exercises/practice/diffie-hellman/.meta/template.j2
@@ -10,8 +10,13 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% set input = case['input'] %}
def test_{{ description }}(self):
{%- if property == "private_key_is_in_range" %}
- primes = [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
- self.assertTrue(1 < private_key(p) < p for p in primes)
+ for prime in [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]:
+ with self.subTest(f"prime={prime}"):
+ key = private_key(prime)
+ self.assertTrue(
+ 1 < key < prime,
+ msg=f"{key} out of range, expected to be >1 and <{prime}"
+ )
{%- elif property == "private_key_is_random" %}
"""
Can fail due to randomness, but most likely will not,
diff --git a/exercises/practice/diffie-hellman/diffie_hellman_test.py b/exercises/practice/diffie-hellman/diffie_hellman_test.py
index 917707cdcf7..a056c8e3b9e 100644
--- a/exercises/practice/diffie-hellman/diffie_hellman_test.py
+++ b/exercises/practice/diffie-hellman/diffie_hellman_test.py
@@ -11,8 +11,13 @@
class DiffieHellmanTest(unittest.TestCase):
def test_private_key_is_greater_than_1_and_less_than_p(self):
- primes = [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
- self.assertTrue(1 < private_key(p) < p for p in primes)
+ for prime in [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]:
+ with self.subTest(f"prime={prime}"):
+ key = private_key(prime)
+ self.assertTrue(
+ 1 < key < prime,
+ msg=f"{key} out of range, expected to be >1 and <{prime}",
+ )
def test_private_key_is_random(self):
"""
From 5571880a4742089fb108f80dbeb981ad48c48fde Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 30 Sep 2022 13:40:52 -0700
Subject: [PATCH 089/932] Changed assertion to be one line
Changed the template and file to have the assertion be one line. Otherwise, we risk the website cutting off the test code.
---
exercises/practice/diffie-hellman/.meta/template.j2 | 5 +----
exercises/practice/diffie-hellman/diffie_hellman_test.py | 5 +----
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/diffie-hellman/.meta/template.j2 b/exercises/practice/diffie-hellman/.meta/template.j2
index 864b5111d20..b9634cdc302 100644
--- a/exercises/practice/diffie-hellman/.meta/template.j2
+++ b/exercises/practice/diffie-hellman/.meta/template.j2
@@ -13,10 +13,7 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
for prime in [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]:
with self.subTest(f"prime={prime}"):
key = private_key(prime)
- self.assertTrue(
- 1 < key < prime,
- msg=f"{key} out of range, expected to be >1 and <{prime}"
- )
+ self.assertTrue(1 < key < prime, msg=f"{key} out of range, expected to be >1 and <{prime}") # fmt: skip
{%- elif property == "private_key_is_random" %}
"""
Can fail due to randomness, but most likely will not,
diff --git a/exercises/practice/diffie-hellman/diffie_hellman_test.py b/exercises/practice/diffie-hellman/diffie_hellman_test.py
index a056c8e3b9e..716b7ba1917 100644
--- a/exercises/practice/diffie-hellman/diffie_hellman_test.py
+++ b/exercises/practice/diffie-hellman/diffie_hellman_test.py
@@ -14,10 +14,7 @@ def test_private_key_is_greater_than_1_and_less_than_p(self):
for prime in [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]:
with self.subTest(f"prime={prime}"):
key = private_key(prime)
- self.assertTrue(
- 1 < key < prime,
- msg=f"{key} out of range, expected to be >1 and <{prime}",
- )
+ self.assertTrue(1 < key < prime, msg=f"{key} out of range, expected to be >1 and <{prime}") # fmt: skip
def test_private_key_is_random(self):
"""
From 41e02cc8a6a541182c9dfe3aa7df13a142febc12 Mon Sep 17 00:00:00 2001
From: PaulT89
Date: Thu, 13 Oct 2022 23:09:26 +1100
Subject: [PATCH 090/932] Regenerate Tests
---
.../palindrome-products/palindrome_products_test.py | 8 ++++----
exercises/practice/poker/poker_test.py | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/exercises/practice/palindrome-products/palindrome_products_test.py b/exercises/practice/palindrome-products/palindrome_products_test.py
index c717485ecea..2ff69adc2d7 100644
--- a/exercises/practice/palindrome-products/palindrome_products_test.py
+++ b/exercises/practice/palindrome-products/palindrome_products_test.py
@@ -9,12 +9,12 @@
class PalindromeProductsTest(unittest.TestCase):
- def test_finds_the_smallest_palindrome_from_single_digit_factors(self):
+ def test_find_the_smallest_palindrome_from_single_digit_factors(self):
value, factors = smallest(min_factor=1, max_factor=9)
self.assertEqual(value, 1)
self.assertFactorsEqual(factors, [[1, 1]])
- def test_finds_the_largest_palindrome_from_single_digit_factors(self):
+ def test_find_the_largest_palindrome_from_single_digit_factors(self):
value, factors = largest(min_factor=1, max_factor=9)
self.assertEqual(value, 9)
self.assertFactorsEqual(factors, [[1, 9], [3, 3]])
@@ -29,7 +29,7 @@ def test_find_the_largest_palindrome_from_double_digit_factors(self):
self.assertEqual(value, 9009)
self.assertFactorsEqual(factors, [[91, 99]])
- def test_find_smallest_palindrome_from_triple_digit_factors(self):
+ def test_find_the_smallest_palindrome_from_triple_digit_factors(self):
value, factors = smallest(min_factor=100, max_factor=999)
self.assertEqual(value, 10201)
self.assertFactorsEqual(factors, [[101, 101]])
@@ -39,7 +39,7 @@ def test_find_the_largest_palindrome_from_triple_digit_factors(self):
self.assertEqual(value, 906609)
self.assertFactorsEqual(factors, [[913, 993]])
- def test_find_smallest_palindrome_from_four_digit_factors(self):
+ def test_find_the_smallest_palindrome_from_four_digit_factors(self):
value, factors = smallest(min_factor=1000, max_factor=9999)
self.assertEqual(value, 1002001)
self.assertFactorsEqual(factors, [[1001, 1001]])
diff --git a/exercises/practice/poker/poker_test.py b/exercises/practice/poker/poker_test.py
index 0273bf0c51e..7951cb533a5 100644
--- a/exercises/practice/poker/poker_test.py
+++ b/exercises/practice/poker/poker_test.py
@@ -178,7 +178,7 @@ def test_straight_flush_beats_four_of_a_kind(self):
best_hands(["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"]), ["7S 8S 9S 6S 10S"]
)
- def test_both_hands_have_straight_flush_tie_goes_to_highest_ranked_card(self):
+ def test_both_hands_have_a_straight_flush_tie_goes_to_highest_ranked_card(self):
self.assertEqual(
best_hands(["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"]), ["5S 7S 8S 9S 6S"]
)
From 1bbfb5d133baf773a188cd9f60d79d18ee33ab64 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Wed, 12 Oct 2022 16:12:20 +0200
Subject: [PATCH 091/932] Normalize practice exercise names
We have added explicit exercise titles to the metadata.toml
files in problem-specifications.
This updates the exercise names to match the values
in this field.
---
config.json | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/config.json b/config.json
index 20c3aee6e0e..bb349eede74 100644
--- a/config.json
+++ b/config.json
@@ -319,7 +319,7 @@
},
{
"slug": "rna-transcription",
- "name": "Rna Transcription",
+ "name": "RNA Transcription",
"uuid": "ca7c5ef1-5135-4fb4-8e68-669ef0f2a51a",
"practices": ["string-methods"],
"prerequisites": [
@@ -342,7 +342,7 @@
},
{
"slug": "etl",
- "name": "Etl",
+ "name": "ETL",
"uuid": "a3b24ef2-303a-494e-8804-e52a67ef406b",
"practices": ["dicts"],
"prerequisites": ["dicts"],
@@ -366,7 +366,7 @@
},
{
"slug": "sum-of-multiples",
- "name": "Sum Of Multiples",
+ "name": "Sum of Multiples",
"uuid": "6e0caa0a-6a1a-4f03-bf0f-e07711f4b069",
"practices": ["sets"],
"prerequisites": [
@@ -398,7 +398,7 @@
},
{
"slug": "difference-of-squares",
- "name": "Difference Of Squares",
+ "name": "Difference of Squares",
"uuid": "913b6099-d75a-4c27-8243-476081752c31",
"practices": ["numbers"],
"prerequisites": ["basics", "numbers"],
@@ -436,7 +436,7 @@
},
{
"slug": "isbn-verifier",
- "name": "Isbn Verifier",
+ "name": "ISBN Verifier",
"uuid": "7961c852-c87a-44b0-b152-efea3ac8555c",
"practices": ["strings"],
"prerequisites": ["basics", "bools", "conditionals", "strings"],
@@ -770,7 +770,7 @@
},
{
"slug": "run-length-encoding",
- "name": "Run Length Encoding",
+ "name": "Run-Length Encoding",
"uuid": "505e7bdb-e18d-45fd-9849-0bf33492efd9",
"practices": ["iteration", "regular-expressions"],
"prerequisites": [
@@ -1178,7 +1178,7 @@
},
{
"slug": "ocr-numbers",
- "name": "Ocr Numbers",
+ "name": "OCR Numbers",
"uuid": "98ca48ed-5818-442c-bce1-308c8b3b3b77",
"practices": ["loops"],
"prerequisites": [
@@ -1196,7 +1196,7 @@
},
{
"slug": "diffie-hellman",
- "name": "Diffie Hellman",
+ "name": "Diffie-Hellman",
"uuid": "92e2d5f8-7d8a-4e81-a55c-52fa6be80c74",
"practices": ["numbers"],
"prerequisites": ["basics", "bools", "numbers"],
@@ -1672,7 +1672,7 @@
},
{
"slug": "dot-dsl",
- "name": "Dot Dsl",
+ "name": "DOT DSL",
"uuid": "a9c2fbda-a1e4-42dd-842f-4de5bb361b91",
"practices": [
"class-composition",
@@ -1934,7 +1934,7 @@
},
{
"slug": "sgf-parsing",
- "name": "Sgf Parsing",
+ "name": "SGF Parsing",
"uuid": "0d6325d1-c0a3-456e-9a92-cea0559e82ed",
"practices": [
"operator-overloading",
@@ -1958,7 +1958,7 @@
},
{
"slug": "paasio",
- "name": "Paasio",
+ "name": "PaaS I/O",
"uuid": "ec36f9ed-f376-43d9-90ee-be0bea0cfc98",
"practices": [
"class-customization",
@@ -1987,7 +1987,7 @@
},
{
"slug": "rest-api",
- "name": "Rest Api",
+ "name": "REST API",
"uuid": "2abe6eed-c7e4-4ad7-91e3-778bc5176726",
"practices": [
"class-composition",
@@ -2011,7 +2011,7 @@
},
{
"slug": "pov",
- "name": "Pov",
+ "name": "POV",
"uuid": "d98b1080-36d4-4357-b12a-685d204856bf",
"practices": ["rich-comparisons"],
"prerequisites": [
@@ -2103,7 +2103,7 @@
},
{
"slug": "pascals-triangle",
- "name": "Pascals Triangle",
+ "name": "Pascal's Triangle",
"uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
"practices": [],
"prerequisites": [],
From 6fee5dc9f59e2c8933494c3f198aa5a6eafce420 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Thu, 20 Oct 2022 14:54:22 +0200
Subject: [PATCH 092/932] Update bin/fetch-configlet script
The script has been tweaked to work better across all platforms.
---
bin/fetch-configlet | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/bin/fetch-configlet b/bin/fetch-configlet
index 43f1c83cee8..827088ab17c 100755
--- a/bin/fetch-configlet
+++ b/bin/fetch-configlet
@@ -1,5 +1,9 @@
#!/usr/bin/env bash
+# This file is a copy of the
+# https://github.com/exercism/configlet/blob/main/scripts/fetch-configlet file.
+# Please submit bugfixes/improvements to the above file to ensure that all tracks benefit from the changes.
+
set -eo pipefail
readonly LATEST='https://api.github.com/repos/exercism/configlet/releases/latest'
@@ -45,14 +49,26 @@ get_download_url() {
cut -d'"' -f4
}
-download_url="$(get_download_url)"
-output_dir="bin"
-output_path="${output_dir}/latest-configlet.${ext}"
-curl "${curlopts[@]}" --output "${output_path}" "${download_url}"
+main() {
+ if [[ -d ./bin ]]; then
+ output_dir="./bin"
+ elif [[ $PWD == */bin ]]; then
+ output_dir="$PWD"
+ else
+ echo "Error: no ./bin directory found. This script should be ran from a repo root." >&2
+ return 1
+ fi
-case "${ext}" in
- *zip) unzip "${output_path}" -d "${output_dir}" ;;
- *) tar xzf "${output_path}" -C "${output_dir}" ;;
-esac
+ download_url="$(get_download_url)"
+ output_path="${output_dir}/latest-configlet.${ext}"
+ curl "${curlopts[@]}" --output "${output_path}" "${download_url}"
+
+ case "${ext}" in
+ *zip) unzip "${output_path}" -d "${output_dir}" ;;
+ *) tar xzf "${output_path}" -C "${output_dir}" ;;
+ esac
+
+ rm -f "${output_path}"
+}
-rm -f "${output_path}"
+main
From 477be913de0ac53bb5185af93467f43fb88f14cf Mon Sep 17 00:00:00 2001
From: Nikolai Diachkov
Date: Fri, 21 Oct 2022 16:42:51 +0300
Subject: [PATCH 093/932] switch parent_id and record_id in exception message
according to the task instructions
---
exercises/practice/tree-building/.meta/example.py | 2 +-
exercises/practice/tree-building/tree_building_test.py | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/tree-building/.meta/example.py b/exercises/practice/tree-building/.meta/example.py
index f728147610d..e3929ea031c 100644
--- a/exercises/practice/tree-building/.meta/example.py
+++ b/exercises/practice/tree-building/.meta/example.py
@@ -18,7 +18,7 @@ def validate_record(record):
raise ValueError('Only root should have equal record and parent id.')
if not record.equal_id() and record.parent_id >= record.record_id:
- raise ValueError("Node record_id should be smaller than it's parent_id.")
+ raise ValueError("Node parent_id should be smaller than it's record_id.")
def BuildTree(records):
diff --git a/exercises/practice/tree-building/tree_building_test.py b/exercises/practice/tree-building/tree_building_test.py
index bda851dbd44..426ed2b95b3 100644
--- a/exercises/practice/tree-building/tree_building_test.py
+++ b/exercises/practice/tree-building/tree_building_test.py
@@ -111,7 +111,7 @@ def test_root_node_has_parent(self):
with self.assertRaises(ValueError) as err:
BuildTree(records)
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "Node record_id should be smaller than it's parent_id.")
+ self.assertEqual(err.exception.args[0], "Node parent_id should be smaller than it's record_id.")
def test_no_root_node(self):
records = [
@@ -167,7 +167,7 @@ def test_cycle_indirectly(self):
with self.assertRaises(ValueError) as err:
BuildTree(records)
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "Node record_id should be smaller than it's parent_id.")
+ self.assertEqual(err.exception.args[0], "Node parent_id should be smaller than it's record_id.")
def test_higher_id_parent_of_lower_id(self):
records = [
@@ -179,7 +179,7 @@ def test_higher_id_parent_of_lower_id(self):
with self.assertRaises(ValueError) as err:
BuildTree(records)
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "Node record_id should be smaller than it's parent_id.")
+ self.assertEqual(err.exception.args[0], "Node parent_id should be smaller than it's record_id.")
def assert_node_is_branch(self, node, node_id, children_count):
self.assertEqual(node.node_id, node_id)
From 6b01dbd9489c47dc54a135b63ebc78122eaed76f Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Mon, 24 Oct 2022 15:32:55 +0200
Subject: [PATCH 094/932] Remove reference to Gitter
The Exerism Forum (https://forum.exercism.org/) has now made Gitter redundant.
We're directing everyone there.
---
README.md | 1 -
exercises/shared/.docs/help.md | 2 --
2 files changed, 3 deletions(-)
diff --git a/README.md b/README.md
index 76c57fbf161..879557ac612 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,6 @@

[](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
-[](https://gitter.im/exercism/python?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
diff --git a/exercises/shared/.docs/help.md b/exercises/shared/.docs/help.md
index 0739a603eac..0e105b02d94 100644
--- a/exercises/shared/.docs/help.md
+++ b/exercises/shared/.docs/help.md
@@ -6,12 +6,10 @@ Below are some resources for getting help if you run into trouble:
- [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.
-- [**Exercism on Gitter**](https://gitter.im/exercism/home) join the Python room for Python-related questions or problems.
- [Python Community Forums](https://discuss.python.org/)
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
-
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
From ee2f6ef3df0d2f1fece1271dd844a9aca4b4c890 Mon Sep 17 00:00:00 2001
From: Rodrigo Santiago
Date: Thu, 27 Oct 2022 17:43:13 -0300
Subject: [PATCH 095/932] Fix dish_ingredients type on categorize_dish
docstring
---
exercises/concept/cater-waiter/sets.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/cater-waiter/sets.py b/exercises/concept/cater-waiter/sets.py
index b0202e6a5fb..faa0114d9c5 100644
--- a/exercises/concept/cater-waiter/sets.py
+++ b/exercises/concept/cater-waiter/sets.py
@@ -14,7 +14,7 @@ def clean_ingredients(dish_name, dish_ingredients):
"""Remove duplicates from `dish_ingredients`.
:param dish_name: str - containing the dish name.
- :param dish_ingredients: list - dish ingredients.
+ :param dish_ingredients: set - dish ingredients.
:return: tuple - containing (dish_name, ingredient set).
This function should return a `tuple` with the name of the dish as the first item,
From b5ddf119a26fc1d9970cb4b568f3d6f057b70c75 Mon Sep 17 00:00:00 2001
From: Rodrigo Santiago
Date: Thu, 27 Oct 2022 17:50:54 -0300
Subject: [PATCH 096/932] Update ethod instruction changing from list to set in
example
---
exercises/concept/cater-waiter/.docs/instructions.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/cater-waiter/.docs/instructions.md b/exercises/concept/cater-waiter/.docs/instructions.md
index 07e2f6bd1a0..2d54bb0ed2c 100644
--- a/exercises/concept/cater-waiter/.docs/instructions.md
+++ b/exercises/concept/cater-waiter/.docs/instructions.md
@@ -50,11 +50,11 @@ All dishes will "fit" into one of the categories imported from `sets_categories_
>>> from sets_categories_data import VEGAN, VEGETARIAN, PALEO, KETO, OMNIVORE
->>> categorize_dish('Sticky Lemon Tofu', ['tofu', 'soy sauce', 'salt', 'black pepper', 'cornstarch', 'vegetable oil', 'garlic', 'ginger', 'water', 'vegetable stock', 'lemon juice', 'lemon zest', 'sugar'])
+>>> categorize_dish('Sticky Lemon Tofu', {'tofu', 'soy sauce', 'salt', 'black pepper', 'cornstarch', 'vegetable oil', 'garlic', 'ginger', 'water', 'vegetable stock', 'lemon juice', 'lemon zest', 'sugar'})
...
'Sticky Lemon Tofu: VEGAN'
->>> categorize_dish('Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', ['shrimp', 'bacon', 'avocado', 'chickpeas', 'fresh tortillas', 'sea salt', 'guajillo chile', 'slivered almonds', 'olive oil', 'butter', 'black pepper', 'garlic', 'onion'])
+>>> categorize_dish('Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', {'shrimp', 'bacon', 'avocado', 'chickpeas', 'fresh tortillas', 'sea salt', 'guajillo chile', 'slivered almonds', 'olive oil', 'butter', 'black pepper', 'garlic', 'onion'})
...
'Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole: OMNIVORE'
```
From c381955d737039279ac2f6af4b278d3833199a3e Mon Sep 17 00:00:00 2001
From: Julian <55046135+0x1AE7F@users.noreply.github.com>
Date: Sun, 30 Oct 2022 18:31:44 +0100
Subject: [PATCH 097/932] Fix typo
Changed "neutrinos" (line 34) to "neutrons"
---
exercises/concept/meltdown-mitigation/conditionals_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/meltdown-mitigation/conditionals_test.py b/exercises/concept/meltdown-mitigation/conditionals_test.py
index 682144c5a6c..9837c343022 100644
--- a/exercises/concept/meltdown-mitigation/conditionals_test.py
+++ b/exercises/concept/meltdown-mitigation/conditionals_test.py
@@ -31,7 +31,7 @@ def test_is_criticality_balanced(self):
# pylint: disable=assignment-from-no-return
actual_result = is_criticality_balanced(temp, neutrons_emitted)
failure_message = (f'Expected {expected} but returned {actual_result} '
- f'with T={temp} and neutrinos={neutrons_emitted}')
+ f'with T={temp} and neutrons={neutrons_emitted}')
self.assertEqual(actual_result, expected, failure_message)
@pytest.mark.task(taskno=2)
From ab76008757f747488de614dcca74192221b10d1f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 06:23:36 -0600
Subject: [PATCH 098/932] Add approaches to Bob (#3200)
Add approaches to Bob
---
.../bob/.approaches/answer-list/content.md | 67 ++++++++++++++
.../bob/.approaches/answer-list/snippet.txt | 6 ++
.../practice/bob/.approaches/config.json | 29 ++++++
.../if-statements-nested/content.md | 48 ++++++++++
.../if-statements-nested/snippet.txt | 8 ++
.../bob/.approaches/if-statements/content.md | 51 ++++++++++
.../bob/.approaches/if-statements/snippet.txt | 7 ++
.../practice/bob/.approaches/introduction.md | 92 +++++++++++++++++++
exercises/practice/bob/.articles/config.json | 11 +++
.../.articles/performance/code/Benchmark.py | 62 +++++++++++++
.../bob/.articles/performance/content.md | 31 +++++++
.../bob/.articles/performance/snippet.md | 5 +
12 files changed, 417 insertions(+)
create mode 100644 exercises/practice/bob/.approaches/answer-list/content.md
create mode 100644 exercises/practice/bob/.approaches/answer-list/snippet.txt
create mode 100644 exercises/practice/bob/.approaches/config.json
create mode 100644 exercises/practice/bob/.approaches/if-statements-nested/content.md
create mode 100644 exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
create mode 100644 exercises/practice/bob/.approaches/if-statements/content.md
create mode 100644 exercises/practice/bob/.approaches/if-statements/snippet.txt
create mode 100644 exercises/practice/bob/.approaches/introduction.md
create mode 100644 exercises/practice/bob/.articles/config.json
create mode 100644 exercises/practice/bob/.articles/performance/code/Benchmark.py
create mode 100644 exercises/practice/bob/.articles/performance/content.md
create mode 100644 exercises/practice/bob/.articles/performance/snippet.md
diff --git a/exercises/practice/bob/.approaches/answer-list/content.md b/exercises/practice/bob/.approaches/answer-list/content.md
new file mode 100644
index 00000000000..de2049a7adb
--- /dev/null
+++ b/exercises/practice/bob/.approaches/answer-list/content.md
@@ -0,0 +1,67 @@
+# Answer list
+
+```python
+_ANSWERS = ['Whatever.', 'Sure.', 'Whoa, chill out!',
+ "Calm down, I know what I'm doing!"]
+
+
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ isShout = (2 if hey_bob.isupper() else 0)
+ isQuestion = (1 if hey_bob.endswith('?') else 0)
+ return _ANSWERS[isShout + isQuestion]
+
+```
+
+In this approach you define a [list][list] that contains Bob’s answers, and each condition is given a score.
+The correct answer is selected from the list by using the score as the list index.
+
+Python doesn't _enforce_ having real constant values,
+but the `_ANSWERS` list is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+Python also does not have real [private][private] variables,
+but a leading underscore is the naming convention for indicating that a variable is not meant to be part of the public API.
+It should be considered an implementation detail and subject to change without notice.
+
+The [`rstrip`][rstrip] method is applied to the input to eliminate any whitespace at the end of the input.
+If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the response for saying nothing.
+Since it doesn't matter if there is leading whitespace, the `rstrip` function is used instead of [`strip`][strip].
+
+A [ternary operator][ternary] is used for determining the score for a shout and a question.
+
+The [`isupper`][isupper] method is used to test that there is at least one cased character and that all cased characters are uppercase.
+
+```exercism/note
+A cased character is one which differs between lowercase and uppercase.
+For example, `?` and `3` are not cased characters, as they do not change between lowercase and uppercase.
+`a` and `z` are cased characters, since their lowercase form changes to `A` and ` Z` when uppercase.
+```
+
+If `isupper` is `True`, then `isShout` is given the value of `2`; otherwise, it is given the value of `0`.
+
+The [`endswith`][endswith] method is used to determine if the input ends with a question mark.
+If the test for `endswith('?')` is `True`, then `isQuestion` is given the value of `1`; otherwise it is given the value of `0`.
+
+
+The response is selected from the list by the index like so
+
+| isShout | isQuestion | Index | Answer |
+| ------- | ---------- | --------- | ------------------------------------- |
+| `false` | `false` | 0 + 0 = 0 | `"Whatever."` |
+| `false` | `true` | 0 + 1 = 1 | `"Sure."` |
+| `true` | `false` | 2 + 0 = 2 | `"Whoa, chill out!"` |
+| `true` | `true` | 2 + 1 = 3 | `"Calm down, I know what I'm doing!"` |
+
+
+[list]: https://docs.python.org/3/library/stdtypes.html?highlight=list#list
+[const]: https://realpython.com/python-constants/
+[private]: https://docs.python.org/3/tutorial/classes.html#private-variables
+[rstrip]: https://docs.python.org/3/library/stdtypes.html?highlight=rstrip#str.rstrip
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not]: https://docs.python.org/3/reference/expressions.html#not
+[strip]: https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.strip
+[ternary]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[isupper]: https://docs.python.org/3/library/stdtypes.html?highlight=isupper#str.isupper
+[endswith]: https://docs.python.org/3/library/stdtypes.html?highlight=endswith#str.endswith
diff --git a/exercises/practice/bob/.approaches/answer-list/snippet.txt b/exercises/practice/bob/.approaches/answer-list/snippet.txt
new file mode 100644
index 00000000000..0d834fa2718
--- /dev/null
+++ b/exercises/practice/bob/.approaches/answer-list/snippet.txt
@@ -0,0 +1,6 @@
+_ANSWERS = ['Whatever.', 'Sure.', 'Whoa, chill out!',
+ "Calm down, I know what I'm doing!"]
+# code snipped
+isShout = (2 if hey_bob.isupper() else 0)
+isQuestion = (1 if hey_bob.endswith('?') else 0)
+return _ANSWERS[isShout + isQuestion]
diff --git a/exercises/practice/bob/.approaches/config.json b/exercises/practice/bob/.approaches/config.json
new file mode 100644
index 00000000000..1c13c45f937
--- /dev/null
+++ b/exercises/practice/bob/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "972b7546-67ca-4364-9367-02d68a8c0314",
+ "slug": "if-statements",
+ "title": "If statements",
+ "blurb": "Use if statements to return the answer.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "138fcf61-262c-47f6-aac3-96528b24ee6b",
+ "slug": "if-statements-nested",
+ "title": "if statements nested",
+ "blurb": "Use nested if statements to return the answer.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "d74a1d7d-af85-417c-8deb-164536e4ab1b",
+ "slug": "answer-list",
+ "title": "Answer list",
+ "blurb": "Index into a list to return the answer.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/content.md b/exercises/practice/bob/.approaches/if-statements-nested/content.md
new file mode 100644
index 00000000000..e0702169863
--- /dev/null
+++ b/exercises/practice/bob/.approaches/if-statements-nested/content.md
@@ -0,0 +1,48 @@
+# `if` statements nested
+
+```python
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ isShout = hey_bob.isupper()
+ isQuestion = hey_bob.endswith('?')
+ if isShout:
+ if isQuestion:
+ return "Calm down, I know what I'm doing!"
+ else:
+ return 'Whoa, chill out!'
+ if isQuestion:
+ return 'Sure.'
+ return 'Whatever.'
+
+```
+
+In this approach you have a series of `if` statements using the calculated variables to evaluate the conditions, some of which are nested.
+As soon as a `True` condition is found, the correct response is returned.
+
+The [`rstrip`][rstrip] method is applied to the input to eliminate any whitespace at the end of the input.
+If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the response for saying nothing.
+Since it doesn't matter if there is leading whitespace, the `rstrip` function is used instead of [`strip`][strip].
+
+The [`isupper`][isupper] method is used to test that there is at least one cased character and that all cased characters are uppercase.
+
+```exercism/note
+A cased character is one which differs between lowercase and uppercase.
+For example, `?` and `3` are not cased characters, as they do not change between lowercase and uppercase.
+`a` and `z` are cased characters, since their lowercase form changes to `A` and ` Z` when uppercase.
+```
+
+The [`endswith`][endswith] method is used to determine if the input ends with a question mark.
+
+Instead of testing a shout and a question on the same line, this approach first tests if the input is a shout.
+If it is a shout, then the nested `if`/[`else`][else] statement returns if it is a shouted question or just a shout.
+If it is not a shout, then the flow of execution skips down to the non-nested test for if the input is a question.
+
+[rstrip]: https://docs.python.org/3/library/stdtypes.html?highlight=rstrip#str.rstrip
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not]: https://docs.python.org/3/reference/expressions.html#not
+[strip]: https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.strip
+[isupper]: https://docs.python.org/3/library/stdtypes.html?highlight=isupper#str.isupper
+[endswith]: https://docs.python.org/3/library/stdtypes.html?highlight=endswith#str.endswith
+[else]: https://docs.python.org/3/reference/compound_stmts.html#else
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
new file mode 100644
index 00000000000..c86b57e682a
--- /dev/null
+++ b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
@@ -0,0 +1,8 @@
+if isShout:
+ if isQuestion:
+ return "Calm down, I know what I'm doing!"
+ else:
+ return 'Whoa, chill out!'
+if isQuestion:
+ return 'Sure.'
+return 'Whatever.'
diff --git a/exercises/practice/bob/.approaches/if-statements/content.md b/exercises/practice/bob/.approaches/if-statements/content.md
new file mode 100644
index 00000000000..8bae5116496
--- /dev/null
+++ b/exercises/practice/bob/.approaches/if-statements/content.md
@@ -0,0 +1,51 @@
+# `if` statements
+
+```python
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ isShout = hey_bob.isupper()
+ isQuestion = hey_bob.endswith('?')
+ if isShout and isQuestion:
+ return "Calm down, I know what I'm doing!"
+ if isShout:
+ return 'Whoa, chill out!'
+ if isQuestion:
+ return 'Sure.'
+ return 'Whatever.'
+
+```
+
+In this approach you have a series of `if` statements using the calculated variables to evaluate the conditions.
+As soon as a `True` condition is found, the correct response is returned.
+
+```exercism/note
+Note that there are no `elif` or `else` statements.
+If an `if` statement can return, then an `elif` or `else` is not needed.
+Execution will either return or will continue to the next statement anyway.
+```
+
+The [`rstrip`][rstrip] method is applied to the input to eliminate any whitespace at the end of the input.
+If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the response for saying nothing.
+Since it doesn't matter if there is leading whitespace, the `rstrip` function is used instead of [`strip`][strip].
+
+The [`isupper`][isupper] method is used to test that there is at least one cased character and that all cased characters are uppercase.
+
+```exercism/note
+A cased character is one which differs between lowercase and uppercase.
+For example, `?` and `3` are not cased characters, as they do not change between lowercase and uppercase.
+`a` and `z` are cased characters, since their lowercase form changes to `A` and ` Z` when uppercase.
+```
+
+The [`endswith`][endswith] method is used to determine if the input ends with a question mark.
+
+The [logical AND operator][and] (`and`) is used to test a shout and a question together.
+
+[rstrip]: https://docs.python.org/3/library/stdtypes.html?highlight=rstrip#str.rstrip
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not]: https://docs.python.org/3/reference/expressions.html#not
+[strip]: https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.strip
+[isupper]: https://docs.python.org/3/library/stdtypes.html?highlight=isupper#str.isupper
+[endswith]: https://docs.python.org/3/library/stdtypes.html?highlight=endswith#str.endswith
+[and]: https://realpython.com/python-and-operator/
diff --git a/exercises/practice/bob/.approaches/if-statements/snippet.txt b/exercises/practice/bob/.approaches/if-statements/snippet.txt
new file mode 100644
index 00000000000..0e166bed30f
--- /dev/null
+++ b/exercises/practice/bob/.approaches/if-statements/snippet.txt
@@ -0,0 +1,7 @@
+if isShout and isQuestion:
+ return "Calm down, I know what I'm doing!"
+if isShout:
+ return 'Whoa, chill out!'
+if isQuestion:
+ return 'Sure.'
+return 'Whatever.'
diff --git a/exercises/practice/bob/.approaches/introduction.md b/exercises/practice/bob/.approaches/introduction.md
new file mode 100644
index 00000000000..173bb46f659
--- /dev/null
+++ b/exercises/practice/bob/.approaches/introduction.md
@@ -0,0 +1,92 @@
+# Introduction
+
+There are various idiomatic approaches to solve Bob.
+A basic approach can use a series of `if` statements to test the conditions.
+The `if` statements could also be nested.
+A list can contain answers from which the right response is selected by an index calculated from scores given to the conditions.
+
+## General guidance
+
+Regardless of the approach used, some things you could look out for include
+
+- If the input is stripped, [`rstrip`][rstrip] only once.
+
+- Use the [`endswith`][endswith] method instead of checking the last character by index for `?`.
+
+- Don't copy/paste the logic for determining a shout and for determing a question into determing a shouted question.
+ Combine the two determinations instead of copying them.
+ Not duplicating the code will keep the code [DRY][dry].
+
+- Perhaps consider making `IsQuestion` and `IsShout` values set once instead of functions that are possibly called twice.
+
+- If an `if` statement can return, then an `elif` or `else` is not needed.
+ Execution will either return or will continue to the next statement anyway.
+
+## Approach: `if` statements
+
+```python
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ isShout = hey_bob.isupper()
+ isQuestion = hey_bob.endswith('?')
+ if isShout and isQuestion:
+ return "Calm down, I know what I'm doing!"
+ if isShout:
+ return 'Whoa, chill out!'
+ if isQuestion:
+ return 'Sure.'
+ return 'Whatever.'
+
+```
+
+For more information, check the [`if` statements approach][approach-if].
+
+## Approach: `if` statements nested
+
+```python
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ isShout = hey_bob.isupper()
+ isQuestion = hey_bob.endswith('?')
+ if isShout:
+ if isQuestion:
+ return "Calm down, I know what I'm doing!"
+ else:
+ return 'Whoa, chill out!'
+ if isQuestion:
+ return 'Sure.'
+ return 'Whatever.'
+
+```
+
+For more information, check the [`if` statements nested approach][approach-if-nested].
+
+## Other approaches
+
+Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows:
+
+### Other approach: answer list
+
+A list can be defined that contains Bob’s answers, and each condition is given a score.
+The correct answer is selected from the list by using the score as the list index.
+For more information, check the [answer list approach][approach-answer-list].
+
+## Which approach to use?
+
+The nested `if` approach is fastest, but some may consider it a bit less readable than the unnested `if` statements.
+The answer list approach is slowest, but some may prefer doing away with the chain of `if` statements.
+Since the difference between them is a few nanoseconds, which one is used may be a matter of stylistic preference.
+
+- To compare the performance of the approaches, check out the [Performance article][article-performance].
+
+[rstrip]: https://docs.python.org/3/library/stdtypes.html?highlight=rstrip#str.rstrip
+[endswith]: https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.endswith
+[dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
+[approach-if]: https://exercism.org/tracks/python/exercises/bob/approaches/if-statements
+[approach-if-nested]: https://exercism.org/tracks/python/exercises/bob/approaches/if-statements-nested
+[approach-answer-list]: https://exercism.org/tracks/python/exercises/bob/approaches/answer-list
+[article-performance]: https://exercism.org/tracks/python/exercises/bob/articles/performance
diff --git a/exercises/practice/bob/.articles/config.json b/exercises/practice/bob/.articles/config.json
new file mode 100644
index 00000000000..0fa4e15775c
--- /dev/null
+++ b/exercises/practice/bob/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "7b04a95b-d56f-48d3-bb79-3523cfee44ad",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach for Bob's response.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
diff --git a/exercises/practice/bob/.articles/performance/code/Benchmark.py b/exercises/practice/bob/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..b176dc90cc3
--- /dev/null
+++ b/exercises/practice/bob/.articles/performance/code/Benchmark.py
@@ -0,0 +1,62 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""response("I really don't have anything to say.")""",
+ """
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ isShout = hey_bob.isupper()
+ isQuestion = hey_bob.endswith('?')
+ if isShout and isQuestion:
+ return "Calm down, I know what I'm doing!"
+ if isShout:
+ return 'Whoa, chill out!'
+ if isQuestion:
+ return 'Sure.'
+ return 'Whatever.'
+
+""", number=loops) / loops
+
+print(f"if statements: {val}")
+
+
+val = timeit.timeit("""response("I really don't have anything to say.")""",
+ """
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ isShout = hey_bob.isupper()
+ isQuestion = hey_bob.endswith('?')
+ if isShout:
+ if isQuestion:
+ return "Calm down, I know what I'm doing!"
+ else:
+ return 'Whoa, chill out!'
+ if isQuestion:
+ return 'Sure.'
+ return 'Whatever.'
+
+""", number=loops) / loops
+
+print(f"if statements nested: {val}")
+
+val = timeit.timeit("""response("I really don't have anything to say.")""",
+ """
+
+_ANSWERS = ["Whatever.", "Sure.", "Whoa, chill out!", "Calm down, I know what I'm doing!"]
+
+def response(hey_bob):
+ hey_bob = hey_bob.rstrip()
+ if not hey_bob: return 'Fine. Be that way!'
+ isShout = 2 if hey_bob.isupper() else 0
+ isQuestion = 1 if hey_bob.endswith('?') else 0
+ return _ANSWERS[isShout + isQuestion]
+
+
+""", number=loops) / loops
+
+print(f"answer list: {val}")
diff --git a/exercises/practice/bob/.articles/performance/content.md b/exercises/practice/bob/.articles/performance/content.md
new file mode 100644
index 00000000000..c90c3688f60
--- /dev/null
+++ b/exercises/practice/bob/.articles/performance/content.md
@@ -0,0 +1,31 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently determine the response for Bob in Python.
+
+The [approaches page][approaches] lists two idiomatic approaches to this exercise:
+
+1. [Using `if` statements][approach-if].
+2. [Using `if` statements nested][approach-if-nested].
+
+For our performance investigation, we'll also include a third approach that [uses an answer list][approach-answer-list].
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+if statements: 2.4691750000056346e-07
+if statements nested: 2.3319310000078987e-07
+answer list: 2.5469370000064373e-07
+```
+
+The nested `if` approach is fastest, but some may consider nested `if` statements a bit less readable than the unnested `if` statements.
+The answer list approach is slowest, but some may prefer doing away with the chain of `if` statements.
+Since the difference between them is a few nanoseconds, which one is used may be a matter of stylistic preference.
+
+[approaches]: https://exercism.org/tracks/python/exercises/bob/approaches
+[approach-if]: https://exercism.org/tracks/python/exercises/bob/approaches/if-statements
+[approach-if-nested]: https://exercism.org/tracks/python/exercises/bob/approaches/if-statements-nested
+[approach-answer-list]: https://exercism.org/tracks/python/exercises/bob/approaches/answer-list
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/bob/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
diff --git a/exercises/practice/bob/.articles/performance/snippet.md b/exercises/practice/bob/.articles/performance/snippet.md
new file mode 100644
index 00000000000..4735892eacd
--- /dev/null
+++ b/exercises/practice/bob/.articles/performance/snippet.md
@@ -0,0 +1,5 @@
+```
+if statements: 2.4691750000056346e-07
+if statements nested: 2.3319310000078987e-07
+answer array: 2.5469370000064373e-07
+```
From 1ce3e3b1a4711945db0feed5d0daf77fae3d47b2 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Wed, 16 Nov 2022 17:29:29 +0100
Subject: [PATCH 099/932] Sync exercise metadata with problem-specifications
This syncs all the metadata for all the practice exercises with
upstream changes in problem-specifications, in one fell swoop.
The changes are all nitpicky changes made in order to ensure that
source urls all use https rather than http, along with some
tweaks to better describe the source of certain exercises.
---
exercises/practice/affine-cipher/.meta/config.json | 2 +-
exercises/practice/allergies/.meta/config.json | 4 ++--
exercises/practice/atbash-cipher/.meta/config.json | 2 +-
exercises/practice/beer-song/.meta/config.json | 2 +-
exercises/practice/binary-search/.meta/config.json | 2 +-
exercises/practice/bob/.meta/config.json | 2 +-
exercises/practice/book-store/.meta/config.json | 2 +-
exercises/practice/bowling/.meta/config.json | 2 +-
exercises/practice/circular-buffer/.meta/config.json | 2 +-
exercises/practice/crypto-square/.meta/config.json | 2 +-
exercises/practice/diamond/.meta/config.json | 2 +-
exercises/practice/difference-of-squares/.meta/config.json | 2 +-
exercises/practice/diffie-hellman/.meta/config.json | 2 +-
exercises/practice/etl/.meta/config.json | 4 ++--
exercises/practice/food-chain/.meta/config.json | 2 +-
exercises/practice/gigasecond/.meta/config.json | 2 +-
exercises/practice/grains/.meta/config.json | 4 ++--
exercises/practice/grep/.meta/config.json | 2 +-
exercises/practice/hamming/.meta/config.json | 2 +-
exercises/practice/hello-world/.meta/config.json | 2 +-
exercises/practice/house/.meta/config.json | 2 +-
exercises/practice/kindergarten-garden/.meta/config.json | 4 ++--
exercises/practice/largest-series-product/.meta/config.json | 2 +-
exercises/practice/leap/.meta/config.json | 4 ++--
exercises/practice/luhn/.meta/config.json | 2 +-
exercises/practice/matrix/.meta/config.json | 4 ++--
exercises/practice/nth-prime/.meta/config.json | 2 +-
exercises/practice/ocr-numbers/.meta/config.json | 2 +-
exercises/practice/palindrome-products/.meta/config.json | 2 +-
exercises/practice/perfect-numbers/.meta/config.json | 2 +-
exercises/practice/phone-number/.meta/config.json | 4 ++--
exercises/practice/prime-factors/.meta/config.json | 2 +-
exercises/practice/pythagorean-triplet/.meta/config.json | 2 +-
exercises/practice/queen-attack/.meta/config.json | 2 +-
exercises/practice/rna-transcription/.meta/config.json | 2 +-
exercises/practice/roman-numerals/.meta/config.json | 2 +-
exercises/practice/saddle-points/.meta/config.json | 2 +-
exercises/practice/say/.meta/config.json | 4 ++--
exercises/practice/secret-handshake/.meta/config.json | 2 +-
exercises/practice/series/.meta/config.json | 2 +-
exercises/practice/sieve/.meta/config.json | 2 +-
exercises/practice/simple-cipher/.meta/config.json | 2 +-
exercises/practice/space-age/.meta/config.json | 2 +-
exercises/practice/sum-of-multiples/.meta/config.json | 2 +-
exercises/practice/triangle/.meta/config.json | 2 +-
exercises/practice/twelve-days/.meta/config.json | 2 +-
exercises/practice/two-bucket/.meta/config.json | 2 +-
47 files changed, 55 insertions(+), 55 deletions(-)
diff --git a/exercises/practice/affine-cipher/.meta/config.json b/exercises/practice/affine-cipher/.meta/config.json
index 6b813c41c91..fb6606be5d4 100644
--- a/exercises/practice/affine-cipher/.meta/config.json
+++ b/exercises/practice/affine-cipher/.meta/config.json
@@ -21,5 +21,5 @@
]
},
"source": "Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/Affine_cipher"
+ "source_url": "https://en.wikipedia.org/wiki/Affine_cipher"
}
diff --git a/exercises/practice/allergies/.meta/config.json b/exercises/practice/allergies/.meta/config.json
index 7158e9f7aab..d38070cec6a 100644
--- a/exercises/practice/allergies/.meta/config.json
+++ b/exercises/practice/allergies/.meta/config.json
@@ -30,6 +30,6 @@
".meta/example.py"
]
},
- "source": "Jumpstart Lab Warm-up",
- "source_url": "http://jumpstartlab.com"
+ "source": "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/atbash-cipher/.meta/config.json b/exercises/practice/atbash-cipher/.meta/config.json
index d52c99e2b8a..a1c24673613 100644
--- a/exercises/practice/atbash-cipher/.meta/config.json
+++ b/exercises/practice/atbash-cipher/.meta/config.json
@@ -29,5 +29,5 @@
]
},
"source": "Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/Atbash"
+ "source_url": "https://en.wikipedia.org/wiki/Atbash"
}
diff --git a/exercises/practice/beer-song/.meta/config.json b/exercises/practice/beer-song/.meta/config.json
index fee3a8ff5a1..a59b25cd4a5 100644
--- a/exercises/practice/beer-song/.meta/config.json
+++ b/exercises/practice/beer-song/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "Learn to Program by Chris Pine",
- "source_url": "http://pine.fm/LearnToProgram/?Chapter=06"
+ "source_url": "https://pine.fm/LearnToProgram/?Chapter=06"
}
diff --git a/exercises/practice/binary-search/.meta/config.json b/exercises/practice/binary-search/.meta/config.json
index 43b9f0b1346..cc3fb2823f6 100644
--- a/exercises/practice/binary-search/.meta/config.json
+++ b/exercises/practice/binary-search/.meta/config.json
@@ -31,5 +31,5 @@
]
},
"source": "Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/Binary_search_algorithm"
+ "source_url": "https://en.wikipedia.org/wiki/Binary_search_algorithm"
}
diff --git a/exercises/practice/bob/.meta/config.json b/exercises/practice/bob/.meta/config.json
index 2ff720e1e16..1c91b437606 100644
--- a/exercises/practice/bob/.meta/config.json
+++ b/exercises/practice/bob/.meta/config.json
@@ -43,5 +43,5 @@
]
},
"source": "Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial.",
- "source_url": "http://pine.fm/LearnToProgram/?Chapter=06"
+ "source_url": "https://pine.fm/LearnToProgram/?Chapter=06"
}
diff --git a/exercises/practice/book-store/.meta/config.json b/exercises/practice/book-store/.meta/config.json
index da8d70010d4..ed1c0f5556e 100644
--- a/exercises/practice/book-store/.meta/config.json
+++ b/exercises/practice/book-store/.meta/config.json
@@ -28,5 +28,5 @@
]
},
"source": "Inspired by the harry potter kata from Cyber-Dojo.",
- "source_url": "http://cyber-dojo.org"
+ "source_url": "https://cyber-dojo.org"
}
diff --git a/exercises/practice/bowling/.meta/config.json b/exercises/practice/bowling/.meta/config.json
index b52768b1d1d..475fd3bf71b 100644
--- a/exercises/practice/bowling/.meta/config.json
+++ b/exercises/practice/bowling/.meta/config.json
@@ -23,5 +23,5 @@
]
},
"source": "The Bowling Game Kata from UncleBob",
- "source_url": "http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata"
+ "source_url": "https://web.archive.org/web/20221001111000/http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata"
}
diff --git a/exercises/practice/circular-buffer/.meta/config.json b/exercises/practice/circular-buffer/.meta/config.json
index f94e3c08645..ba40c18f705 100644
--- a/exercises/practice/circular-buffer/.meta/config.json
+++ b/exercises/practice/circular-buffer/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/Circular_buffer"
+ "source_url": "https://en.wikipedia.org/wiki/Circular_buffer"
}
diff --git a/exercises/practice/crypto-square/.meta/config.json b/exercises/practice/crypto-square/.meta/config.json
index 69b29a4745f..c8e34bd0516 100644
--- a/exercises/practice/crypto-square/.meta/config.json
+++ b/exercises/practice/crypto-square/.meta/config.json
@@ -30,5 +30,5 @@
]
},
"source": "J Dalbey's Programming Practice problems",
- "source_url": "http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
+ "source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
diff --git a/exercises/practice/diamond/.meta/config.json b/exercises/practice/diamond/.meta/config.json
index 3e68c65ffd5..0108dfaa9bc 100644
--- a/exercises/practice/diamond/.meta/config.json
+++ b/exercises/practice/diamond/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "Seb Rose",
- "source_url": "http://claysnow.co.uk/recycling-tests-in-tdd/"
+ "source_url": "https://web.archive.org/web/20220807163751/http://claysnow.co.uk/recycling-tests-in-tdd/"
}
diff --git a/exercises/practice/difference-of-squares/.meta/config.json b/exercises/practice/difference-of-squares/.meta/config.json
index a0cfedf099f..3dc17de6735 100644
--- a/exercises/practice/difference-of-squares/.meta/config.json
+++ b/exercises/practice/difference-of-squares/.meta/config.json
@@ -28,5 +28,5 @@
]
},
"source": "Problem 6 at Project Euler",
- "source_url": "http://projecteuler.net/problem=6"
+ "source_url": "https://projecteuler.net/problem=6"
}
diff --git a/exercises/practice/diffie-hellman/.meta/config.json b/exercises/practice/diffie-hellman/.meta/config.json
index 5ae726f56fe..feb9623d4ec 100644
--- a/exercises/practice/diffie-hellman/.meta/config.json
+++ b/exercises/practice/diffie-hellman/.meta/config.json
@@ -21,5 +21,5 @@
]
},
"source": "Wikipedia, 1024 bit key from www.cryptopp.com/wiki.",
- "source_url": "http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange"
+ "source_url": "https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange"
}
diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json
index 28aa9acb8ee..777e51cb845 100644
--- a/exercises/practice/etl/.meta/config.json
+++ b/exercises/practice/etl/.meta/config.json
@@ -28,6 +28,6 @@
".meta/example.py"
]
},
- "source": "The Jumpstart Lab team",
- "source_url": "http://jumpstartlab.com"
+ "source": "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/food-chain/.meta/config.json b/exercises/practice/food-chain/.meta/config.json
index 1c71a17e290..1f359e9b0e2 100644
--- a/exercises/practice/food-chain/.meta/config.json
+++ b/exercises/practice/food-chain/.meta/config.json
@@ -22,5 +22,5 @@
]
},
"source": "Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/There_Was_an_Old_Lady_Who_Swallowed_a_Fly"
+ "source_url": "https://en.wikipedia.org/wiki/There_Was_an_Old_Lady_Who_Swallowed_a_Fly"
}
diff --git a/exercises/practice/gigasecond/.meta/config.json b/exercises/practice/gigasecond/.meta/config.json
index 5ef0e8680fb..b0af657f37f 100644
--- a/exercises/practice/gigasecond/.meta/config.json
+++ b/exercises/practice/gigasecond/.meta/config.json
@@ -30,5 +30,5 @@
]
},
"source": "Chapter 9 in Chris Pine's online Learn to Program tutorial.",
- "source_url": "http://pine.fm/LearnToProgram/?Chapter=09"
+ "source_url": "https://pine.fm/LearnToProgram/?Chapter=09"
}
diff --git a/exercises/practice/grains/.meta/config.json b/exercises/practice/grains/.meta/config.json
index 55ec49f0b79..12b2a81dcad 100644
--- a/exercises/practice/grains/.meta/config.json
+++ b/exercises/practice/grains/.meta/config.json
@@ -29,6 +29,6 @@
".meta/example.py"
]
},
- "source": "JavaRanch Cattle Drive, exercise 6",
- "source_url": "http://www.javaranch.com/grains.jsp"
+ "source": "The CodeRanch Cattle Drive, Assignment 6",
+ "source_url": "https://coderanch.com/wiki/718824/Grains"
}
diff --git a/exercises/practice/grep/.meta/config.json b/exercises/practice/grep/.meta/config.json
index 082620a8485..57c0ec8e079 100644
--- a/exercises/practice/grep/.meta/config.json
+++ b/exercises/practice/grep/.meta/config.json
@@ -27,5 +27,5 @@
]
},
"source": "Conversation with Nate Foster.",
- "source_url": "http://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf"
+ "source_url": "https://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf"
}
diff --git a/exercises/practice/hamming/.meta/config.json b/exercises/practice/hamming/.meta/config.json
index 13e6ee6fc24..34b97637ba0 100644
--- a/exercises/practice/hamming/.meta/config.json
+++ b/exercises/practice/hamming/.meta/config.json
@@ -33,5 +33,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/hello-world/.meta/config.json b/exercises/practice/hello-world/.meta/config.json
index e2bdf55bc63..c3520cfdeea 100644
--- a/exercises/practice/hello-world/.meta/config.json
+++ b/exercises/practice/hello-world/.meta/config.json
@@ -29,5 +29,5 @@
]
},
"source": "This is an exercise to introduce users to using Exercism",
- "source_url": "http://en.wikipedia.org/wiki/%22Hello,_world!%22_program"
+ "source_url": "https://en.wikipedia.org/wiki/%22Hello,_world!%22_program"
}
diff --git a/exercises/practice/house/.meta/config.json b/exercises/practice/house/.meta/config.json
index e18b4ea5851..7de79da60f8 100644
--- a/exercises/practice/house/.meta/config.json
+++ b/exercises/practice/house/.meta/config.json
@@ -27,5 +27,5 @@
]
},
"source": "British nursery rhyme",
- "source_url": "http://en.wikipedia.org/wiki/This_Is_The_House_That_Jack_Built"
+ "source_url": "https://en.wikipedia.org/wiki/This_Is_The_House_That_Jack_Built"
}
diff --git a/exercises/practice/kindergarten-garden/.meta/config.json b/exercises/practice/kindergarten-garden/.meta/config.json
index f10fda1c5c9..396235f79d9 100644
--- a/exercises/practice/kindergarten-garden/.meta/config.json
+++ b/exercises/practice/kindergarten-garden/.meta/config.json
@@ -27,6 +27,6 @@
".meta/example.py"
]
},
- "source": "Random musings during airplane trip.",
- "source_url": "http://jumpstartlab.com"
+ "source": "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/largest-series-product/.meta/config.json b/exercises/practice/largest-series-product/.meta/config.json
index b49cdb4cc05..8b12e5b55cc 100644
--- a/exercises/practice/largest-series-product/.meta/config.json
+++ b/exercises/practice/largest-series-product/.meta/config.json
@@ -29,5 +29,5 @@
]
},
"source": "A variation on Problem 8 at Project Euler",
- "source_url": "http://projecteuler.net/problem=8"
+ "source_url": "https://projecteuler.net/problem=8"
}
diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json
index b1f9cbff298..687638ae0b4 100644
--- a/exercises/practice/leap/.meta/config.json
+++ b/exercises/practice/leap/.meta/config.json
@@ -33,6 +33,6 @@
".meta/example.py"
]
},
- "source": "JavaRanch Cattle Drive, exercise 3",
- "source_url": "http://www.javaranch.com/leap.jsp"
+ "source": "CodeRanch Cattle Drive, Assignment 3",
+ "source_url": "https://coderanch.com/t/718816/Leap"
}
diff --git a/exercises/practice/luhn/.meta/config.json b/exercises/practice/luhn/.meta/config.json
index 24aa17c97da..ead518b8e1f 100644
--- a/exercises/practice/luhn/.meta/config.json
+++ b/exercises/practice/luhn/.meta/config.json
@@ -35,5 +35,5 @@
]
},
"source": "The Luhn Algorithm on Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/Luhn_algorithm"
+ "source_url": "https://en.wikipedia.org/wiki/Luhn_algorithm"
}
diff --git a/exercises/practice/matrix/.meta/config.json b/exercises/practice/matrix/.meta/config.json
index 63c7af00490..5b9aaf85ba6 100644
--- a/exercises/practice/matrix/.meta/config.json
+++ b/exercises/practice/matrix/.meta/config.json
@@ -28,6 +28,6 @@
".meta/example.py"
]
},
- "source": "Warmup to the `saddle-points` warmup.",
- "source_url": "http://jumpstartlab.com"
+ "source": "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/nth-prime/.meta/config.json b/exercises/practice/nth-prime/.meta/config.json
index 46b004d8b6a..c38d07e93a4 100644
--- a/exercises/practice/nth-prime/.meta/config.json
+++ b/exercises/practice/nth-prime/.meta/config.json
@@ -30,5 +30,5 @@
]
},
"source": "A variation on Problem 7 at Project Euler",
- "source_url": "http://projecteuler.net/problem=7"
+ "source_url": "https://projecteuler.net/problem=7"
}
diff --git a/exercises/practice/ocr-numbers/.meta/config.json b/exercises/practice/ocr-numbers/.meta/config.json
index 52e7f234791..669baa24a04 100644
--- a/exercises/practice/ocr-numbers/.meta/config.json
+++ b/exercises/practice/ocr-numbers/.meta/config.json
@@ -29,5 +29,5 @@
]
},
"source": "Inspired by the Bank OCR kata",
- "source_url": "http://codingdojo.org/cgi-bin/wiki.pl?KataBankOCR"
+ "source_url": "https://codingdojo.org/kata/BankOCR/"
}
diff --git a/exercises/practice/palindrome-products/.meta/config.json b/exercises/practice/palindrome-products/.meta/config.json
index d05e4708b6b..936f9e8b92a 100644
--- a/exercises/practice/palindrome-products/.meta/config.json
+++ b/exercises/practice/palindrome-products/.meta/config.json
@@ -31,5 +31,5 @@
]
},
"source": "Problem 4 at Project Euler",
- "source_url": "http://projecteuler.net/problem=4"
+ "source_url": "https://projecteuler.net/problem=4"
}
diff --git a/exercises/practice/perfect-numbers/.meta/config.json b/exercises/practice/perfect-numbers/.meta/config.json
index 1e5021db2dd..208b5094ecf 100644
--- a/exercises/practice/perfect-numbers/.meta/config.json
+++ b/exercises/practice/perfect-numbers/.meta/config.json
@@ -27,5 +27,5 @@
]
},
"source": "Taken from Chapter 2 of Functional Thinking by Neal Ford.",
- "source_url": "http://shop.oreilly.com/product/0636920029687.do"
+ "source_url": "https://www.oreilly.com/library/view/functional-thinking/9781449365509/"
}
diff --git a/exercises/practice/phone-number/.meta/config.json b/exercises/practice/phone-number/.meta/config.json
index f05c4f8978e..db2af4d694e 100644
--- a/exercises/practice/phone-number/.meta/config.json
+++ b/exercises/practice/phone-number/.meta/config.json
@@ -29,6 +29,6 @@
".meta/example.py"
]
},
- "source": "Event Manager by JumpstartLab",
- "source_url": "http://tutorials.jumpstartlab.com/projects/eventmanager.html"
+ "source": "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/prime-factors/.meta/config.json b/exercises/practice/prime-factors/.meta/config.json
index bb2f524650f..1d89a38d6b7 100644
--- a/exercises/practice/prime-factors/.meta/config.json
+++ b/exercises/practice/prime-factors/.meta/config.json
@@ -28,5 +28,5 @@
]
},
"source": "The Prime Factors Kata by Uncle Bob",
- "source_url": "http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata"
+ "source_url": "https://web.archive.org/web/20221026171801/http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata"
}
diff --git a/exercises/practice/pythagorean-triplet/.meta/config.json b/exercises/practice/pythagorean-triplet/.meta/config.json
index 08a7aa50b83..209e162326b 100644
--- a/exercises/practice/pythagorean-triplet/.meta/config.json
+++ b/exercises/practice/pythagorean-triplet/.meta/config.json
@@ -33,5 +33,5 @@
]
},
"source": "Problem 9 at Project Euler",
- "source_url": "http://projecteuler.net/problem=9"
+ "source_url": "https://projecteuler.net/problem=9"
}
diff --git a/exercises/practice/queen-attack/.meta/config.json b/exercises/practice/queen-attack/.meta/config.json
index c906de479d3..a4558a4e5a6 100644
--- a/exercises/practice/queen-attack/.meta/config.json
+++ b/exercises/practice/queen-attack/.meta/config.json
@@ -33,5 +33,5 @@
]
},
"source": "J Dalbey's Programming Practice problems",
- "source_url": "http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
+ "source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
diff --git a/exercises/practice/rna-transcription/.meta/config.json b/exercises/practice/rna-transcription/.meta/config.json
index 5f26e6c0b18..b310b8c0361 100644
--- a/exercises/practice/rna-transcription/.meta/config.json
+++ b/exercises/practice/rna-transcription/.meta/config.json
@@ -34,5 +34,5 @@
]
},
"source": "Hyperphysics",
- "source_url": "http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html"
+ "source_url": "https://web.archive.org/web/20220408112140/http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html"
}
diff --git a/exercises/practice/roman-numerals/.meta/config.json b/exercises/practice/roman-numerals/.meta/config.json
index 108aa295760..ea6611e8fb9 100644
--- a/exercises/practice/roman-numerals/.meta/config.json
+++ b/exercises/practice/roman-numerals/.meta/config.json
@@ -28,5 +28,5 @@
]
},
"source": "The Roman Numeral Kata",
- "source_url": "http://codingdojo.org/cgi-bin/index.pl?KataRomanNumerals"
+ "source_url": "https://codingdojo.org/kata/RomanNumerals/"
}
diff --git a/exercises/practice/saddle-points/.meta/config.json b/exercises/practice/saddle-points/.meta/config.json
index 81ef0e53f0c..7deb5f4aaf0 100644
--- a/exercises/practice/saddle-points/.meta/config.json
+++ b/exercises/practice/saddle-points/.meta/config.json
@@ -36,5 +36,5 @@
]
},
"source": "J Dalbey's Programming Practice problems",
- "source_url": "http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
+ "source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
diff --git a/exercises/practice/say/.meta/config.json b/exercises/practice/say/.meta/config.json
index 7a95c43e9c6..506533a1138 100644
--- a/exercises/practice/say/.meta/config.json
+++ b/exercises/practice/say/.meta/config.json
@@ -29,6 +29,6 @@
".meta/example.py"
]
},
- "source": "A variation on JavaRanch CattleDrive, exercise 4a",
- "source_url": "http://www.javaranch.com/say.jsp"
+ "source": "A variation on the JavaRanch CattleDrive, Assignment 4",
+ "source_url": "https://coderanch.com/wiki/718804"
}
diff --git a/exercises/practice/secret-handshake/.meta/config.json b/exercises/practice/secret-handshake/.meta/config.json
index 1f9dc801d9b..ee9aeb52a13 100644
--- a/exercises/practice/secret-handshake/.meta/config.json
+++ b/exercises/practice/secret-handshake/.meta/config.json
@@ -31,5 +31,5 @@
]
},
"source": "Bert, in Mary Poppins",
- "source_url": "http://www.imdb.com/title/tt0058331/quotes/qt0437047"
+ "source_url": "https://www.imdb.com/title/tt0058331/quotes/qt0437047"
}
diff --git a/exercises/practice/series/.meta/config.json b/exercises/practice/series/.meta/config.json
index afa21de3ac8..9953afd602c 100644
--- a/exercises/practice/series/.meta/config.json
+++ b/exercises/practice/series/.meta/config.json
@@ -28,5 +28,5 @@
]
},
"source": "A subset of the Problem 8 at Project Euler",
- "source_url": "http://projecteuler.net/problem=8"
+ "source_url": "https://projecteuler.net/problem=8"
}
diff --git a/exercises/practice/sieve/.meta/config.json b/exercises/practice/sieve/.meta/config.json
index 52af39b8d3d..ccf94249879 100644
--- a/exercises/practice/sieve/.meta/config.json
+++ b/exercises/practice/sieve/.meta/config.json
@@ -30,5 +30,5 @@
]
},
"source": "Sieve of Eratosthenes at Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"
+ "source_url": "https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"
}
diff --git a/exercises/practice/simple-cipher/.meta/config.json b/exercises/practice/simple-cipher/.meta/config.json
index fb95445dbbb..2176f407c5d 100644
--- a/exercises/practice/simple-cipher/.meta/config.json
+++ b/exercises/practice/simple-cipher/.meta/config.json
@@ -32,5 +32,5 @@
]
},
"source": "Substitution Cipher at Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/Substitution_cipher"
+ "source_url": "https://en.wikipedia.org/wiki/Substitution_cipher"
}
diff --git a/exercises/practice/space-age/.meta/config.json b/exercises/practice/space-age/.meta/config.json
index 23b8e4f59b6..738e0fe39e1 100644
--- a/exercises/practice/space-age/.meta/config.json
+++ b/exercises/practice/space-age/.meta/config.json
@@ -29,5 +29,5 @@
]
},
"source": "Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial.",
- "source_url": "http://pine.fm/LearnToProgram/?Chapter=01"
+ "source_url": "https://pine.fm/LearnToProgram/?Chapter=01"
}
diff --git a/exercises/practice/sum-of-multiples/.meta/config.json b/exercises/practice/sum-of-multiples/.meta/config.json
index 86acf2c0b6f..bc2eba2a1cf 100644
--- a/exercises/practice/sum-of-multiples/.meta/config.json
+++ b/exercises/practice/sum-of-multiples/.meta/config.json
@@ -32,5 +32,5 @@
]
},
"source": "A variation on Problem 1 at Project Euler",
- "source_url": "http://projecteuler.net/problem=1"
+ "source_url": "https://projecteuler.net/problem=1"
}
diff --git a/exercises/practice/triangle/.meta/config.json b/exercises/practice/triangle/.meta/config.json
index 87bcc3d9bac..d5264660fe3 100644
--- a/exercises/practice/triangle/.meta/config.json
+++ b/exercises/practice/triangle/.meta/config.json
@@ -32,5 +32,5 @@
]
},
"source": "The Ruby Koans triangle project, parts 1 & 2",
- "source_url": "http://rubykoans.com"
+ "source_url": "https://web.archive.org/web/20220831105330/http://rubykoans.com"
}
diff --git a/exercises/practice/twelve-days/.meta/config.json b/exercises/practice/twelve-days/.meta/config.json
index d7c84712487..2ed3689f68e 100644
--- a/exercises/practice/twelve-days/.meta/config.json
+++ b/exercises/practice/twelve-days/.meta/config.json
@@ -27,5 +27,5 @@
]
},
"source": "Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/The_Twelve_Days_of_Christmas_(song)"
+ "source_url": "https://en.wikipedia.org/wiki/The_Twelve_Days_of_Christmas_(song)"
}
diff --git a/exercises/practice/two-bucket/.meta/config.json b/exercises/practice/two-bucket/.meta/config.json
index c70e317cb05..177aedc4e45 100644
--- a/exercises/practice/two-bucket/.meta/config.json
+++ b/exercises/practice/two-bucket/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "Water Pouring Problem",
- "source_url": "http://demonstrations.wolfram.com/WaterPouringProblem/"
+ "source_url": "https://demonstrations.wolfram.com/WaterPouringProblem/"
}
From a91777d7d860cd8c356780bba5aaab33b20aab3a Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 04:05:24 -0600
Subject: [PATCH 100/932] Bob approaches fixes (#3201)
Final isShout and isQuestion remnants changed to is_shout and is_question.
* Update introduction.md
* Update snippet.txt
* Update content.md
* Update snippet.txt
* Update content.md
* Update snippet.txt
* Update content.md
* Update content.md
* Update Benchmark.py
* Update content.md
---
.../bob/.approaches/answer-list/content.md | 37 ++++++++++---------
.../bob/.approaches/answer-list/snippet.txt | 8 ++--
.../if-statements-nested/content.md | 10 ++---
.../if-statements-nested/snippet.txt | 6 +--
.../bob/.approaches/if-statements/content.md | 10 ++---
.../bob/.approaches/if-statements/snippet.txt | 6 +--
.../practice/bob/.approaches/introduction.md | 22 +++++------
.../.articles/performance/code/Benchmark.py | 35 ++++++++++--------
8 files changed, 70 insertions(+), 64 deletions(-)
diff --git a/exercises/practice/bob/.approaches/answer-list/content.md b/exercises/practice/bob/.approaches/answer-list/content.md
index de2049a7adb..b3894780e59 100644
--- a/exercises/practice/bob/.approaches/answer-list/content.md
+++ b/exercises/practice/bob/.approaches/answer-list/content.md
@@ -1,7 +1,7 @@
# Answer list
```python
-_ANSWERS = ['Whatever.', 'Sure.', 'Whoa, chill out!',
+ANSWERS = ['Whatever.', 'Sure.', 'Whoa, chill out!',
"Calm down, I know what I'm doing!"]
@@ -9,9 +9,9 @@ def response(hey_bob):
hey_bob = hey_bob.rstrip()
if not hey_bob:
return 'Fine. Be that way!'
- isShout = (2 if hey_bob.isupper() else 0)
- isQuestion = (1 if hey_bob.endswith('?') else 0)
- return _ANSWERS[isShout + isQuestion]
+ is_shout = 2 if hey_bob.isupper() else 0
+ is_question = 1 if hey_bob.endswith('?') else 0
+ return ANSWERS[is_shout + is_question]
```
@@ -19,11 +19,15 @@ In this approach you define a [list][list] that contains Bob’s answers, and ea
The correct answer is selected from the list by using the score as the list index.
Python doesn't _enforce_ having real constant values,
-but the `_ANSWERS` list is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+but the `ANSWERS` list is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
It indicates that the value is not intended to be changed.
-Python also does not have real [private][private] variables,
-but a leading underscore is the naming convention for indicating that a variable is not meant to be part of the public API.
-It should be considered an implementation detail and subject to change without notice.
+
+```exercism/note
+`ANSWERS` could prevent item reassignment by being defined as a [tuple](https://realpython.com/python-lists-tuples/#python-tuples) instead of a list.
+The items in a tuple cannot be changed, and the performance between a tuple and a list here is equivalent.
+The entire `ANSWERS` tuple could still be reassigned to another tuple,
+so uppercase letters would still be used to indicate that the `ANSWERS` tuple should not be changed.
+```
The [`rstrip`][rstrip] method is applied to the input to eliminate any whitespace at the end of the input.
If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the response for saying nothing.
@@ -39,25 +43,24 @@ For example, `?` and `3` are not cased characters, as they do not change between
`a` and `z` are cased characters, since their lowercase form changes to `A` and ` Z` when uppercase.
```
-If `isupper` is `True`, then `isShout` is given the value of `2`; otherwise, it is given the value of `0`.
+If `isupper` is `True`, then `is_shout` is given the value of `2`; otherwise, it is given the value of `0`.
The [`endswith`][endswith] method is used to determine if the input ends with a question mark.
-If the test for `endswith('?')` is `True`, then `isQuestion` is given the value of `1`; otherwise it is given the value of `0`.
+If the test for `endswith('?')` is `True`, then `is_question` is given the value of `1`; otherwise it is given the value of `0`.
The response is selected from the list by the index like so
-| isShout | isQuestion | Index | Answer |
-| ------- | ---------- | --------- | ------------------------------------- |
-| `false` | `false` | 0 + 0 = 0 | `"Whatever."` |
-| `false` | `true` | 0 + 1 = 1 | `"Sure."` |
-| `true` | `false` | 2 + 0 = 2 | `"Whoa, chill out!"` |
-| `true` | `true` | 2 + 1 = 3 | `"Calm down, I know what I'm doing!"` |
+| is_shout | is_question | Index | Answer |
+| -------- | ----------- | --------- | ------------------------------------- |
+| `false` | `false` | 0 + 0 = 0 | `"Whatever."` |
+| `false` | `true` | 0 + 1 = 1 | `"Sure."` |
+| `true` | `false` | 2 + 0 = 2 | `"Whoa, chill out!"` |
+| `true` | `true` | 2 + 1 = 3 | `"Calm down, I know what I'm doing!"` |
[list]: https://docs.python.org/3/library/stdtypes.html?highlight=list#list
[const]: https://realpython.com/python-constants/
-[private]: https://docs.python.org/3/tutorial/classes.html#private-variables
[rstrip]: https://docs.python.org/3/library/stdtypes.html?highlight=rstrip#str.rstrip
[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
[not]: https://docs.python.org/3/reference/expressions.html#not
diff --git a/exercises/practice/bob/.approaches/answer-list/snippet.txt b/exercises/practice/bob/.approaches/answer-list/snippet.txt
index 0d834fa2718..b1ce322f727 100644
--- a/exercises/practice/bob/.approaches/answer-list/snippet.txt
+++ b/exercises/practice/bob/.approaches/answer-list/snippet.txt
@@ -1,6 +1,6 @@
-_ANSWERS = ['Whatever.', 'Sure.', 'Whoa, chill out!',
+ANSWERS = ['Whatever.', 'Sure.', 'Whoa, chill out!',
"Calm down, I know what I'm doing!"]
# code snipped
-isShout = (2 if hey_bob.isupper() else 0)
-isQuestion = (1 if hey_bob.endswith('?') else 0)
-return _ANSWERS[isShout + isQuestion]
+is_shout = 2 if hey_bob.isupper() else 0
+is_question = 1 if hey_bob.endswith('?') else 0
+return ANSWERS[is_shout + is_question]
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/content.md b/exercises/practice/bob/.approaches/if-statements-nested/content.md
index e0702169863..06b5ed527cb 100644
--- a/exercises/practice/bob/.approaches/if-statements-nested/content.md
+++ b/exercises/practice/bob/.approaches/if-statements-nested/content.md
@@ -5,14 +5,14 @@ def response(hey_bob):
hey_bob = hey_bob.rstrip()
if not hey_bob:
return 'Fine. Be that way!'
- isShout = hey_bob.isupper()
- isQuestion = hey_bob.endswith('?')
- if isShout:
- if isQuestion:
+ is_shout = hey_bob.isupper()
+ is_question = hey_bob.endswith('?')
+ if is_shout:
+ if is_question:
return "Calm down, I know what I'm doing!"
else:
return 'Whoa, chill out!'
- if isQuestion:
+ if is_question:
return 'Sure.'
return 'Whatever.'
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
index c86b57e682a..0df6c2fe9e1 100644
--- a/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
+++ b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
@@ -1,8 +1,8 @@
-if isShout:
- if isQuestion:
+if is_shout:
+ if is_question:
return "Calm down, I know what I'm doing!"
else:
return 'Whoa, chill out!'
-if isQuestion:
+if is_question:
return 'Sure.'
return 'Whatever.'
diff --git a/exercises/practice/bob/.approaches/if-statements/content.md b/exercises/practice/bob/.approaches/if-statements/content.md
index 8bae5116496..7d4d84e3689 100644
--- a/exercises/practice/bob/.approaches/if-statements/content.md
+++ b/exercises/practice/bob/.approaches/if-statements/content.md
@@ -5,13 +5,13 @@ def response(hey_bob):
hey_bob = hey_bob.rstrip()
if not hey_bob:
return 'Fine. Be that way!'
- isShout = hey_bob.isupper()
- isQuestion = hey_bob.endswith('?')
- if isShout and isQuestion:
+ is_shout = hey_bob.isupper()
+ is_question = hey_bob.endswith('?')
+ if is_shout and is_question:
return "Calm down, I know what I'm doing!"
- if isShout:
+ if is_shout:
return 'Whoa, chill out!'
- if isQuestion:
+ if is_question:
return 'Sure.'
return 'Whatever.'
diff --git a/exercises/practice/bob/.approaches/if-statements/snippet.txt b/exercises/practice/bob/.approaches/if-statements/snippet.txt
index 0e166bed30f..1ffdaf7799f 100644
--- a/exercises/practice/bob/.approaches/if-statements/snippet.txt
+++ b/exercises/practice/bob/.approaches/if-statements/snippet.txt
@@ -1,7 +1,7 @@
-if isShout and isQuestion:
+if is_shout and is_question:
return "Calm down, I know what I'm doing!"
-if isShout:
+if is_shout:
return 'Whoa, chill out!'
-if isQuestion:
+if is_question:
return 'Sure.'
return 'Whatever.'
diff --git a/exercises/practice/bob/.approaches/introduction.md b/exercises/practice/bob/.approaches/introduction.md
index 173bb46f659..ccdfa93f90f 100644
--- a/exercises/practice/bob/.approaches/introduction.md
+++ b/exercises/practice/bob/.approaches/introduction.md
@@ -17,7 +17,7 @@ Regardless of the approach used, some things you could look out for include
Combine the two determinations instead of copying them.
Not duplicating the code will keep the code [DRY][dry].
-- Perhaps consider making `IsQuestion` and `IsShout` values set once instead of functions that are possibly called twice.
+- Perhaps consider making `is_question` and `is_shout` values set once instead of functions that are possibly called twice.
- If an `if` statement can return, then an `elif` or `else` is not needed.
Execution will either return or will continue to the next statement anyway.
@@ -29,13 +29,13 @@ def response(hey_bob):
hey_bob = hey_bob.rstrip()
if not hey_bob:
return 'Fine. Be that way!'
- isShout = hey_bob.isupper()
- isQuestion = hey_bob.endswith('?')
- if isShout and isQuestion:
+ is_shout = hey_bob.isupper()
+ is_question = hey_bob.endswith('?')
+ if is_shout and is_question:
return "Calm down, I know what I'm doing!"
- if isShout:
+ if is_shout:
return 'Whoa, chill out!'
- if isQuestion:
+ if is_question:
return 'Sure.'
return 'Whatever.'
@@ -50,14 +50,14 @@ def response(hey_bob):
hey_bob = hey_bob.rstrip()
if not hey_bob:
return 'Fine. Be that way!'
- isShout = hey_bob.isupper()
- isQuestion = hey_bob.endswith('?')
- if isShout:
- if isQuestion:
+ is_shout = hey_bob.isupper()
+ is_question = hey_bob.endswith('?')
+ if is_shout:
+ if is_question:
return "Calm down, I know what I'm doing!"
else:
return 'Whoa, chill out!'
- if isQuestion:
+ if is_question:
return 'Sure.'
return 'Whatever.'
diff --git a/exercises/practice/bob/.articles/performance/code/Benchmark.py b/exercises/practice/bob/.articles/performance/code/Benchmark.py
index b176dc90cc3..e726ed19532 100644
--- a/exercises/practice/bob/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/bob/.articles/performance/code/Benchmark.py
@@ -8,13 +8,13 @@ def response(hey_bob):
hey_bob = hey_bob.rstrip()
if not hey_bob:
return 'Fine. Be that way!'
- isShout = hey_bob.isupper()
- isQuestion = hey_bob.endswith('?')
- if isShout and isQuestion:
+ is_shout = hey_bob.isupper()
+ is_question = hey_bob.endswith('?')
+ if is_shout and is_question:
return "Calm down, I know what I'm doing!"
- if isShout:
+ if is_shout:
return 'Whoa, chill out!'
- if isQuestion:
+ if is_question:
return 'Sure.'
return 'Whatever.'
@@ -29,14 +29,14 @@ def response(hey_bob):
hey_bob = hey_bob.rstrip()
if not hey_bob:
return 'Fine. Be that way!'
- isShout = hey_bob.isupper()
- isQuestion = hey_bob.endswith('?')
- if isShout:
- if isQuestion:
+ is_shout = hey_bob.isupper()
+ is_question = hey_bob.endswith('?')
+ if is_shout:
+ if is_question:
return "Calm down, I know what I'm doing!"
else:
- return 'Whoa, chill out!'
- if isQuestion:
+ return 'Whoa, chill out!'
+ if is_question:
return 'Sure.'
return 'Whatever.'
@@ -47,14 +47,17 @@ def response(hey_bob):
val = timeit.timeit("""response("I really don't have anything to say.")""",
"""
-_ANSWERS = ["Whatever.", "Sure.", "Whoa, chill out!", "Calm down, I know what I'm doing!"]
+ANSWERS = ['Whatever.', 'Sure.', 'Whoa, chill out!',
+ "Calm down, I know what I'm doing!"]
+
def response(hey_bob):
hey_bob = hey_bob.rstrip()
- if not hey_bob: return 'Fine. Be that way!'
- isShout = 2 if hey_bob.isupper() else 0
- isQuestion = 1 if hey_bob.endswith('?') else 0
- return _ANSWERS[isShout + isQuestion]
+ if not hey_bob:
+ return 'Fine. Be that way!'
+ is_shout = 2 if hey_bob.isupper() else 0
+ is_question = 1 if hey_bob.endswith('?') else 0
+ return ANSWERS[is_shout + is_question]
""", number=loops) / loops
From b6362043e86d7585149866bd29be1b5498e5539e Mon Sep 17 00:00:00 2001
From: Exercism Bot <66069679+exercism-bot@users.noreply.github.com>
Date: Thu, 17 Nov 2022 13:28:43 +0000
Subject: [PATCH 101/932] =?UTF-8?q?=F0=9F=A4=96=20Sync=20org-wide=20files?=
=?UTF-8?q?=20to=20upstream=20repo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
More info: https://github.com/exercism/org-wide-files/commit/ceface8fc48f8f8be135cecafe5ea8355aac9ad6
---
.github/labels.yml | 10 +++----
CODE_OF_CONDUCT.md | 57 ++++++++++++++++++++---------------
bin/fetch-configlet | 72 +++++++++++++++++++++++++++------------------
3 files changed, 83 insertions(+), 56 deletions(-)
diff --git a/.github/labels.yml b/.github/labels.yml
index c76eadfd19a..3f8780530db 100644
--- a/.github/labels.yml
+++ b/.github/labels.yml
@@ -157,16 +157,16 @@
description: "Work on Documentation"
color: "ffffff"
-# This label can be added to accept PRs as part of Hacktoberfest
-- name: "hacktoberfest-accepted"
- description: "Make this PR count for hacktoberfest"
- color: "ff7518"
-
# This Exercism-wide label is added to all automatically created pull requests that help migrate/prepare a track for Exercism v3
- name: "v3-migration 🤖"
description: "Preparing for Exercism v3"
color: "e99695"
+# This Exercism-wide label can be used to bulk-close issues in preparation for pausing community contributions
+- name: "paused"
+ description: "Work paused until further notice"
+ color: "e4e669"
+
# ----------------------------------------------------------------------------------------- #
# These are the repository-specific labels that augment the Exercise-wide labels defined in #
# https://github.com/exercism/org-wide-files/blob/main/global-files/.github/labels.yml. #
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 9bb22baa715..df8e36761c1 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -2,17 +2,23 @@
## Introduction
-Exercism is a platform centered around empathetic conversation. We have a low tolerance for communication that makes anyone feel unwelcome, unsupported, insulted or discriminated against.
+Exercism is a platform centered around empathetic conversation.
+We have a low tolerance for communication that makes anyone feel unwelcome, unsupported, insulted or discriminated against.
## Seen or experienced something uncomfortable?
-If you see or experience abuse, harassment, discrimination, or feel unsafe or upset, please email [abuse@exercism.org](mailto:abuse@exercism.org?subject=%5BCoC%5D) and include \[CoC\] in the subject line. We will follow up with you as a priority.
+If you see or experience abuse, harassment, discrimination, or feel unsafe or upset, please email [abuse@exercism.org](mailto:abuse@exercism.org?subject=%5BCoC%5D) and include \[CoC\] in the subject line.
+We will follow up with you as a priority.
## Enforcement
-We actively monitor for Code of Conduct (CoC) violations and take any reports of violations extremely seriously. We have banned contributors, mentors and users due to violations.
+We actively monitor for Code of Conduct (CoC) violations and take any reports of violations extremely seriously.
+We have banned contributors, mentors and users due to violations.
-After we receive a report of a CoC violation, we view that person's conversation history on Exercism and related communication channels and attempt to understand whether someone has deliberately broken the CoC, or accidentally crossed a line. We generally reach out to the person who has been reported to discuss any concerns we have and warn them that repeated violations will result in a ban. Sometimes we decide that no violation has occurred and that no action is required and sometimes we will also ban people on a first offense. We strive to be fair, but will err on the side of protecting the culture of our community.
+After we receive a report of a CoC violation, we view that person's conversation history on Exercism and related communication channels and attempt to understand whether someone has deliberately broken the CoC, or accidentally crossed a line.
+We generally reach out to the person who has been reported to discuss any concerns we have and warn them that repeated violations will result in a ban.
+Sometimes we decide that no violation has occurred and that no action is required and sometimes we will also ban people on a first offense.
+We strive to be fair, but will err on the side of protecting the culture of our community.
Exercism's leadership reserve the right to take whatever action they feel appropriate with regards to CoC violations.
@@ -36,15 +42,16 @@ Exercism should be a safe place for everybody regardless of
- Race
- Age
- Religion
-- Anything else you can think of.
+- Anything else you can think of
As someone who is part of this community, you agree that:
-- We are collectively and individually committed to safety and inclusivity.
-- We have zero tolerance for abuse, harassment, or discrimination.
-- We respect people’s boundaries and identities.
-- We refrain from using language that can be considered offensive or oppressive (systemically or otherwise), eg. sexist, racist, homophobic, transphobic, ableist, classist, etc. - this includes (but is not limited to) various slurs.
-- We avoid using offensive topics as a form of humor.
+- We are collectively and individually committed to safety and inclusivity
+- We have zero tolerance for abuse, harassment, or discrimination
+- We respect people’s boundaries and identities
+- We refrain from using language that can be considered offensive or oppressive (systemically or otherwise), eg. sexist, racist, homophobic, transphobic, ableist, classist, etc.
+ - this includes (but is not limited to) various slurs.
+- We avoid using offensive topics as a form of humor
We actively work towards:
@@ -57,26 +64,30 @@ We condemn:
- Stalking, doxxing, or publishing private information
- Violence, threats of violence or violent language
- Anything that compromises people’s safety
-- Conduct or speech which might be considered sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory or offensive in nature.
-- The use of unwelcome, suggestive, derogatory or inappropriate nicknames or terms.
-- Disrespect towards others (jokes, innuendo, dismissive attitudes) and towards differences of opinion.
-- Intimidation or harassment (online or in-person). Please read the [Citizen Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md) for how we interpret harassment.
-- Inappropriate attention or contact.
-- Not understanding the differences between constructive criticism and disparagement.
+- Conduct or speech which might be considered sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory or offensive in nature
+- The use of unwelcome, suggestive, derogatory or inappropriate nicknames or terms
+- Disrespect towards others (jokes, innuendo, dismissive attitudes) and towards differences of opinion
+- Intimidation or harassment (online or in-person).
+ Please read the [Citizen Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md) for how we interpret harassment
+- Inappropriate attention or contact
+- Not understanding the differences between constructive criticism and disparagement
These things are NOT OK.
-Be aware of how your actions affect others. If it makes someone uncomfortable, stop.
+Be aware of how your actions affect others.
+If it makes someone uncomfortable, stop.
If you say something that is found offensive, and you are called out on it, try to:
-- Listen without interruption.
-- Believe what the person is saying & do not attempt to disqualify what they have to say.
-- Ask for tips / help with avoiding making the offense in the future.
-- Apologize and ask forgiveness.
+- Listen without interruption
+- Believe what the person is saying & do not attempt to disqualify what they have to say
+- Ask for tips / help with avoiding making the offense in the future
+- Apologize and ask forgiveness
## History
-This policy was initially adopted from the Front-end London Slack community and has been modified since. A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md).
+This policy was initially adopted from the Front-end London Slack community and has been modified since.
+A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md).
-_This policy is a "living" document, and subject to refinement and expansion in the future. This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Slack, Twitter, email) and any other Exercism entity or event._
+_This policy is a "living" document, and subject to refinement and expansion in the future.
+This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Slack, Twitter, email) and any other Exercism entity or event._
diff --git a/bin/fetch-configlet b/bin/fetch-configlet
index 827088ab17c..4800e150849 100755
--- a/bin/fetch-configlet
+++ b/bin/fetch-configlet
@@ -6,29 +6,6 @@
set -eo pipefail
-readonly LATEST='https://api.github.com/repos/exercism/configlet/releases/latest'
-
-case "$(uname)" in
- Darwin*) os='mac' ;;
- Linux*) os='linux' ;;
- Windows*) os='windows' ;;
- MINGW*) os='windows' ;;
- MSYS_NT-*) os='windows' ;;
- *) os='linux' ;;
-esac
-
-case "${os}" in
- windows*) ext='zip' ;;
- *) ext='tgz' ;;
-esac
-
-case "$(uname -m)" in
- *64*) arch='64bit' ;;
- *686*) arch='32bit' ;;
- *386*) arch='32bit' ;;
- *) arch='64bit' ;;
-esac
-
curlopts=(
--silent
--show-error
@@ -41,15 +18,25 @@ if [[ -n "${GITHUB_TOKEN}" ]]; then
curlopts+=(--header "authorization: Bearer ${GITHUB_TOKEN}")
fi
-suffix="${os}-${arch}.${ext}"
-
get_download_url() {
- curl "${curlopts[@]}" --header 'Accept: application/vnd.github.v3+json' "${LATEST}" |
+ local os="$1"
+ local ext="$2"
+ local latest='https://api.github.com/repos/exercism/configlet/releases/latest'
+ local arch
+ case "$(uname -m)" in
+ x86_64) arch='x86-64' ;;
+ *686*) arch='i386' ;;
+ *386*) arch='i386' ;;
+ *) arch='x86-64' ;;
+ esac
+ local suffix="${os}_${arch}.${ext}"
+ curl "${curlopts[@]}" --header 'Accept: application/vnd.github.v3+json' "${latest}" |
grep "\"browser_download_url\": \".*/download/.*/configlet.*${suffix}\"$" |
cut -d'"' -f4
}
main() {
+ local output_dir
if [[ -d ./bin ]]; then
output_dir="./bin"
elif [[ $PWD == */bin ]]; then
@@ -59,8 +46,26 @@ main() {
return 1
fi
- download_url="$(get_download_url)"
- output_path="${output_dir}/latest-configlet.${ext}"
+ local os
+ case "$(uname)" in
+ Darwin*) os='macos' ;;
+ Linux*) os='linux' ;;
+ Windows*) os='windows' ;;
+ MINGW*) os='windows' ;;
+ MSYS_NT-*) os='windows' ;;
+ *) os='linux' ;;
+ esac
+
+ local ext
+ case "${os}" in
+ windows*) ext='zip' ;;
+ *) ext='tar.gz' ;;
+ esac
+
+ echo "Fetching configlet..." >&2
+ local download_url
+ download_url="$(get_download_url "${os}" "${ext}")"
+ local output_path="${output_dir}/latest-configlet.${ext}"
curl "${curlopts[@]}" --output "${output_path}" "${download_url}"
case "${ext}" in
@@ -69,6 +74,17 @@ main() {
esac
rm -f "${output_path}"
+
+ local executable_ext
+ case "${os}" in
+ windows*) executable_ext='.exe' ;;
+ *) executable_ext='' ;;
+ esac
+
+ local configlet_path="${output_dir}/configlet${executable_ext}"
+ local configlet_version
+ configlet_version="$(${configlet_path} --version)"
+ echo "Downloaded configlet ${configlet_version} to ${configlet_path}"
}
main
From 37d75769d0e4102434a589cee1f6d7ccdf9fa0d5 Mon Sep 17 00:00:00 2001
From: 0xA <43623870+zylideum@users.noreply.github.com>
Date: Wed, 9 Nov 2022 10:54:55 -0700
Subject: [PATCH 102/932] Fixed typo in Task 4 example
---
exercises/concept/tisbury-treasure-hunt/.docs/instructions.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/tisbury-treasure-hunt/.docs/instructions.md b/exercises/concept/tisbury-treasure-hunt/.docs/instructions.md
index ae5dbf8b529..b9e514e512f 100644
--- a/exercises/concept/tisbury-treasure-hunt/.docs/instructions.md
+++ b/exercises/concept/tisbury-treasure-hunt/.docs/instructions.md
@@ -98,7 +98,7 @@ Re-format the coordinate as needed for accurate comparison.
>>> create_record(('Brass Spyglass', '4B'), ('Abandoned Lighthouse', ('4', 'B'), 'Blue'))
('Brass Spyglass', '4B', 'Abandoned Lighthouse', ('4', 'B'), 'Blue')
->>> create_record(('Brass Spyglass', '4B'), (('1', 'C'), 'Seaside Cottages', 'blue'))
+>>> create_record(('Brass Spyglass', '4B'), ('Seaside Cottages', ('1', 'C'), 'blue'))
"not a match"
```
From 4374b84249c61e9a6fb13343306bca44f8c38fe0 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 1 Nov 2022 12:14:17 +0100
Subject: [PATCH 103/932] Added pdb
---
docs/TRACEBACKS.md | 100 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index e4a407e1a61..3aecb00edf0 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -188,8 +188,108 @@ Setting `PYTHONOPTIMIZE` to `1` is equivalent to running Python with the `-O` op
Setting `PYTHONOPTIMIZE` to `2` is equivalent to running Python with the `-OO` option, which both disables assertions and removes docstrings from the bytcode.
Reducing bytecode is one way to make the code run faster.
+## Python Debugger
+
+Python has a built in debugger, [pdb][pdb]. It can be used to step through code and inspect variables. You can also set breakpoints with it.
+To get started you have to first import pdb and then call pdb.set_trace() where you want to start debugging.
+
+```python
+import pdb
+
+def add(num1, num2):
+ return num1 + num2
+
+pdb.set_trace()
+sum = add(1,5)
+print(sum)
+```
+
+Running this code will give you a prompt where you can type in commands. Write `help` to get a list of commands.
+The most common onces are `step` which steps into a function called at that line. `next` which steps over a function call and move to the next line. `where` which tells you which line you are on. Some other usefull commands are `whatis ` which tells you the type of a variable and `print()` which prints the value of a variable. You can also just use `` to print the value of a variable. Another command is `jump ` which jumps to a specific line number.
+
+Here are an example on how to use the debugger based on the code earlier:
+
+```python
+>>> python pdb.py
+... > c:\pdb.py(7)()
+... -> sum = add(1,5)
+... (Pdb)
+>>> step
+... > c:\pdb.py(3)add()
+... -> def add(num1, num2):
+... (Pdb)
+>>> whatis num1
+...
+>>> print(num2)
+... 5
+>>> next
+... > c:\pdb.py(4)add()
+... -> return num1 + num2
+... (Pdb)
+>>> jump 3
+... > c:\pdb.py(3)add()
+... -> def add(num1, num2):
+... (Pdb)
+```
+
+Breakpoints is setup by `break : ` where condition is an optional condition that has to be true for the breakpoint to be hit. You can simply write `break` to get a list of the breakpoints you have set. To disable a breakpoint you can write `disable `. To enable a breakpoint you can write `enable `. To delete a breakpoint you can write `clear `. To continue execution you can write `continue` or `c`. To exit the debugger you can write `quit` or `q`.
+
+Here are an example on how to use the debugger based on the code earlier:
+
+```python
+>>> python pdb.py
+... > c:\pdb.py(7)()
+... -> sum = add(1,5)
+... (Pdb)
+>>> break
+...
+>>> break pdb:4
+... Breakpoint 1 at c:\pdb.py:4
+>>> break
+... Num Type Disp Enb Where
+... 2 breakpoint keep yes at c:\pdb.py:4
+>>> c # continue
+... > c:\pdn.py(4)add()
+... -> return num1 + num2
+>>> disable break 1
+... Disabled breakpoint 1 at c:\pdb.py:4
+>>> break
+... Num Type Disp Enb Where
+... 1 breakpoint keep no at c:\pdb.py:4
+... breakpoint already hit 1 time
+>>> clear break 1
+... Deleted breakpoint 1 at c:\pdb.py:4
+>>> break
+...
+```
+
+In python 3.7 and newer there is an easier way to create breakpoints You can simply write `breakpoint()` where you want to create a breakpoint.
+
+```python
+import pdb
+
+def add(num1, num2):
+ breakpoint()
+ return num1 + num2
+
+pdb.set_trace()
+sum = add(1,5)
+print(sum)
+```
+
+```python
+>>> python pdb.py
+... > c:\pdb.py(7)()
+... -> sum = add(1,5)
+... (Pdb)
+>>> c # continue
+... > c:\pdb.py(5)add()
+... -> return num1 + num2
+```
+
[assert]: https://realpython.com/python-assert-statement/
[AssertionError]: https://www.geeksforgeeks.org/python-assertion-error/
[floor divison operator]: https://www.codingem.com/python-floor-division
[logging]: https://docs.python.org/3/howto/logging.html
[print]: https://www.w3schools.com/python/ref_func_print.asp
+[pdb]: https://www.geeksforgeeks.org/python-debugger-python-pdb/
From bac4d3d6ca45133e6936534d681c58f40d4f4fe3 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 1 Nov 2022 12:20:38 +0100
Subject: [PATCH 104/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 3aecb00edf0..69deaa8310c 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -190,7 +190,8 @@ Reducing bytecode is one way to make the code run faster.
## Python Debugger
-Python has a built in debugger, [pdb][pdb]. It can be used to step through code and inspect variables. You can also set breakpoints with it.
+Python has a built in debugger, [pdb][pdb].
+It can be used to step through code and inspect variables. You can also set breakpoints with it.
To get started you have to first import pdb and then call pdb.set_trace() where you want to start debugging.
```python
@@ -204,8 +205,13 @@ sum = add(1,5)
print(sum)
```
-Running this code will give you a prompt where you can type in commands. Write `help` to get a list of commands.
-The most common onces are `step` which steps into a function called at that line. `next` which steps over a function call and move to the next line. `where` which tells you which line you are on. Some other usefull commands are `whatis ` which tells you the type of a variable and `print()` which prints the value of a variable. You can also just use `` to print the value of a variable. Another command is `jump ` which jumps to a specific line number.
+Running this code will give you a prompt where you can type in commands.
+Write `help` to get a list of commands.
+The most common onces are `step` which steps into a function called at that line.
+`next` which steps over a function call and move to the next line. `where` which tells you which line you are on.
+Some other usefull commands are `whatis ` which tells you the type of a variable and `print()` which prints the value of a variable.
+You can also just use `` to print the value of a variable.
+Another command is `jump ` which jumps to a specific line number.
Here are an example on how to use the debugger based on the code earlier:
@@ -232,7 +238,12 @@ Here are an example on how to use the debugger based on the code earlier:
... (Pdb)
```
-Breakpoints is setup by `break : ` where condition is an optional condition that has to be true for the breakpoint to be hit. You can simply write `break` to get a list of the breakpoints you have set. To disable a breakpoint you can write `disable `. To enable a breakpoint you can write `enable `. To delete a breakpoint you can write `clear `. To continue execution you can write `continue` or `c`. To exit the debugger you can write `quit` or `q`.
+Breakpoints is setup by `break : ` where condition is an optional condition that has to be true for the breakpoint to be hit.
+You can simply write `break` to get a list of the breakpoints you have set.
+To disable a breakpoint you can write `disable `.
+To enable a breakpoint you can write `enable `.
+To delete a breakpoint you can write `clear `.
+To continue execution you can write `continue` or `c`. To exit the debugger you can write `quit` or `q`.
Here are an example on how to use the debugger based on the code earlier:
@@ -288,7 +299,7 @@ print(sum)
```
[assert]: https://realpython.com/python-assert-statement/
-[AssertionError]: https://www.geeksforgeeks.org/python-assertion-error/
+[assertionerror]: https://www.geeksforgeeks.org/python-assertion-error/
[floor divison operator]: https://www.codingem.com/python-floor-division
[logging]: https://docs.python.org/3/howto/logging.html
[print]: https://www.w3schools.com/python/ref_func_print.asp
From 623fe0e8b7fa3bb4ac1bc8b2ec2509e7525860a5 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 1 Nov 2022 12:27:06 +0100
Subject: [PATCH 105/932] Spelling fixes
---
docs/TRACEBACKS.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 69deaa8310c..41ebf756ee9 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -207,13 +207,13 @@ print(sum)
Running this code will give you a prompt where you can type in commands.
Write `help` to get a list of commands.
-The most common onces are `step` which steps into a function called at that line.
-`next` which steps over a function call and move to the next line. `where` which tells you which line you are on.
-Some other usefull commands are `whatis ` which tells you the type of a variable and `print()` which prints the value of a variable.
+The most common ones are `step` which steps into a function called at that line.
+`next` steps over a function call and move to the next line. `where` tells you which line you are on.
+Some other useful commands are `whatis ` which tells you the type of a variable and `print()` which prints the value of a variable.
You can also just use `` to print the value of a variable.
Another command is `jump ` which jumps to a specific line number.
-Here are an example on how to use the debugger based on the code earlier:
+Here is an example of how to use the debugger based on the code earlier:
```python
>>> python pdb.py
@@ -238,14 +238,14 @@ Here are an example on how to use the debugger based on the code earlier:
... (Pdb)
```
-Breakpoints is setup by `break : ` where condition is an optional condition that has to be true for the breakpoint to be hit.
+Breakpoints are set up by `break : ` where the condition is an optional condition that has to be true for the breakpoint to be hit.
You can simply write `break` to get a list of the breakpoints you have set.
To disable a breakpoint you can write `disable `.
To enable a breakpoint you can write `enable `.
To delete a breakpoint you can write `clear `.
To continue execution you can write `continue` or `c`. To exit the debugger you can write `quit` or `q`.
-Here are an example on how to use the debugger based on the code earlier:
+Here is an example of how to use the debugger based on the code earlier:
```python
>>> python pdb.py
From 996df0e92ab31d234ff6e444c4fc6171c653a98a Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 1 Nov 2022 13:29:24 +0100
Subject: [PATCH 106/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 41ebf756ee9..8661efbce95 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -277,13 +277,11 @@ Here is an example of how to use the debugger based on the code earlier:
In python 3.7 and newer there is an easier way to create breakpoints You can simply write `breakpoint()` where you want to create a breakpoint.
```python
-import pdb
-
def add(num1, num2):
breakpoint()
return num1 + num2
-pdb.set_trace()
+breakpoint()
sum = add(1,5)
print(sum)
```
From c490085cf9091b453305c05b240f42a3e491ccd6 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Fri, 18 Nov 2022 09:20:37 +0100
Subject: [PATCH 107/932] Apply suggestions from code review
Co-authored-by: BethanyG
---
docs/TRACEBACKS.md | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 8661efbce95..0a80c048a3a 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -192,7 +192,7 @@ Reducing bytecode is one way to make the code run faster.
Python has a built in debugger, [pdb][pdb].
It can be used to step through code and inspect variables. You can also set breakpoints with it.
-To get started you have to first import pdb and then call pdb.set_trace() where you want to start debugging.
+To get started you have to first `import pdb` and then call `pdb.set_trace()` where you want to start debugging:
```python
import pdb
@@ -205,7 +205,7 @@ sum = add(1,5)
print(sum)
```
-Running this code will give you a prompt where you can type in commands.
+Running this code will give you a pdb prompt where you can type in commands.
Write `help` to get a list of commands.
The most common ones are `step` which steps into a function called at that line.
`next` steps over a function call and move to the next line. `where` tells you which line you are on.
@@ -213,7 +213,8 @@ Some other useful commands are `whatis ` which tells you the type of a
You can also just use `` to print the value of a variable.
Another command is `jump ` which jumps to a specific line number.
-Here is an example of how to use the debugger based on the code earlier:
+Here is a small example of how to use the debugger based on the code earlier.
+Note that for this and following examples, MacOS or Linux platforms would have file paths using forward slashes:
```python
>>> python pdb.py
@@ -245,7 +246,7 @@ To enable a breakpoint you can write `enable `.
To delete a breakpoint you can write `clear `.
To continue execution you can write `continue` or `c`. To exit the debugger you can write `quit` or `q`.
-Here is an example of how to use the debugger based on the code earlier:
+Here is an example of how to use the above debugger commands based on the code earlier:
```python
>>> python pdb.py
@@ -274,7 +275,8 @@ Here is an example of how to use the debugger based on the code earlier:
...
```
-In python 3.7 and newer there is an easier way to create breakpoints You can simply write `breakpoint()` where you want to create a breakpoint.
+In Python 3.7+ there is an easier way to create breakpoints
+Simply writing `breakpoint()` where needed will create one.
```python
def add(num1, num2):
From 979f7f7a6ea1ef2698e362a181c0f87cebf60210 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Fri, 18 Nov 2022 09:21:47 +0100
Subject: [PATCH 108/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 0a80c048a3a..acca29b0412 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -246,7 +246,7 @@ To enable a breakpoint you can write `enable `.
To delete a breakpoint you can write `clear `.
To continue execution you can write `continue` or `c`. To exit the debugger you can write `quit` or `q`.
-Here is an example of how to use the above debugger commands based on the code earlier:
+Here is an example of how to use the above debugger commands based on the code earlier:
```python
>>> python pdb.py
@@ -275,7 +275,7 @@ Here is an example of how to use the above debugger commands based on the code
...
```
-In Python 3.7+ there is an easier way to create breakpoints
+In Python 3.7+ there is an easier way to create breakpoints
Simply writing `breakpoint()` where needed will create one.
```python
From 5983e7e946bba0d939d83742aca7102e8b8207a2 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Fri, 18 Nov 2022 09:30:55 +0100
Subject: [PATCH 109/932] Update TRACEBACKS.md
---
docs/TRACEBACKS.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index acca29b0412..ddda2eb8bd3 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -191,7 +191,8 @@ Reducing bytecode is one way to make the code run faster.
## Python Debugger
Python has a built in debugger, [pdb][pdb].
-It can be used to step through code and inspect variables. You can also set breakpoints with it.
+It can be used to step through code and inspect variables.
+You can also set breakpoints with it.
To get started you have to first `import pdb` and then call `pdb.set_trace()` where you want to start debugging:
```python
From 7a1e726a2b3ee6c75ab2f5638dbc5064932939e4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 06:38:29 -0600
Subject: [PATCH 110/932] Create config.json
---
.../practice/leap/.approaches/config.json | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/config.json
diff --git a/exercises/practice/leap/.approaches/config.json b/exercises/practice/leap/.approaches/config.json
new file mode 100644
index 00000000000..3b5d57f997a
--- /dev/null
+++ b/exercises/practice/leap/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "5d42dc83-2473-425a-90bd-bf03f92b8c8b",
+ "slug": "boolean-chain",
+ "title": "Boolean chain",
+ "blurb": "Use a chain of boolean expressions.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "9952fef5-9f2f-4575-94fc-bc4e96593cd6",
+ "slug": "ternary-operator",
+ "title": "Ternary operator",
+ "blurb": "Use a ternary operator of boolean expressions.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "66302791-0770-4f08-beaa-251c49e280a2",
+ "slug": "datetime-addition",
+ "title": "datetime addition",
+ "blurb": "Use datetime addition.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From ab6b85aea0957b1f690c879d661a102c8ce343f9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 06:58:09 -0600
Subject: [PATCH 111/932] Create config.json
---
exercises/practice/leap/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/leap/.articles/config.json
diff --git a/exercises/practice/leap/.articles/config.json b/exercises/practice/leap/.articles/config.json
new file mode 100644
index 00000000000..acafe3b22d7
--- /dev/null
+++ b/exercises/practice/leap/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "e54a0a87-cb9d-4d5c-aa86-93a239ffdd8c",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach for determining a leap year.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 086bf51fb0e0d0714878be47dc5867dcca9b2b08 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 06:59:13 -0600
Subject: [PATCH 112/932] Create snippet.md
---
exercises/practice/leap/.articles/performance/snippet.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/leap/.articles/performance/snippet.md
diff --git a/exercises/practice/leap/.articles/performance/snippet.md b/exercises/practice/leap/.articles/performance/snippet.md
new file mode 100644
index 00000000000..2b3e77bb99d
--- /dev/null
+++ b/exercises/practice/leap/.articles/performance/snippet.md
@@ -0,0 +1,5 @@
+```
+if statements 2019: 8.861289999913425e-08
+ternary 2019: 1.0278620000462979e-07
+datetime add 2019: 6.689728000201284e-07
+```
From 2ab585fe5fc2d33578290a6441dd590fdff72fa4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:02:29 -0600
Subject: [PATCH 113/932] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 88 +++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 exercises/practice/leap/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/leap/.articles/performance/code/Benchmark.py b/exercises/practice/leap/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..467a5952923
--- /dev/null
+++ b/exercises/practice/leap/.articles/performance/code/Benchmark.py
@@ -0,0 +1,88 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""leap_year(1900)""",
+ """
+def leap_year(year):
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
+
+""", number=loops) / loops
+
+print(f"if statements 1900: {val}")
+
+val = timeit.timeit("""leap_year(2000)""",
+ """
+def leap_year(year):
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
+
+""", number=loops) / loops
+
+print(f"if statements 2000: {val}")
+
+
+val = timeit.timeit("""leap_year(2019)""",
+ """
+def leap_year(year):
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
+
+""", number=loops) / loops
+
+print(f"if statements 2019: {val}")
+
+val = timeit.timeit("""leap_year(2020)""",
+ """
+def leap_year(year):
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
+
+""", number=loops) / loops
+
+print(f"if statements 2020: {val}")
+
+val = timeit.timeit("""leap_year(1900)""",
+ """
+def leap_year(year):
+ return not year % 400 if not year % 100 else not year % 4
+
+""", number=loops) / loops
+
+print(f"ternary 1900: {val}")
+
+val = timeit.timeit("""leap_year(2000)""",
+ """
+def leap_year(year):
+ return not year % 400 if not year % 100 else not year % 4
+
+""", number=loops) / loops
+
+print(f"ternary 2000: {val}")
+
+val = timeit.timeit("""leap_year(2019)""",
+ """
+def leap_year(year):
+ return not year % 400 if not year % 100 else not year % 4
+
+""", number=loops) / loops
+
+print(f"ternary 2019: {val}")
+
+val = timeit.timeit("""leap_year(2020)""",
+ """
+def leap_year(year):
+ return not year % 400 if not year % 100 else not year % 4
+
+""", number=loops) / loops
+
+print(f"ternary 2020: {val}")
+
+val = timeit.timeit("""leap_year(2019)""",
+ """
+import datetime
+
+def leap_year(year):
+ return (datetime.datetime(year, 2, 28) +
+ datetime.timedelta(days=1)).day == 29
+
+""", number=loops) / loops
+
+print(f"datetime add 2019: {val}")
From f067a6d8478c7eb75a2a62af9e908a60deb12489 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:10:55 -0600
Subject: [PATCH 114/932] Create content.md
---
.../leap/.articles/performance/content.md | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 exercises/practice/leap/.articles/performance/content.md
diff --git a/exercises/practice/leap/.articles/performance/content.md b/exercises/practice/leap/.articles/performance/content.md
new file mode 100644
index 00000000000..f3a539fcb6a
--- /dev/null
+++ b/exercises/practice/leap/.articles/performance/content.md
@@ -0,0 +1,36 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently calculate if a year is a leap year in Python.
+
+The [approaches page][approaches] lists two idiomatic approaches to this exercise:
+
+1. [Using the boolean chain][approach-boolean-chain]
+2. [Using the ternary operator][approach-ternary-operator]
+
+For our performance investigation, we'll also include a third approach that [uses datetime addition][approach-datetime-addition].
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+if statements 1900: 1.468243999988772e-07
+if statements 2000: 1.3710349999018945e-07
+if statements 2019: 8.861289999913425e-08
+if statements 2020: 1.21072500012815e-07
+ternary 1900: 1.091794999956619e-07
+ternary 2000: 1.0275900000124239e-07
+ternary 2019: 1.0278620000462979e-07
+ternary 2020: 1.0290379999787546e-07
+datetime add 2019: 6.689728000201284e-07
+```
+
+You can see that the ternary operator was faster than the chain of conditions.
+Adding the `datetime` may not only be a "cheat", but it was slower than the ternary operator.
+
+[approaches]: https://exercism.org/tracks/python/exercises/leap/approaches
+[approach-boolean-chain]: https://exercism.org/tracks/python/exercises/leap/approaches/boolean-chain
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/leap/approaches/ternary-operator
+[approach-datetime-addition]: https://exercism.org/tracks/python/exercises/leap/approaches/datetime-addition
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/leap/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
From 019adb5fbfaec30019e1e41dc24a6e2acf4fc871 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:25:56 -0600
Subject: [PATCH 115/932] Create introduction.md
---
.../practice/leap/.approaches/introduction.md | 67 +++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/introduction.md
diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md
new file mode 100644
index 00000000000..2ce1069844f
--- /dev/null
+++ b/exercises/practice/leap/.approaches/introduction.md
@@ -0,0 +1,67 @@
+# Introduction
+
+There are various idiomatic approaches to solve Leap.
+You can use a chain of boolean expressions to test the conditions.
+Or you can use a [ternary operator][ternary-operator].
+
+## General guidance
+
+The key to solving Leap is to know if the year is evenly divisible by `4`, `100` and `400`.
+For determining that, you will use the [modulo operator][modulo-operator].
+
+## Approach: Chain of Boolean expressions
+
+```python
+def leap_year(year):
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
+
+```
+
+For more information, check the [Boolean chain approach][approach-boolean-chain].
+
+## Approach: Ternary operator of Boolean expressions
+
+```python
+def leap_year(year):
+ return (not year % 400 if not year % 100 else not year % 4)
+
+```
+
+For more information, check the [Ternary operator approach][approach-ternary-operator].
+
+## Other approaches
+
+Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows:
+
+### Approach: datetime addition
+
+Add a day to February 28th for the year and see if the new day is the 29th. For more information, see the [`datetime` addition approach][approach-datetime-addition].
+
+stiick code here for now
+
+```python
+import datetime
+
+
+def leap_year(year):
+ return (datetime.datetime(year, 2, 28)
+ + datetime.timedelta(days=1)).day == 29
+
+```
+
+## Which approach to use?
+
+- The chain of boolean expressions shhould be the most efficient, as it proceeds from the most likely to least likely conditions.
+It has a maximum of three checks.
+- The ternary operator has a maximum of only two checks, but it starts from a less likely condition.
+Yet the ternary operator was faster in benchmarking.
+- Using `datetime` addition may be considered a "cheat" for the exercise, and it was slower than the ternary operator in benchmarking.
+
+For more information, check the [Performance article][article-performance].
+
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[approach-boolean-chain]: https://exercism.org/tracks/python/exercises/leap/approaches/boolean-chain
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/leap/approaches/ternary-operator
+[approach-datetime-addition]: https://exercism.org/tracks/python/exercises/leap/approaches/datetime-addition
+[article-performance]: https://exercism.org/tracks/python/exercises/leap/articles/performance
From a785caa11affdf505de8e2772ba59f211c83f119 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:26:57 -0600
Subject: [PATCH 116/932] Create snippet.txt
---
exercises/practice/leap/.approaches/boolean-chain/snippet.txt | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/boolean-chain/snippet.txt
diff --git a/exercises/practice/leap/.approaches/boolean-chain/snippet.txt b/exercises/practice/leap/.approaches/boolean-chain/snippet.txt
new file mode 100644
index 00000000000..df5c3e62de0
--- /dev/null
+++ b/exercises/practice/leap/.approaches/boolean-chain/snippet.txt
@@ -0,0 +1,2 @@
+def leap_year(year):
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
From 7383c4eec4390f3344e853721934f2eae3804297 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:27:40 -0600
Subject: [PATCH 117/932] Create snippet.txt
---
.../practice/leap/.approaches/ternary-operator/snippet.txt | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/ternary-operator/snippet.txt
diff --git a/exercises/practice/leap/.approaches/ternary-operator/snippet.txt b/exercises/practice/leap/.approaches/ternary-operator/snippet.txt
new file mode 100644
index 00000000000..bc7d90c3f3d
--- /dev/null
+++ b/exercises/practice/leap/.approaches/ternary-operator/snippet.txt
@@ -0,0 +1,2 @@
+def leap_year(year):
+ return (not year % 400 if not year % 100 else not year % 4)
From b51761126b310408bfd6cf23b36bc84b40b5f19f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:28:39 -0600
Subject: [PATCH 118/932] Create snippet.txt
---
.../practice/leap/.approaches/datetime-addition/snippet.txt | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/datetime-addition/snippet.txt
diff --git a/exercises/practice/leap/.approaches/datetime-addition/snippet.txt b/exercises/practice/leap/.approaches/datetime-addition/snippet.txt
new file mode 100644
index 00000000000..b02df891b0f
--- /dev/null
+++ b/exercises/practice/leap/.approaches/datetime-addition/snippet.txt
@@ -0,0 +1,6 @@
+import datetime
+
+
+def leap_year(year):
+ return (datetime.datetime(year, 2, 28)
+ + datetime.timedelta(days=1)).day == 29
From 6532f014a716986554e811a77ed281d4baa10dfd Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:46:06 -0600
Subject: [PATCH 119/932] Create content.md
---
.../leap/.approaches/boolean-chain/content.md | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/boolean-chain/content.md
diff --git a/exercises/practice/leap/.approaches/boolean-chain/content.md b/exercises/practice/leap/.approaches/boolean-chain/content.md
new file mode 100644
index 00000000000..c63b0c9b17e
--- /dev/null
+++ b/exercises/practice/leap/.approaches/boolean-chain/content.md
@@ -0,0 +1,44 @@
+# Chain of boolean expressions
+
+```python
+def leap_year(year):
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
+
+```
+
+The first boolean expression uses the [modulo operator][modulo-operator] to check if the year is evenly divided by `4`.
+- If the year is not evenly divisible by `4`, then the chain will "short circuit" due to the next operator being a [logical AND][logical-and] and`), and will return `False`.
+- If the year _is_ evenly divisible by `4`, then the year is checked to _not_ be evenly divisible by `100`.
+- If the year is not evenly divisible by `100`, then the expression is `True` and the chain will "short-circuit" to return `True`,
+since the next operator is a [logical OR][logical-or] (`or`).
+- If the year _is_ evenly divisible by `100`, then the expression is `False`, and the returned value from the chain will be if the year is evenly divisible by `400`.
+
+| year | year % 4 == 0 | year % 100 != 0 | year % 400 == 0 | is leap year |
+| ---- | ------------- | --------------- | --------------- | ------------ |
+| 2020 | True | True | not evaluated | True |
+| 2019 | False | not evaluated | not evaluated | False |
+| 2000 | True | False | True | True |
+| 1900 | True | False | False | False |
+
+
+The chain of boolean expressions is efficient, as it proceeds from testing the most likely to least likely conditions.
+It is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year.
+
+## Shortening
+
+By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+For example
+
+```python
+def leap_year(year):
+ return not year % 4 and (year % 100 != 0 or not year % 400)
+
+```
+
+It can be thought of as the expression _not_ having a remainder.
+
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[logical-and]: https://realpython.com/python-and-operator/
+[logical-or]: https://realpython.com/python-or-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
From 0ea174b2497df342f140daf6ed495335966be903 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:49:42 -0600
Subject: [PATCH 120/932] Update introduction.md
---
exercises/practice/leap/.approaches/introduction.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md
index 2ce1069844f..a0a1a18169c 100644
--- a/exercises/practice/leap/.approaches/introduction.md
+++ b/exercises/practice/leap/.approaches/introduction.md
@@ -51,10 +51,11 @@ def leap_year(year):
## Which approach to use?
-- The chain of boolean expressions shhould be the most efficient, as it proceeds from the most likely to least likely conditions.
+- The chain of boolean expressions should be the most efficient, as it proceeds from the most likely to least likely conditions.
It has a maximum of three checks.
+It is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year.
- The ternary operator has a maximum of only two checks, but it starts from a less likely condition.
-Yet the ternary operator was faster in benchmarking.
+The ternary operator was faster in benchmarking when the year was a leap year or was evenly divisible by `100`.
- Using `datetime` addition may be considered a "cheat" for the exercise, and it was slower than the ternary operator in benchmarking.
For more information, check the [Performance article][article-performance].
From 0cdaf5328d39c6ef988408a9b997b5af9c367bc7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 07:52:00 -0600
Subject: [PATCH 121/932] Update content.md
---
exercises/practice/leap/.articles/performance/content.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/leap/.articles/performance/content.md b/exercises/practice/leap/.articles/performance/content.md
index f3a539fcb6a..387de7902b8 100644
--- a/exercises/practice/leap/.articles/performance/content.md
+++ b/exercises/practice/leap/.articles/performance/content.md
@@ -25,8 +25,9 @@ ternary 2020: 1.0290379999787546e-07
datetime add 2019: 6.689728000201284e-07
```
-You can see that the ternary operator was faster than the chain of conditions.
-Adding the `datetime` may not only be a "cheat", but it was slower than the ternary operator.
+The boolean chain is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year.
+The ternary operator is faster in benchmarking when the year is a leap year or is evenly divisible by `100`.
+Adding the `datetime` may not only be a "cheat", but it is slower than the ternary operator.
[approaches]: https://exercism.org/tracks/python/exercises/leap/approaches
[approach-boolean-chain]: https://exercism.org/tracks/python/exercises/leap/approaches/boolean-chain
From 01f0fdf5f3ec1df697e072b64783eef0482800a9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 09:54:13 -0600
Subject: [PATCH 122/932] Create content.md
---
.../.approaches/ternary-operator/content.md | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/ternary-operator/content.md
diff --git a/exercises/practice/leap/.approaches/ternary-operator/content.md b/exercises/practice/leap/.approaches/ternary-operator/content.md
new file mode 100644
index 00000000000..b4a81f5a6a1
--- /dev/null
+++ b/exercises/practice/leap/.approaches/ternary-operator/content.md
@@ -0,0 +1,33 @@
+# Ternary operator
+
+```python
+def leap_year(year):
+ return (not year % 400 if not year % 100 else not year % 4)
+
+```
+
+A [ternary operator][ternary-operator] uses a maximum of two checks to determine if a year is a leap year.
+
+It starts by testing the outlier condition of the year being evenly divisible by `100`.
+It does this by using the [modulo operator][modulo-operator].
+And by using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+
+- If the year is evenly divisible by `100`, then the expression is `True`, and the ternary operator returns if the year is evenly divisible by `400`.
+- If the year is _not_ evenly divisible by `100`, then the expression is `False`, and the ternary operator returns if the year is evenly divisible by `4`.
+
+| year | year % 100 == 0 | year % 400 == 0 | year % 4 == 0 | is leap year |
+| ---- | --------------- | --------------- | -------------- | ------------ |
+| 2020 | False | not evaluated | True | True |
+| 2019 | False | not evaluated | False | False |
+| 2000 | True | True | not evaluated | True |
+| 1900 | True | False | not evaluated | False |
+
+Although it uses a maximum of only two checks, the ternary operator tests an outlier condition first,
+making it less efficient than another approach that would first test if the year is evenly divisible by `4`,
+which is more likely than the year being evenly divisible by `100`.
+The ternary operator was fastest in benchmarking when the year was a leap year or was evenly divisible by 100.
+
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
From 1976c063e2b5de4a2eb714b044c3d7c026118771 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 10:09:39 -0600
Subject: [PATCH 123/932] Create content.md
---
.../.approaches/datetime-addition/content.md | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 exercises/practice/leap/.approaches/datetime-addition/content.md
diff --git a/exercises/practice/leap/.approaches/datetime-addition/content.md b/exercises/practice/leap/.approaches/datetime-addition/content.md
new file mode 100644
index 00000000000..862abcfe1b3
--- /dev/null
+++ b/exercises/practice/leap/.approaches/datetime-addition/content.md
@@ -0,0 +1,26 @@
+# `datetime` addition
+
+```python
+import datetime
+
+
+def leap_year(year):
+ return (datetime.datetime(year, 2, 28)
+ + datetime.timedelta(days=1)).day == 29
+
+```
+
+```exercism/caution
+This approach may be considered a "cheat" for this exercise.
+```
+
+By adding a day to February 28th for the `year`, you can see if the new day is the 29th or the 1st.
+If it is the 29th, then the function returns `True` for the `year` being a leap year.
+
+- A new [datetime][datetime] object is created for Febuary 28th of the `year`.
+- Then the [timedelta][timedelta] of one day is added to that `datetime`,
+and the function returns if the [day][day] property of the resulting `datetime` object is the 29th.
+
+[timedelta]: https://docs.python.org/3/library/datetime.html#timedelta-objects
+[day]: https://docs.python.org/3/library/datetime.html#datetime.datetime.day
+[datetime]: https://docs.python.org/3/library/datetime.html#datetime-objects
From de0e83123a3e3fcdda196f3782aadc15a6279bd4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 10:12:51 -0600
Subject: [PATCH 124/932] Update introduction.md
---
.../practice/leap/.approaches/introduction.md | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md
index a0a1a18169c..a5c8c829a56 100644
--- a/exercises/practice/leap/.approaches/introduction.md
+++ b/exercises/practice/leap/.approaches/introduction.md
@@ -37,26 +37,16 @@ Besides the aforementioned, idiomatic approaches, you could also approach the ex
Add a day to February 28th for the year and see if the new day is the 29th. For more information, see the [`datetime` addition approach][approach-datetime-addition].
-stiick code here for now
-
-```python
-import datetime
-
-
-def leap_year(year):
- return (datetime.datetime(year, 2, 28)
- + datetime.timedelta(days=1)).day == 29
-
-```
-
## Which approach to use?
- The chain of boolean expressions should be the most efficient, as it proceeds from the most likely to least likely conditions.
It has a maximum of three checks.
It is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year.
+Since most years fit those conditions, it is overall the most efficient approach.
- The ternary operator has a maximum of only two checks, but it starts from a less likely condition.
-The ternary operator was faster in benchmarking when the year was a leap year or was evenly divisible by `100`.
-- Using `datetime` addition may be considered a "cheat" for the exercise, and it was slower than the ternary operator in benchmarking.
+The ternary operator was faster in benchmarking when the year was a leap year or was evenly divisible by `100`,
+but those are the least likely conditions.
+- Using `datetime` addition may be considered a "cheat" for the exercise, and it was slower than the other approaches in benchmarking.
For more information, check the [Performance article][article-performance].
From 207b8f7298088df6bd06850c488c86a458890524 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 10:15:14 -0600
Subject: [PATCH 125/932] Update content.md
---
exercises/practice/leap/.articles/performance/content.md | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/leap/.articles/performance/content.md b/exercises/practice/leap/.articles/performance/content.md
index 387de7902b8..ce5ead1ca22 100644
--- a/exercises/practice/leap/.articles/performance/content.md
+++ b/exercises/practice/leap/.articles/performance/content.md
@@ -25,9 +25,11 @@ ternary 2020: 1.0290379999787546e-07
datetime add 2019: 6.689728000201284e-07
```
-The boolean chain is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year.
-The ternary operator is faster in benchmarking when the year is a leap year or is evenly divisible by `100`.
-Adding the `datetime` may not only be a "cheat", but it is slower than the ternary operator.
+- The boolean chain is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year.
+Since most years fit those conditions, it is overall the most efficient approach.
+- The ternary operator is faster in benchmarking when the year is a leap year or is evenly divisible by `100`,
+but those are the least likely conditions.
+- Adding to the `datetime` may not only be a "cheat", but it is slower than the other approaches.
[approaches]: https://exercism.org/tracks/python/exercises/leap/approaches
[approach-boolean-chain]: https://exercism.org/tracks/python/exercises/leap/approaches/boolean-chain
From a87e359bc946dc368d9ca0e837683342cc114536 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 10:19:09 -0600
Subject: [PATCH 126/932] Update content.md
---
exercises/practice/leap/.approaches/boolean-chain/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/leap/.approaches/boolean-chain/content.md b/exercises/practice/leap/.approaches/boolean-chain/content.md
index c63b0c9b17e..4c0e24456c5 100644
--- a/exercises/practice/leap/.approaches/boolean-chain/content.md
+++ b/exercises/practice/leap/.approaches/boolean-chain/content.md
@@ -7,7 +7,7 @@ def leap_year(year):
```
The first boolean expression uses the [modulo operator][modulo-operator] to check if the year is evenly divided by `4`.
-- If the year is not evenly divisible by `4`, then the chain will "short circuit" due to the next operator being a [logical AND][logical-and] and`), and will return `False`.
+- If the year is not evenly divisible by `4`, then the chain will "short circuit" due to the next operator being a [logical AND][logical-and] {`and`), and will return `False`.
- If the year _is_ evenly divisible by `4`, then the year is checked to _not_ be evenly divisible by `100`.
- If the year is not evenly divisible by `100`, then the expression is `True` and the chain will "short-circuit" to return `True`,
since the next operator is a [logical OR][logical-or] (`or`).
From 543c6cbd5a7a9c914d881ca3cca13c5cc08cb1e2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 10:21:16 -0600
Subject: [PATCH 127/932] Update content.md
---
.../practice/leap/.approaches/ternary-operator/content.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/leap/.approaches/ternary-operator/content.md b/exercises/practice/leap/.approaches/ternary-operator/content.md
index b4a81f5a6a1..f6f8af30182 100644
--- a/exercises/practice/leap/.approaches/ternary-operator/content.md
+++ b/exercises/practice/leap/.approaches/ternary-operator/content.md
@@ -10,7 +10,7 @@ A [ternary operator][ternary-operator] uses a maximum of two checks to determine
It starts by testing the outlier condition of the year being evenly divisible by `100`.
It does this by using the [modulo operator][modulo-operator].
-And by using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+Also, by using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
- If the year is evenly divisible by `100`, then the expression is `True`, and the ternary operator returns if the year is evenly divisible by `400`.
- If the year is _not_ evenly divisible by `100`, then the expression is `False`, and the ternary operator returns if the year is evenly divisible by `4`.
@@ -25,7 +25,8 @@ And by using the [falsiness][falsiness] of `0`, the [`not` operator][not-operato
Although it uses a maximum of only two checks, the ternary operator tests an outlier condition first,
making it less efficient than another approach that would first test if the year is evenly divisible by `4`,
which is more likely than the year being evenly divisible by `100`.
-The ternary operator was fastest in benchmarking when the year was a leap year or was evenly divisible by 100.
+The ternary operator was fastest in benchmarking when the year was a leap year or was evenly divisible by `100`,
+but those are the least likely conditions.
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
[modulo-operator]: https://realpython.com/python-modulo-operator/
From 8b8d2c67de28c450642c09d9ecca09ca840447f6 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 15 Nov 2022 10:22:32 -0600
Subject: [PATCH 128/932] Update content.md
---
.../practice/leap/.approaches/datetime-addition/content.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/leap/.approaches/datetime-addition/content.md b/exercises/practice/leap/.approaches/datetime-addition/content.md
index 862abcfe1b3..3748fa47caf 100644
--- a/exercises/practice/leap/.approaches/datetime-addition/content.md
+++ b/exercises/practice/leap/.approaches/datetime-addition/content.md
@@ -14,10 +14,10 @@ def leap_year(year):
This approach may be considered a "cheat" for this exercise.
```
-By adding a day to February 28th for the `year`, you can see if the new day is the 29th or the 1st.
-If it is the 29th, then the function returns `True` for the `year` being a leap year.
+By adding a day to February 28th for the year, you can see if the new day is the 29th or the 1st.
+If it is the 29th, then the function returns `True` for the year being a leap year.
-- A new [datetime][datetime] object is created for Febuary 28th of the `year`.
+- A new [datetime][datetime] object is created for Febuary 28th of the year.
- Then the [timedelta][timedelta] of one day is added to that `datetime`,
and the function returns if the [day][day] property of the resulting `datetime` object is the 29th.
From cbfcfaa4ab5dd55ba8a0972bece29e56b8452693 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 17:59:05 -0600
Subject: [PATCH 129/932] Update content.md
---
exercises/practice/leap/.approaches/boolean-chain/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/leap/.approaches/boolean-chain/content.md b/exercises/practice/leap/.approaches/boolean-chain/content.md
index 4c0e24456c5..c909863fcaa 100644
--- a/exercises/practice/leap/.approaches/boolean-chain/content.md
+++ b/exercises/practice/leap/.approaches/boolean-chain/content.md
@@ -24,7 +24,7 @@ since the next operator is a [logical OR][logical-or] (`or`).
The chain of boolean expressions is efficient, as it proceeds from testing the most likely to least likely conditions.
It is the fastest approach when testing a year that is not evenly divisible by `100` and is not a leap year.
-## Shortening
+## Refactoring
By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
For example
From 16abd717721ee0a632385fa7666d4d32405610ef Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 03:53:21 -0600
Subject: [PATCH 130/932] Update content.md
---
exercises/practice/leap/.approaches/ternary-operator/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/leap/.approaches/ternary-operator/content.md b/exercises/practice/leap/.approaches/ternary-operator/content.md
index f6f8af30182..e20142ddc67 100644
--- a/exercises/practice/leap/.approaches/ternary-operator/content.md
+++ b/exercises/practice/leap/.approaches/ternary-operator/content.md
@@ -2,7 +2,7 @@
```python
def leap_year(year):
- return (not year % 400 if not year % 100 else not year % 4)
+ return not year % 400 if not year % 100 else not year % 4
```
From 4828855f8d21c0a1fc9b85e8e24569fbe0493b69 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 03:53:38 -0600
Subject: [PATCH 131/932] Update snippet.txt
---
.../practice/leap/.approaches/ternary-operator/snippet.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/leap/.approaches/ternary-operator/snippet.txt b/exercises/practice/leap/.approaches/ternary-operator/snippet.txt
index bc7d90c3f3d..037ba3d5027 100644
--- a/exercises/practice/leap/.approaches/ternary-operator/snippet.txt
+++ b/exercises/practice/leap/.approaches/ternary-operator/snippet.txt
@@ -1,2 +1,2 @@
def leap_year(year):
- return (not year % 400 if not year % 100 else not year % 4)
+ return not year % 400 if not year % 100 else not year % 4
From 5b2722ebf20fcf32d84a112e111e3aee3222efa6 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Sat, 19 Nov 2022 11:13:59 +0100
Subject: [PATCH 132/932] Sync docs with problem-specifications
Most of the docs changes recently have been formatting-related,
though in a few exercises there have also been improvements to
the descriptions in an attempt to make the exercises easier to
understand.
---
.../affine-cipher/.docs/instructions.md | 6 +-
.../all-your-base/.docs/instructions.md | 17 ++--
.../practice/allergies/.docs/instructions.md | 31 ++++---
.../alphametics/.docs/instructions.md | 11 ++-
.../armstrong-numbers/.docs/instructions.md | 4 +-
.../atbash-cipher/.docs/instructions.md | 15 ++--
.../bank-account/.docs/instructions.md | 16 ++--
.../practice/beer-song/.docs/instructions.md | 14 ----
.../binary-search-tree/.docs/instructions.md | 39 ++++-----
.../binary-search/.docs/instructions.md | 35 +++-----
exercises/practice/bob/.docs/instructions.md | 6 +-
.../practice/book-store/.docs/instructions.md | 15 +---
.../practice/bowling/.docs/instructions.md | 51 ++++++------
.../practice/change/.docs/instructions.md | 9 +--
.../circular-buffer/.docs/instructions.md | 37 ++++-----
.../collatz-conjecture/.docs/instructions.md | 12 +--
.../practice/connect/.docs/instructions.md | 26 +++---
.../crypto-square/.docs/instructions.md | 31 +++----
.../practice/custom-set/.docs/instructions.md | 7 +-
.../practice/darts/.docs/instructions.md | 22 +++--
.../practice/diamond/.docs/instructions.md | 27 +++----
.../.docs/instructions.md | 9 +--
.../diffie-hellman/.docs/instructions.md | 19 +++--
.../dnd-character/.docs/instructions.md | 36 ++++-----
.../practice/dominoes/.docs/instructions.md | 8 +-
.../practice/dot-dsl/.docs/instructions.md | 29 +++----
exercises/practice/etl/.docs/instructions.md | 23 ++----
.../flatten-array/.docs/instructions.md | 2 +-
.../practice/food-chain/.docs/instructions.md | 8 +-
.../practice/forth/.docs/instructions.md | 21 +++--
.../practice/gigasecond/.docs/instructions.md | 3 +-
.../go-counting/.docs/instructions.md | 29 +++----
.../grade-school/.docs/instructions.md | 36 ++-------
.../practice/grains/.docs/instructions.md | 23 ++----
exercises/practice/grep/.docs/instructions.md | 80 ++++---------------
.../practice/hamming/.docs/instructions.md | 16 ++--
.../practice/hangman/.docs/instructions.md | 16 ++--
.../hello-world/.docs/instructions.md | 9 ++-
.../practice/house/.docs/instructions.md | 13 ++-
.../isbn-verifier/.docs/instructions.md | 20 ++---
.../kindergarten-garden/.docs/instructions.md | 18 ++---
.../practice/knapsack/.docs/instructions.md | 29 +++----
.../.docs/instructions.md | 10 +--
exercises/practice/leap/.docs/instructions.md | 12 ++-
.../practice/ledger/.docs/instructions.md | 17 ++--
.../linked-list/.docs/instructions.md | 46 +++++------
.../practice/list-ops/.docs/instructions.md | 25 +++---
exercises/practice/luhn/.docs/instructions.md | 24 +++---
.../practice/markdown/.docs/instructions.md | 20 +++--
.../matching-brackets/.docs/instructions.md | 4 +-
.../practice/matrix/.docs/instructions.md | 9 +--
.../practice/meetup/.docs/instructions.md | 54 ++++++++++---
.../minesweeper/.docs/instructions.md | 15 ++--
.../practice/nth-prime/.docs/instructions.md | 6 +-
.../ocr-numbers/.docs/instructions.md | 8 +-
.../practice/paasio/.docs/instructions.md | 9 +--
.../palindrome-products/.docs/instructions.md | 21 ++---
.../practice/pangram/.docs/instructions.md | 8 +-
.../perfect-numbers/.docs/instructions.md | 10 ++-
.../phone-number/.docs/instructions.md | 6 +-
.../practice/pig-latin/.docs/instructions.md | 19 ++---
.../practice/poker/.docs/instructions.md | 5 +-
exercises/practice/pov/.docs/instructions.md | 25 +++---
.../prime-factors/.docs/instructions.md | 26 +++---
.../protein-translation/.docs/instructions.md | 7 +-
.../pythagorean-triplet/.docs/instructions.md | 5 +-
.../rail-fence-cipher/.docs/instructions.md | 10 +--
.../practice/raindrops/.docs/instructions.md | 6 +-
.../rational-numbers/.docs/instructions.md | 9 ++-
.../practice/react/.docs/instructions.md | 15 ++--
.../practice/rectangles/.docs/instructions.md | 3 +-
.../resistor-color-duo/.docs/instructions.md | 24 +++---
.../resistor-color/.docs/instructions.md | 7 +-
.../practice/rest-api/.docs/instructions.md | 14 +++-
.../rna-transcription/.docs/instructions.md | 17 ++--
.../practice/robot-name/.docs/instructions.md | 12 ++-
.../robot-simulator/.docs/instructions.md | 11 +--
.../roman-numerals/.docs/instructions.md | 30 ++++---
.../rotational-cipher/.docs/instructions.md | 8 +-
.../run-length-encoding/.docs/instructions.md | 12 +--
.../saddle-points/.docs/instructions.md | 10 +--
.../practice/satellite/.docs/instructions.md | 14 ++--
exercises/practice/say/.docs/instructions.md | 17 ++--
.../scale-generator/.docs/instructions.md | 57 +++++--------
.../secret-handshake/.docs/instructions.md | 7 +-
.../practice/series/.docs/instructions.md | 10 +--
.../sgf-parsing/.docs/instructions.md | 29 +++++--
.../practice/sieve/.docs/instructions.md | 22 +++--
.../simple-cipher/.docs/instructions.md | 69 +++++++---------
.../simple-linked-list/.docs/instructions.md | 21 ++---
.../practice/space-age/.docs/instructions.md | 5 +-
.../spiral-matrix/.docs/instructions.md | 4 +-
.../sum-of-multiples/.docs/instructions.md | 6 +-
.../practice/tournament/.docs/instructions.md | 13 +--
.../practice/transpose/.docs/instructions.md | 8 +-
.../tree-building/.docs/instructions.md | 17 ++--
.../practice/triangle/.docs/instructions.md | 16 ++--
.../twelve-days/.docs/instructions.md | 3 +-
.../practice/two-bucket/.docs/instructions.md | 15 +++-
.../practice/two-fer/.docs/instructions.md | 3 +-
.../.docs/instructions.md | 6 +-
.../practice/word-count/.docs/instructions.md | 2 +-
.../word-search/.docs/instructions.md | 9 +--
.../practice/wordy/.docs/instructions.md | 17 +---
.../practice/yacht/.docs/instructions.md | 23 +++---
.../zebra-puzzle/.docs/instructions.md | 4 +-
.../practice/zipper/.docs/instructions.md | 11 ++-
107 files changed, 827 insertions(+), 1038 deletions(-)
diff --git a/exercises/practice/affine-cipher/.docs/instructions.md b/exercises/practice/affine-cipher/.docs/instructions.md
index 6855736fa79..2ad6d152158 100644
--- a/exercises/practice/affine-cipher/.docs/instructions.md
+++ b/exercises/practice/affine-cipher/.docs/instructions.md
@@ -6,7 +6,7 @@ The affine cipher is a type of monoalphabetic substitution cipher.
Each character is mapped to its numeric equivalent, encrypted with a mathematical function and then converted to the letter relating to its new numeric value.
Although all monoalphabetic ciphers are weak, the affine cipher is much stronger than the atbash cipher, because it has many more keys.
-[comment]: # ( monoalphabetic as spelled by Merriam-Webster, compare to polyalphabetic )
+[//]: # ( monoalphabetic as spelled by Merriam-Webster, compare to polyalphabetic )
## Encryption
@@ -52,7 +52,7 @@ The MMI of `a` is `x` such that the remainder after dividing `ax` by `m` is `1`:
ax mod m = 1
```
-More information regarding how to find a Modular Multiplicative Inverse and what it means can be found in the [related Wikipedia article][MMI].
+More information regarding how to find a Modular Multiplicative Inverse and what it means can be found in the [related Wikipedia article][mmi].
## General Examples
@@ -70,5 +70,5 @@ Finding MMI for `a = 15`:
- `(15 * 7) mod 26 = 1`, ie. `105 mod 26 = 1`
- `7` is the MMI of `15 mod 26`
-[MMI]: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
+[mmi]: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
[coprime-integers]: https://en.wikipedia.org/wiki/Coprime_integers
diff --git a/exercises/practice/all-your-base/.docs/instructions.md b/exercises/practice/all-your-base/.docs/instructions.md
index 2de87cffd73..d5a2cde652e 100644
--- a/exercises/practice/all-your-base/.docs/instructions.md
+++ b/exercises/practice/all-your-base/.docs/instructions.md
@@ -2,31 +2,32 @@
Convert a number, represented as a sequence of digits in one base, to any other base.
-Implement general base conversion. Given a number in base **a**,
-represented as a sequence of digits, convert it to base **b**.
+Implement general base conversion.
+Given a number in base **a**, represented as a sequence of digits, convert it to base **b**.
## Note
- Try to implement the conversion yourself.
Do not use something else to perform the conversion for you.
-## About [Positional Notation](https://en.wikipedia.org/wiki/Positional_notation)
+## About [Positional Notation][positional-notation]
-In positional notation, a number in base **b** can be understood as a linear
-combination of powers of **b**.
+In positional notation, a number in base **b** can be understood as a linear combination of powers of **b**.
The number 42, *in base 10*, means:
-(4 \* 10^1) + (2 \* 10^0)
+`(4 * 10^1) + (2 * 10^0)`
The number 101010, *in base 2*, means:
-(1 \* 2^5) + (0 \* 2^4) + (1 \* 2^3) + (0 \* 2^2) + (1 \* 2^1) + (0 \* 2^0)
+`(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)`
The number 1120, *in base 3*, means:
-(1 \* 3^3) + (1 \* 3^2) + (2 \* 3^1) + (0 \* 3^0)
+`(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)`
I think you got the idea!
*Yes. Those three numbers above are exactly the same. Congratulations!*
+
+[positional-notation]: https://en.wikipedia.org/wiki/Positional_notation
diff --git a/exercises/practice/allergies/.docs/instructions.md b/exercises/practice/allergies/.docs/instructions.md
index e89b869721c..a1394920968 100644
--- a/exercises/practice/allergies/.docs/instructions.md
+++ b/exercises/practice/allergies/.docs/instructions.md
@@ -2,29 +2,26 @@
Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies.
-An allergy test produces a single numeric score which contains the
-information about all the allergies the person has (that they were
-tested for).
+An allergy test produces a single numeric score which contains the information about all the allergies the person has (that they were tested for).
The list of items (and their value) that were tested are:
-* eggs (1)
-* peanuts (2)
-* shellfish (4)
-* strawberries (8)
-* tomatoes (16)
-* chocolate (32)
-* pollen (64)
-* cats (128)
+- eggs (1)
+- peanuts (2)
+- shellfish (4)
+- strawberries (8)
+- tomatoes (16)
+- chocolate (32)
+- pollen (64)
+- cats (128)
So if Tom is allergic to peanuts and chocolate, he gets a score of 34.
Now, given just that score of 34, your program should be able to say:
-* Whether Tom is allergic to any one of those allergens listed above.
-* All the allergens Tom is allergic to.
+- Whether Tom is allergic to any one of those allergens listed above.
+- All the allergens Tom is allergic to.
-Note: a given score may include allergens **not** listed above (i.e.
-allergens that score 256, 512, 1024, etc.). Your program should
-ignore those components of the score. For example, if the allergy
-score is 257, your program should only report the eggs (1) allergy.
+Note: a given score may include allergens **not** listed above (i.e. allergens that score 256, 512, 1024, etc.).
+Your program should ignore those components of the score.
+For example, if the allergy score is 257, your program should only report the eggs (1) allergy.
diff --git a/exercises/practice/alphametics/.docs/instructions.md b/exercises/practice/alphametics/.docs/instructions.md
index 6936c192d56..649576ec7e4 100644
--- a/exercises/practice/alphametics/.docs/instructions.md
+++ b/exercises/practice/alphametics/.docs/instructions.md
@@ -2,8 +2,7 @@
Write a function to solve alphametics puzzles.
-[Alphametics](https://en.wikipedia.org/wiki/Alphametics) is a puzzle where
-letters in words are replaced with numbers.
+[Alphametics][alphametics] is a puzzle where letters in words are replaced with numbers.
For example `SEND + MORE = MONEY`:
@@ -23,10 +22,10 @@ Replacing these with valid numbers gives:
1 0 6 5 2
```
-This is correct because every letter is replaced by a different number and the
-words, translated into numbers, then make a valid sum.
+This is correct because every letter is replaced by a different number and the words, translated into numbers, then make a valid sum.
-Each letter must represent a different digit, and the leading digit of
-a multi-digit number must not be zero.
+Each letter must represent a different digit, and the leading digit of a multi-digit number must not be zero.
Write a function to solve alphametics puzzles.
+
+[alphametics]: https://en.wikipedia.org/wiki/Alphametics
diff --git a/exercises/practice/armstrong-numbers/.docs/instructions.md b/exercises/practice/armstrong-numbers/.docs/instructions.md
index 452a996fb18..744cfbe7fa6 100644
--- a/exercises/practice/armstrong-numbers/.docs/instructions.md
+++ b/exercises/practice/armstrong-numbers/.docs/instructions.md
@@ -1,6 +1,6 @@
# Instructions
-An [Armstrong number](https://en.wikipedia.org/wiki/Narcissistic_number) is a number that is the sum of its own digits each raised to the power of the number of digits.
+An [Armstrong number][armstrong-number] is a number that is the sum of its own digits each raised to the power of the number of digits.
For example:
@@ -10,3 +10,5 @@ For example:
- 154 is *not* an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190`
Write some code to determine whether a number is an Armstrong number.
+
+[armstrong-number]: https://en.wikipedia.org/wiki/Narcissistic_number
diff --git a/exercises/practice/atbash-cipher/.docs/instructions.md b/exercises/practice/atbash-cipher/.docs/instructions.md
index 328f7cbd14d..21ca2ce0aa8 100644
--- a/exercises/practice/atbash-cipher/.docs/instructions.md
+++ b/exercises/practice/atbash-cipher/.docs/instructions.md
@@ -2,10 +2,8 @@
Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.
-The Atbash cipher is a simple substitution cipher that relies on
-transposing all the letters in the alphabet such that the resulting
-alphabet is backwards. The first letter is replaced with the last
-letter, the second with the second-last, and so on.
+The Atbash cipher is a simple substitution cipher that relies on transposing all the letters in the alphabet such that the resulting alphabet is backwards.
+The first letter is replaced with the last letter, the second with the second-last, and so on.
An Atbash cipher for the Latin alphabet would be as follows:
@@ -14,13 +12,12 @@ Plain: abcdefghijklmnopqrstuvwxyz
Cipher: zyxwvutsrqponmlkjihgfedcba
```
-It is a very weak cipher because it only has one possible key, and it is
-a simple mono-alphabetic substitution cipher. However, this may not have
-been an issue in the cipher's time.
+It is a very weak cipher because it only has one possible key, and it is a simple mono-alphabetic substitution cipher.
+However, this may not have been an issue in the cipher's time.
-Ciphertext is written out in groups of fixed length, the traditional group size
-being 5 letters, leaving numbers unchanged, and punctuation is excluded.
+Ciphertext is written out in groups of fixed length, the traditional group size being 5 letters, leaving numbers unchanged, and punctuation is excluded.
This is to make it harder to guess things based on word boundaries.
+All text will be encoded as lowercase letters.
## Examples
diff --git a/exercises/practice/bank-account/.docs/instructions.md b/exercises/practice/bank-account/.docs/instructions.md
index d1bb8af0f73..f536fdbb735 100644
--- a/exercises/practice/bank-account/.docs/instructions.md
+++ b/exercises/practice/bank-account/.docs/instructions.md
@@ -1,14 +1,12 @@
# Instructions
-Simulate a bank account supporting opening/closing, withdrawals, and deposits
-of money. Watch out for concurrent transactions!
+Simulate a bank account supporting opening/closing, withdrawals, and deposits of money.
+Watch out for concurrent transactions!
-A bank account can be accessed in multiple ways. Clients can make
-deposits and withdrawals using the internet, mobile phones, etc. Shops
-can charge against the account.
+A bank account can be accessed in multiple ways.
+Clients can make deposits and withdrawals using the internet, mobile phones, etc.
+Shops can charge against the account.
-Create an account that can be accessed from multiple threads/processes
-(terminology depends on your programming language).
+Create an account that can be accessed from multiple threads/processes (terminology depends on your programming language).
-It should be possible to close an account; operations against a closed
-account must fail.
+It should be possible to close an account; operations against a closed account must fail.
diff --git a/exercises/practice/beer-song/.docs/instructions.md b/exercises/practice/beer-song/.docs/instructions.md
index 57429d8ab01..e909cfe3178 100644
--- a/exercises/practice/beer-song/.docs/instructions.md
+++ b/exercises/practice/beer-song/.docs/instructions.md
@@ -305,17 +305,3 @@ Take it down and pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
```
-
-## For bonus points
-
-Did you get the tests passing and the code clean? If you want to, these
-are some additional things you could try:
-
-* Remove as much duplication as you possibly can.
-* Optimize for readability, even if it means introducing duplication.
-* If you've removed all the duplication, do you have a lot of
- conditionals? Try replacing the conditionals with polymorphism, if it
- applies in this language. How readable is it?
-
-Then please share your thoughts in a comment on the submission. Did this
-experiment make the code better? Worse? Did you learn anything from it?
diff --git a/exercises/practice/binary-search-tree/.docs/instructions.md b/exercises/practice/binary-search-tree/.docs/instructions.md
index ba3c42eb673..c9bbba5b96d 100644
--- a/exercises/practice/binary-search-tree/.docs/instructions.md
+++ b/exercises/practice/binary-search-tree/.docs/instructions.md
@@ -2,29 +2,22 @@
Insert and search for numbers in a binary tree.
-When we need to represent sorted data, an array does not make a good
-data structure.
-
-Say we have the array `[1, 3, 4, 5]`, and we add 2 to it so it becomes
-`[1, 3, 4, 5, 2]` now we must sort the entire array again! We can
-improve on this by realizing that we only need to make space for the new
-item `[1, nil, 3, 4, 5]`, and then adding the item in the space we
-added. But this still requires us to shift many elements down by one.
-
-Binary Search Trees, however, can operate on sorted data much more
-efficiently.
-
-A binary search tree consists of a series of connected nodes. Each node
-contains a piece of data (e.g. the number 3), a variable named `left`,
-and a variable named `right`. The `left` and `right` variables point at
-`nil`, or other nodes. Since these other nodes in turn have other nodes
-beneath them, we say that the left and right variables are pointing at
-subtrees. All data in the left subtree is less than or equal to the
-current node's data, and all data in the right subtree is greater than
-the current node's data.
-
-For example, if we had a node containing the data 4, and we added the
-data 2, our tree would look like this:
+When we need to represent sorted data, an array does not make a good data structure.
+
+Say we have the array `[1, 3, 4, 5]`, and we add 2 to it so it becomes `[1, 3, 4, 5, 2]`.
+Now we must sort the entire array again!
+We can improve on this by realizing that we only need to make space for the new item `[1, nil, 3, 4, 5]`, and then adding the item in the space we added.
+But this still requires us to shift many elements down by one.
+
+Binary Search Trees, however, can operate on sorted data much more efficiently.
+
+A binary search tree consists of a series of connected nodes.
+Each node contains a piece of data (e.g. the number 3), a variable named `left`, and a variable named `right`.
+The `left` and `right` variables point at `nil`, or other nodes.
+Since these other nodes in turn have other nodes beneath them, we say that the left and right variables are pointing at subtrees.
+All data in the left subtree is less than or equal to the current node's data, and all data in the right subtree is greater than the current node's data.
+
+For example, if we had a node containing the data 4, and we added the data 2, our tree would look like this:
4
/
diff --git a/exercises/practice/binary-search/.docs/instructions.md b/exercises/practice/binary-search/.docs/instructions.md
index 4dcaba726a6..b67705406fe 100644
--- a/exercises/practice/binary-search/.docs/instructions.md
+++ b/exercises/practice/binary-search/.docs/instructions.md
@@ -2,34 +2,23 @@
Implement a binary search algorithm.
-Searching a sorted collection is a common task. A dictionary is a sorted
-list of word definitions. Given a word, one can find its definition. A
-telephone book is a sorted list of people's names, addresses, and
-telephone numbers. Knowing someone's name allows one to quickly find
-their telephone number and address.
+Searching a sorted collection is a common task.
+A dictionary is a sorted list of word definitions.
+Given a word, one can find its definition.
+A telephone book is a sorted list of people's names, addresses, and telephone numbers.
+Knowing someone's name allows one to quickly find their telephone number and address.
-If the list to be searched contains more than a few items (a dozen, say)
-a binary search will require far fewer comparisons than a linear search,
-but it imposes the requirement that the list be sorted.
+If the list to be searched contains more than a few items (a dozen, say) a binary search will require far fewer comparisons than a linear search, but it imposes the requirement that the list be sorted.
-In computer science, a binary search or half-interval search algorithm
-finds the position of a specified input value (the search "key") within
-an array sorted by key value.
+In computer science, a binary search or half-interval search algorithm finds the position of a specified input value (the search "key") within an array sorted by key value.
-In each step, the algorithm compares the search key value with the key
-value of the middle element of the array.
+In each step, the algorithm compares the search key value with the key value of the middle element of the array.
-If the keys match, then a matching element has been found and its index,
-or position, is returned.
+If the keys match, then a matching element has been found and its index, or position, is returned.
-Otherwise, if the search key is less than the middle element's key, then
-the algorithm repeats its action on the sub-array to the left of the
-middle element or, if the search key is greater, on the sub-array to the
-right.
+Otherwise, if the search key is less than the middle element's key, then the algorithm repeats its action on the sub-array to the left of the middle element or, if the search key is greater, on the sub-array to the right.
-If the remaining array to be searched is empty, then the key cannot be
-found in the array and a special "not found" indication is returned.
+If the remaining array to be searched is empty, then the key cannot be found in the array and a special "not found" indication is returned.
-A binary search halves the number of items to check with each iteration,
-so locating an item (or determining its absence) takes logarithmic time.
+A binary search halves the number of items to check with each iteration, so locating an item (or determining its absence) takes logarithmic time.
A binary search is a dichotomic divide and conquer search algorithm.
diff --git a/exercises/practice/bob/.docs/instructions.md b/exercises/practice/bob/.docs/instructions.md
index edddb1413dc..7888c9b76f4 100644
--- a/exercises/practice/bob/.docs/instructions.md
+++ b/exercises/practice/bob/.docs/instructions.md
@@ -1,6 +1,7 @@
# Instructions
-Bob is a lackadaisical teenager. In conversation, his responses are very limited.
+Bob is a lackadaisical teenager.
+In conversation, his responses are very limited.
Bob answers 'Sure.' if you ask him a question, such as "How are you?".
@@ -8,8 +9,7 @@ He answers 'Whoa, chill out!' if you YELL AT HIM (in all capitals).
He answers 'Calm down, I know what I'm doing!' if you yell a question at him.
-He says 'Fine. Be that way!' if you address him without actually saying
-anything.
+He says 'Fine. Be that way!' if you address him without actually saying anything.
He answers 'Whatever.' to anything else.
diff --git a/exercises/practice/book-store/.docs/instructions.md b/exercises/practice/book-store/.docs/instructions.md
index cce9deea01b..341ad01fbd2 100644
--- a/exercises/practice/book-store/.docs/instructions.md
+++ b/exercises/practice/book-store/.docs/instructions.md
@@ -1,12 +1,10 @@
# Instructions
-To try and encourage more sales of different books from a popular 5 book
-series, a bookshop has decided to offer discounts on multiple book purchases.
+To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts on multiple book purchases.
One copy of any of the five books costs $8.
-If, however, you buy two different books, you get a 5%
-discount on those two books.
+If, however, you buy two different books, you get a 5% discount on those two books.
If you buy 3 different books, you get a 10% discount.
@@ -14,14 +12,9 @@ If you buy 4 different books, you get a 20% discount.
If you buy all 5, you get a 25% discount.
-Note: that if you buy four books, of which 3 are
-different titles, you get a 10% discount on the 3 that
-form part of a set, but the fourth book still costs $8.
+Note: that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs $8.
-Your mission is to write a piece of code to calculate the
-price of any conceivable shopping basket (containing only
-books of the same series), giving as big a discount as
-possible.
+Your mission is to write a piece of code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible.
For example, how much does this basket of books cost?
diff --git a/exercises/practice/bowling/.docs/instructions.md b/exercises/practice/bowling/.docs/instructions.md
index be9b27faf00..48a2fedcf42 100644
--- a/exercises/practice/bowling/.docs/instructions.md
+++ b/exercises/practice/bowling/.docs/instructions.md
@@ -2,29 +2,24 @@
Score a bowling game.
-Bowling is a game where players roll a heavy ball to knock down pins
-arranged in a triangle. Write code to keep track of the score
-of a game of bowling.
+Bowling is a game where players roll a heavy ball to knock down pins arranged in a triangle.
+Write code to keep track of the score of a game of bowling.
## Scoring Bowling
-The game consists of 10 frames. A frame is composed of one or two ball
-throws with 10 pins standing at frame initialization. There are three
-cases for the tabulation of a frame.
+The game consists of 10 frames.
+A frame is composed of one or two ball throws with 10 pins standing at frame initialization.
+There are three cases for the tabulation of a frame.
-* An open frame is where a score of less than 10 is recorded for the
- frame. In this case the score for the frame is the number of pins
- knocked down.
+- An open frame is where a score of less than 10 is recorded for the frame.
+ In this case the score for the frame is the number of pins knocked down.
-* A spare is where all ten pins are knocked down by the second
- throw. The total value of a spare is 10 plus the number of pins
- knocked down in their next throw.
+- A spare is where all ten pins are knocked down by the second throw.
+ The total value of a spare is 10 plus the number of pins knocked down in their next throw.
-* A strike is where all ten pins are knocked down by the first
- throw. The total value of a strike is 10 plus the number of pins
- knocked down in the next two throws. If a strike is immediately
- followed by a second strike, then the value of the first strike
- cannot be determined until the ball is thrown one more time.
+- A strike is where all ten pins are knocked down by the first throw.
+ The total value of a strike is 10 plus the number of pins knocked down in the next two throws.
+ If a strike is immediately followed by a second strike, then the value of the first strike cannot be determined until the ball is thrown one more time.
Here is a three frame example:
@@ -40,11 +35,11 @@ Frame 3 is (9 + 0) = 9
This means the current running total is 48.
-The tenth frame in the game is a special case. If someone throws a
-strike or a spare then they get a fill ball. Fill balls exist to
-calculate the total of the 10th frame. Scoring a strike or spare on
-the fill ball does not give the player more fill balls. The total
-value of the 10th frame is the total number of pins knocked down.
+The tenth frame in the game is a special case.
+If someone throws a strike or a spare then they get a fill ball.
+Fill balls exist to calculate the total of the 10th frame.
+Scoring a strike or spare on the fill ball does not give the player more fill balls.
+The total value of the 10th frame is the total number of pins knocked down.
For a tenth frame of X1/ (strike and a spare), the total value is 20.
@@ -52,10 +47,10 @@ For a tenth frame of XXX (three strikes), the total value is 30.
## Requirements
-Write code to keep track of the score of a game of bowling. It should
-support two operations:
+Write code to keep track of the score of a game of bowling.
+It should support two operations:
-* `roll(pins : int)` is called each time the player rolls a ball. The
- argument is the number of pins knocked down.
-* `score() : int` is called only at the very end of the game. It
- returns the total score for that game.
+- `roll(pins : int)` is called each time the player rolls a ball.
+ The argument is the number of pins knocked down.
+- `score() : int` is called only at the very end of the game.
+ It returns the total score for that game.
diff --git a/exercises/practice/change/.docs/instructions.md b/exercises/practice/change/.docs/instructions.md
index 59f4f4f90d9..30fa567750e 100644
--- a/exercises/practice/change/.docs/instructions.md
+++ b/exercises/practice/change/.docs/instructions.md
@@ -1,14 +1,11 @@
# Instructions
-Correctly determine the fewest number of coins to be given to a customer such
-that the sum of the coins' value would equal the correct amount of change.
+Correctly determine the fewest number of coins to be given to a customer such that the sum of the coins' value would equal the correct amount of change.
## For example
-- An input of 15 with [1, 5, 10, 25, 100] should return one nickel (5)
- and one dime (10) or [5, 10]
-- An input of 40 with [1, 5, 10, 25, 100] should return one nickel (5)
- and one dime (10) and one quarter (25) or [5, 10, 25]
+- An input of 15 with [1, 5, 10, 25, 100] should return one nickel (5) and one dime (10) or [5, 10]
+- An input of 40 with [1, 5, 10, 25, 100] should return one nickel (5) and one dime (10) and one quarter (25) or [5, 10, 25]
## Edge cases
diff --git a/exercises/practice/circular-buffer/.docs/instructions.md b/exercises/practice/circular-buffer/.docs/instructions.md
index e9b00b91dc9..3487a0f6141 100644
--- a/exercises/practice/circular-buffer/.docs/instructions.md
+++ b/exercises/practice/circular-buffer/.docs/instructions.md
@@ -1,26 +1,22 @@
# Instructions
-A circular buffer, cyclic buffer or ring buffer is a data structure that
-uses a single, fixed-size buffer as if it were connected end-to-end.
+A circular buffer, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end.
-A circular buffer first starts empty and of some predefined length. For
-example, this is a 7-element buffer:
+A circular buffer first starts empty and of some predefined length.
+For example, this is a 7-element buffer:
[ ][ ][ ][ ][ ][ ][ ]
-Assume that a 1 is written into the middle of the buffer (exact starting
-location does not matter in a circular buffer):
+Assume that a 1 is written into the middle of the buffer (exact starting location does not matter in a circular buffer):
[ ][ ][ ][1][ ][ ][ ]
-Then assume that two more elements are added — 2 & 3 — which get
-appended after the 1:
+Then assume that two more elements are added — 2 & 3 — which get appended after the 1:
[ ][ ][ ][1][2][3][ ]
-If two elements are then removed from the buffer, the oldest values
-inside the buffer are removed. The two elements removed, in this case,
-are 1 & 2, leaving the buffer with just a 3:
+If two elements are then removed from the buffer, the oldest values inside the buffer are removed.
+The two elements removed, in this case, are 1 & 2, leaving the buffer with just a 3:
[ ][ ][ ][ ][ ][3][ ]
@@ -28,24 +24,19 @@ If the buffer has 7 elements then it is completely full:
[5][6][7][8][9][3][4]
-When the buffer is full an error will be raised, alerting the client
-that further writes are blocked until a slot becomes free.
+When the buffer is full an error will be raised, alerting the client that further writes are blocked until a slot becomes free.
-When the buffer is full, the client can opt to overwrite the oldest
-data with a forced write. In this case, two more elements — A & B —
-are added and they overwrite the 3 & 4:
+When the buffer is full, the client can opt to overwrite the oldest data with a forced write.
+In this case, two more elements — A & B — are added and they overwrite the 3 & 4:
[5][6][7][8][9][A][B]
-3 & 4 have been replaced by A & B making 5 now the oldest data in the
-buffer. Finally, if two elements are removed then what would be
-returned is 5 & 6 yielding the buffer:
+3 & 4 have been replaced by A & B making 5 now the oldest data in the buffer.
+Finally, if two elements are removed then what would be returned is 5 & 6 yielding the buffer:
[ ][ ][7][8][9][A][B]
-Because there is space available, if the client again uses overwrite
-to store C & D then the space where 5 & 6 were stored previously will
-be used not the location of 7 & 8. 7 is still the oldest element and
-the buffer is once again full.
+Because there is space available, if the client again uses overwrite to store C & D then the space where 5 & 6 were stored previously will be used not the location of 7 & 8.
+7 is still the oldest element and the buffer is once again full.
[C][D][7][8][9][A][B]
diff --git a/exercises/practice/collatz-conjecture/.docs/instructions.md b/exercises/practice/collatz-conjecture/.docs/instructions.md
index f8c76e7f11e..ba060483e4d 100644
--- a/exercises/practice/collatz-conjecture/.docs/instructions.md
+++ b/exercises/practice/collatz-conjecture/.docs/instructions.md
@@ -2,10 +2,11 @@
The Collatz Conjecture or 3x+1 problem can be summarized as follows:
-Take any positive integer n. If n is even, divide n by 2 to get n / 2. If n is
-odd, multiply n by 3 and add 1 to get 3n + 1. Repeat the process indefinitely.
-The conjecture states that no matter which number you start with, you will
-always reach 1 eventually.
+Take any positive integer n.
+If n is even, divide n by 2 to get n / 2.
+If n is odd, multiply n by 3 and add 1 to get 3n + 1.
+Repeat the process indefinitely.
+The conjecture states that no matter which number you start with, you will always reach 1 eventually.
Given a number n, return the number of steps required to reach 1.
@@ -24,4 +25,5 @@ Starting with n = 12, the steps would be as follows:
8. 2
9. 1
-Resulting in 9 steps. So for input n = 12, the return value would be 9.
+Resulting in 9 steps.
+So for input n = 12, the return value would be 9.
diff --git a/exercises/practice/connect/.docs/instructions.md b/exercises/practice/connect/.docs/instructions.md
index 2fa003a8350..7f34bfa817f 100644
--- a/exercises/practice/connect/.docs/instructions.md
+++ b/exercises/practice/connect/.docs/instructions.md
@@ -2,19 +2,14 @@
Compute the result for a game of Hex / Polygon.
-The abstract boardgame known as
-[Hex](https://en.wikipedia.org/wiki/Hex_%28board_game%29) / Polygon /
-CON-TAC-TIX is quite simple in rules, though complex in practice. Two players
-place stones on a parallelogram with hexagonal fields. The player to connect his/her
-stones to the opposite side first wins. The four sides of the parallelogram are
-divided between the two players (i.e. one player gets assigned a side and the
-side directly opposite it and the other player gets assigned the two other
-sides).
+The abstract boardgame known as [Hex][hex] / Polygon / CON-TAC-TIX is quite simple in rules, though complex in practice.
+Two players place stones on a parallelogram with hexagonal fields.
+The player to connect his/her stones to the opposite side first wins.
+The four sides of the parallelogram are divided between the two players (i.e. one player gets assigned a side and the side directly opposite it and the other player gets assigned the two other sides).
-Your goal is to build a program that given a simple representation of a board
-computes the winner (or lack thereof). Note that all games need not be "fair".
-(For example, players may have mismatched piece counts or the game's board might
-have a different width and height.)
+Your goal is to build a program that given a simple representation of a board computes the winner (or lack thereof).
+Note that all games need not be "fair".
+(For example, players may have mismatched piece counts or the game's board might have a different width and height.)
The boards look like this:
@@ -26,6 +21,7 @@ The boards look like this:
X O O O X
```
-"Player `O`" plays from top to bottom, "Player `X`" plays from left to right. In
-the above example `O` has made a connection from left to right but nobody has
-won since `O` didn't connect top and bottom.
+"Player `O`" plays from top to bottom, "Player `X`" plays from left to right.
+In the above example `O` has made a connection from left to right but nobody has won since `O` didn't connect top and bottom.
+
+[hex]: https://en.wikipedia.org/wiki/Hex_%28board_game%29
diff --git a/exercises/practice/crypto-square/.docs/instructions.md b/exercises/practice/crypto-square/.docs/instructions.md
index 8a489f6e2cc..6c3826ee558 100644
--- a/exercises/practice/crypto-square/.docs/instructions.md
+++ b/exercises/practice/crypto-square/.docs/instructions.md
@@ -4,11 +4,10 @@ Implement the classic method for composing secret messages called a square code.
Given an English text, output the encoded version of that text.
-First, the input is normalized: the spaces and punctuation are removed
-from the English text and the message is down-cased.
+First, the input is normalized: the spaces and punctuation are removed from the English text and the message is down-cased.
-Then, the normalized characters are broken into rows. These rows can be
-regarded as forming a rectangle when printed with intervening newlines.
+Then, the normalized characters are broken into rows.
+These rows can be regarded as forming a rectangle when printed with intervening newlines.
For example, the sentence
@@ -22,18 +21,16 @@ is normalized to:
"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots"
```
-The plaintext should be organized in to a rectangle. The size of the
-rectangle should be decided by the length of the message.
+The plaintext should be organized into a rectangle as square as possible.
+The size of the rectangle should be decided by the length of the message.
-If `c` is the number of columns and `r` is the number of rows, then for
-the rectangle `r` x `c` find the smallest possible integer `c` such that:
+If `c` is the number of columns and `r` is the number of rows, then for the rectangle `r` x `c` find the smallest possible integer `c` such that:
-- `r * c >= length(message)`,
+- `r * c >= length of message`,
- and `c >= r`,
- and `c - r <= 1`.
-Our normalized text is 54 characters long, dictating a rectangle with
-`c = 8` and `r = 7`:
+Our normalized text is 54 characters long, dictating a rectangle with `c = 8` and `r = 7`:
```text
"ifmanwas"
@@ -45,8 +42,7 @@ Our normalized text is 54 characters long, dictating a rectangle with
"sroots "
```
-The coded message is obtained by reading down the columns going left to
-right.
+The coded message is obtained by reading down the columns going left to right.
The message above is coded as:
@@ -54,17 +50,14 @@ The message above is coded as:
"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"
```
-Output the encoded text in chunks that fill perfect rectangles `(r X c)`,
-with `c` chunks of `r` length, separated by spaces. For phrases that are
-`n` characters short of the perfect rectangle, pad each of the last `n`
-chunks with a single trailing space.
+Output the encoded text in chunks that fill perfect rectangles `(r X c)`, with `c` chunks of `r` length, separated by spaces.
+For phrases that are `n` characters short of the perfect rectangle, pad each of the last `n` chunks with a single trailing space.
```text
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
```
-Notice that were we to stack these, we could visually decode the
-ciphertext back in to the original message:
+Notice that were we to stack these, we could visually decode the ciphertext back in to the original message:
```text
"imtgdvs"
diff --git a/exercises/practice/custom-set/.docs/instructions.md b/exercises/practice/custom-set/.docs/instructions.md
index e4931b058b6..33b90e28d7f 100644
--- a/exercises/practice/custom-set/.docs/instructions.md
+++ b/exercises/practice/custom-set/.docs/instructions.md
@@ -2,7 +2,6 @@
Create a custom set type.
-Sometimes it is necessary to define a custom data structure of some
-type, like a set. In this exercise you will define your own set. How it
-works internally doesn't matter, as long as it behaves like a set of
-unique elements.
+Sometimes it is necessary to define a custom data structure of some type, like a set.
+In this exercise you will define your own set.
+How it works internally doesn't matter, as long as it behaves like a set of unique elements.
diff --git a/exercises/practice/darts/.docs/instructions.md b/exercises/practice/darts/.docs/instructions.md
index b2cddf39156..7af7428a06c 100644
--- a/exercises/practice/darts/.docs/instructions.md
+++ b/exercises/practice/darts/.docs/instructions.md
@@ -2,16 +2,22 @@
Write a function that returns the earned points in a single toss of a Darts game.
-[Darts](https://en.wikipedia.org/wiki/Darts) is a game where players
-throw darts at a [target](https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg).
+[Darts][darts] is a game where players throw darts at a [target][darts-target].
In our particular instance of the game, the target rewards 4 different amounts of points, depending on where the dart lands:
-* If the dart lands outside the target, player earns no points (0 points).
-* If the dart lands in the outer circle of the target, player earns 1 point.
-* If the dart lands in the middle circle of the target, player earns 5 points.
-* If the dart lands in the inner circle of the target, player earns 10 points.
+- If the dart lands outside the target, player earns no points (0 points).
+- If the dart lands in the outer circle of the target, player earns 1 point.
+- If the dart lands in the middle circle of the target, player earns 5 points.
+- If the dart lands in the inner circle of the target, player earns 10 points.
-The outer circle has a radius of 10 units (this is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1. Of course, they are all centered at the same point (that is, the circles are [concentric](http://mathworld.wolfram.com/ConcentricCircles.html)) defined by the coordinates (0, 0).
+The outer circle has a radius of 10 units (this is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1.
+Of course, they are all centered at the same point (that is, the circles are [concentric][] defined by the coordinates (0, 0).
-Write a function that given a point in the target (defined by its [Cartesian coordinates](https://www.mathsisfun.com/data/cartesian-coordinates.html) `x` and `y`, where `x` and `y` are [real](https://www.mathsisfun.com/numbers/real-numbers.html)), returns the correct amount earned by a dart landing at that point.
+Write a function that given a point in the target (defined by its [Cartesian coordinates][cartesian-coordinates] `x` and `y`, where `x` and `y` are [real][real-numbers]), returns the correct amount earned by a dart landing at that point.
+
+[darts]: https://en.wikipedia.org/wiki/Darts
+[darts-target]: https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg
+[concentric]: https://mathworld.wolfram.com/ConcentricCircles.html
+[cartesian-coordinates]: https://www.mathsisfun.com/data/cartesian-coordinates.html
+[real-numbers]: https://www.mathsisfun.com/numbers/real-numbers.html
diff --git a/exercises/practice/diamond/.docs/instructions.md b/exercises/practice/diamond/.docs/instructions.md
index 1de7016f095..3034802feb2 100644
--- a/exercises/practice/diamond/.docs/instructions.md
+++ b/exercises/practice/diamond/.docs/instructions.md
@@ -1,22 +1,21 @@
# Instructions
-The diamond kata takes as its input a letter, and outputs it in a diamond
-shape. Given a letter, it prints a diamond starting with 'A', with the
-supplied letter at the widest point.
+The diamond kata takes as its input a letter, and outputs it in a diamond shape.
+Given a letter, it prints a diamond starting with 'A', with the supplied letter at the widest point.
## Requirements
-* The first row contains one 'A'.
-* The last row contains one 'A'.
-* All rows, except the first and last, have exactly two identical letters.
-* All rows have as many trailing spaces as leading spaces. (This might be 0).
-* The diamond is horizontally symmetric.
-* The diamond is vertically symmetric.
-* The diamond has a square shape (width equals height).
-* The letters form a diamond shape.
-* The top half has the letters in ascending order.
-* The bottom half has the letters in descending order.
-* The four corners (containing the spaces) are triangles.
+- The first row contains one 'A'.
+- The last row contains one 'A'.
+- All rows, except the first and last, have exactly two identical letters.
+- All rows have as many trailing spaces as leading spaces. (This might be 0).
+- The diamond is horizontally symmetric.
+- The diamond is vertically symmetric.
+- The diamond has a square shape (width equals height).
+- The letters form a diamond shape.
+- The top half has the letters in ascending order.
+- The bottom half has the letters in descending order.
+- The four corners (containing the spaces) are triangles.
## Examples
diff --git a/exercises/practice/difference-of-squares/.docs/instructions.md b/exercises/practice/difference-of-squares/.docs/instructions.md
index c3999e86ab7..39c38b5094a 100644
--- a/exercises/practice/difference-of-squares/.docs/instructions.md
+++ b/exercises/practice/difference-of-squares/.docs/instructions.md
@@ -8,10 +8,7 @@ The square of the sum of the first ten natural numbers is
The sum of the squares of the first ten natural numbers is
1² + 2² + ... + 10² = 385.
-Hence the difference between the square of the sum of the first
-ten natural numbers and the sum of the squares of the first ten
-natural numbers is 3025 - 385 = 2640.
+Hence the difference between the square of the sum of the first ten natural numbers and the sum of the squares of the first ten natural numbers is 3025 - 385 = 2640.
-You are not expected to discover an efficient solution to this yourself from
-first principles; research is allowed, indeed, encouraged. Finding the best
-algorithm for the problem is a key skill in software engineering.
+You are not expected to discover an efficient solution to this yourself from first principles; research is allowed, indeed, encouraged.
+Finding the best algorithm for the problem is a key skill in software engineering.
diff --git a/exercises/practice/diffie-hellman/.docs/instructions.md b/exercises/practice/diffie-hellman/.docs/instructions.md
index 5a6172f1ff9..9f1c85e3125 100644
--- a/exercises/practice/diffie-hellman/.docs/instructions.md
+++ b/exercises/practice/diffie-hellman/.docs/instructions.md
@@ -2,9 +2,8 @@
Diffie-Hellman key exchange.
-Alice and Bob use Diffie-Hellman key exchange to share secrets. They
-start with prime numbers, pick private keys, generate and share public
-keys, and then generate a shared secret key.
+Alice and Bob use Diffie-Hellman key exchange to share secrets.
+They start with prime numbers, pick private keys, generate and share public keys, and then generate a shared secret key.
## Step 0
@@ -12,8 +11,8 @@ The test program supplies prime numbers p and g.
## Step 1
-Alice picks a private key, a, greater than 1 and less than p. Bob does
-the same to pick a private key b.
+Alice picks a private key, a, greater than 1 and less than p.
+Bob does the same to pick a private key b.
## Step 2
@@ -21,12 +20,12 @@ Alice calculates a public key A.
A = gᵃ mod p
-Using the same p and g, Bob similarly calculates a public key B from his
-private key b.
+Using the same p and g, Bob similarly calculates a public key B from his private key b.
## Step 3
-Alice and Bob exchange public keys. Alice calculates secret key s.
+Alice and Bob exchange public keys.
+Alice calculates secret key s.
s = Bᵃ mod p
@@ -34,5 +33,5 @@ Bob calculates
s = Aᵇ mod p
-The calculations produce the same result! Alice and Bob now share
-secret s.
+The calculations produce the same result!
+Alice and Bob now share secret s.
diff --git a/exercises/practice/dnd-character/.docs/instructions.md b/exercises/practice/dnd-character/.docs/instructions.md
index 53d9f98517a..b0a603591ec 100644
--- a/exercises/practice/dnd-character/.docs/instructions.md
+++ b/exercises/practice/dnd-character/.docs/instructions.md
@@ -1,33 +1,31 @@
# Instructions
-For a game of [Dungeons & Dragons][DND], each player starts by generating a
-character they can play with. This character has, among other things, six
-abilities; strength, dexterity, constitution, intelligence, wisdom and
-charisma. These six abilities have scores that are determined randomly. You
-do this by rolling four 6-sided dice and record the sum of the largest three
-dice. You do this six times, once for each ability.
+For a game of [Dungeons & Dragons][dnd], each player starts by generating a character they can play with.
+This character has, among other things, six abilities; strength, dexterity, constitution, intelligence, wisdom and charisma.
+These six abilities have scores that are determined randomly.
+You do this by rolling four 6-sided dice and record the sum of the largest three dice.
+You do this six times, once for each ability.
-Your character's initial hitpoints are 10 + your character's constitution
-modifier. You find your character's constitution modifier by subtracting 10
-from your character's constitution, divide by 2 and round down.
+Your character's initial hitpoints are 10 + your character's constitution modifier.
+You find your character's constitution modifier by subtracting 10 from your character's constitution, divide by 2 and round down.
Write a random character generator that follows the rules above.
For example, the six throws of four dice may look like:
-* 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength.
-* 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity.
-* 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution.
-* 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence.
-* 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom.
-* 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma.
+- 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength.
+- 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity.
+- 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution.
+- 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence.
+- 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom.
+- 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma.
Because constitution is 3, the constitution modifier is -4 and the hitpoints are 6.
## Notes
-Most programming languages feature (pseudo-)random generators, but few
-programming languages are designed to roll dice. One such language is [Troll].
+Most programming languages feature (pseudo-)random generators, but few programming languages are designed to roll dice.
+One such language is [Troll][troll].
-[DND]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
-[Troll]: http://hjemmesider.diku.dk/~torbenm/Troll/
+[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
+[troll]: https://di.ku.dk/Ansatte/?pure=da%2Fpublications%2Ftroll-a-language-for-specifying-dicerolls(84a45ff0-068b-11df-825d-000ea68e967b)%2Fexport.html
diff --git a/exercises/practice/dominoes/.docs/instructions.md b/exercises/practice/dominoes/.docs/instructions.md
index 808aa424231..1ced9f6448f 100644
--- a/exercises/practice/dominoes/.docs/instructions.md
+++ b/exercises/practice/dominoes/.docs/instructions.md
@@ -2,14 +2,12 @@
Make a chain of dominoes.
-Compute a way to order a given set of dominoes in such a way that they form a
-correct domino chain (the dots on one half of a stone match the dots on the
-neighboring half of an adjacent stone) and that dots on the halves of the
-stones which don't have a neighbor (the first and last stone) match each other.
+Compute a way to order a given set of dominoes in such a way that they form a correct domino chain (the dots on one half of a stone match the dots on the neighboring half of an adjacent stone) and that dots on the halves of the stones which don't have a neighbor (the first and last stone) match each other.
For example given the stones `[2|1]`, `[2|3]` and `[1|3]` you should compute something
like `[1|2] [2|3] [3|1]` or `[3|2] [2|1] [1|3]` or `[1|3] [3|2] [2|1]` etc, where the first and last numbers are the same.
-For stones `[1|2]`, `[4|1]` and `[2|3]` the resulting chain is not valid: `[4|1] [1|2] [2|3]`'s first and last numbers are not the same. 4 != 3
+For stones `[1|2]`, `[4|1]` and `[2|3]` the resulting chain is not valid: `[4|1] [1|2] [2|3]`'s first and last numbers are not the same.
+4 != 3
Some test cases may use duplicate stones in a chain solution, assume that multiple Domino sets are being used.
diff --git a/exercises/practice/dot-dsl/.docs/instructions.md b/exercises/practice/dot-dsl/.docs/instructions.md
index f61f3f0b4dc..9230547ea5e 100644
--- a/exercises/practice/dot-dsl/.docs/instructions.md
+++ b/exercises/practice/dot-dsl/.docs/instructions.md
@@ -1,16 +1,12 @@
# Instructions
-A [Domain Specific Language
-(DSL)](https://en.wikipedia.org/wiki/Domain-specific_language) is a
-small language optimized for a specific domain. Since a DSL is
-targeted, it can greatly impact productivity/understanding by allowing the
-writer to declare *what* they want rather than *how*.
+A [Domain Specific Language (DSL)][dsl] is a small language optimized for a specific domain.
+Since a DSL is targeted, it can greatly impact productivity/understanding by allowing the writer to declare *what* they want rather than *how*.
One problem area where they are applied are complex customizations/configurations.
-For example the [DOT language](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) allows
-you to write a textual description of a graph which is then transformed into a picture by one of
-the [Graphviz](http://graphviz.org/) tools (such as `dot`). A simple graph looks like this:
+For example the [DOT language][dot-language] allows you to write a textual description of a graph which is then transformed into a picture by one of the [Graphviz][graphviz] tools (such as `dot`).
+A simple graph looks like this:
graph {
graph [bgcolor="yellow"]
@@ -19,15 +15,16 @@ the [Graphviz](http://graphviz.org/) tools (such as `dot`). A simple graph looks
a -- b [color="green"]
}
-Putting this in a file `example.dot` and running `dot example.dot -T png
--o example.png` creates an image `example.png` with red and blue circle
-connected by a green line on a yellow background.
+Putting this in a file `example.dot` and running `dot example.dot -T png -o example.png` creates an image `example.png` with red and blue circle connected by a green line on a yellow background.
Write a Domain Specific Language similar to the Graphviz dot language.
-Our DSL is similar to the Graphviz dot language in that our DSL will be used
-to create graph data structures. However, unlike the DOT Language, our DSL will
-be an internal DSL for use only in our language.
+Our DSL is similar to the Graphviz dot language in that our DSL will be used to create graph data structures.
+However, unlike the DOT Language, our DSL will be an internal DSL for use only in our language.
-More information about the difference between internal and external DSLs can be
-found [here](https://martinfowler.com/bliki/DomainSpecificLanguage.html).
+More information about the difference between internal and external DSLs can be found [here][fowler-dsl].
+
+[dsl]: https://en.wikipedia.org/wiki/Domain-specific_language
+[dot-language]: https://en.wikipedia.org/wiki/DOT_(graph_description_language)
+[graphviz]: https://graphviz.org/
+[fowler-dsl]: https://martinfowler.com/bliki/DomainSpecificLanguage.html
diff --git a/exercises/practice/etl/.docs/instructions.md b/exercises/practice/etl/.docs/instructions.md
index ff96906c6b6..fffe64f2015 100644
--- a/exercises/practice/etl/.docs/instructions.md
+++ b/exercises/practice/etl/.docs/instructions.md
@@ -4,12 +4,10 @@ We are going to do the `Transform` step of an Extract-Transform-Load.
## 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."
+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.)
+(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
@@ -25,10 +23,8 @@ 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:
+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:
- "a" is worth 1 point.
- "b" is worth 3 points.
@@ -36,12 +32,9 @@ input letters:
- "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.
+Your mission, should you choose to accept it, is to transform the legacy data format to the shiny new format.
## Notes
-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.
+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.
diff --git a/exercises/practice/flatten-array/.docs/instructions.md b/exercises/practice/flatten-array/.docs/instructions.md
index 02b68cdfeb7..51bea67909f 100644
--- a/exercises/practice/flatten-array/.docs/instructions.md
+++ b/exercises/practice/flatten-array/.docs/instructions.md
@@ -4,7 +4,7 @@ Take a nested list and return a single flattened list with all values except nil
The challenge is to write a function that accepts an arbitrarily-deep nested list-like structure and returns a flattened structure without any nil/null values.
-For Example
+For example:
input: [1,[2,3,null,4],[null],5]
diff --git a/exercises/practice/food-chain/.docs/instructions.md b/exercises/practice/food-chain/.docs/instructions.md
index 4d9c10b599a..125820e321b 100644
--- a/exercises/practice/food-chain/.docs/instructions.md
+++ b/exercises/practice/food-chain/.docs/instructions.md
@@ -2,11 +2,9 @@
Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'.
-While you could copy/paste the lyrics,
-or read them from a file, this problem is much more
-interesting if you approach it algorithmically.
+While you could copy/paste the lyrics, or read them from a file, this problem is much more interesting if you approach it algorithmically.
-This is a [cumulative song](http://en.wikipedia.org/wiki/Cumulative_song) of unknown origin.
+This is a [cumulative song][cumulative-song] of unknown origin.
This is one of many common variants.
@@ -62,3 +60,5 @@ I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a horse.
She's dead, of course!
```
+
+[cumulative-song]: https://en.wikipedia.org/wiki/Cumulative_song
diff --git a/exercises/practice/forth/.docs/instructions.md b/exercises/practice/forth/.docs/instructions.md
index f481b725a63..91ad26e6e9c 100644
--- a/exercises/practice/forth/.docs/instructions.md
+++ b/exercises/practice/forth/.docs/instructions.md
@@ -2,25 +2,22 @@
Implement an evaluator for a very simple subset of Forth.
-[Forth](https://en.wikipedia.org/wiki/Forth_%28programming_language%29)
-is a stack-based programming language. Implement a very basic evaluator
-for a small subset of Forth.
+[Forth][forth]
+is a stack-based programming language.
+Implement a very basic evaluator for a small subset of Forth.
Your evaluator has to support the following words:
- `+`, `-`, `*`, `/` (integer arithmetic)
- `DUP`, `DROP`, `SWAP`, `OVER` (stack manipulation)
-Your evaluator also has to support defining new words using the
-customary syntax: `: word-name definition ;`.
+Your evaluator also has to support defining new words using the customary syntax: `: word-name definition ;`.
-To keep things simple the only data type you need to support is signed
-integers of at least 16 bits size.
+To keep things simple the only data type you need to support is signed integers of at least 16 bits size.
-You should use the following rules for the syntax: a number is a
-sequence of one or more (ASCII) digits, a word is a sequence of one or
-more letters, digits, symbols or punctuation that is not a number.
-(Forth probably uses slightly different rules, but this is close
-enough.)
+You should use the following rules for the syntax: a number is a sequence of one or more (ASCII) digits, a word is a sequence of one or more letters, digits, symbols or punctuation that is not a number.
+(Forth probably uses slightly different rules, but this is close enough.)
Words are case-insensitive.
+
+[forth]: https://en.wikipedia.org/wiki/Forth_%28programming_language%29
diff --git a/exercises/practice/gigasecond/.docs/instructions.md b/exercises/practice/gigasecond/.docs/instructions.md
index 680870f3a81..41a057c44a2 100644
--- a/exercises/practice/gigasecond/.docs/instructions.md
+++ b/exercises/practice/gigasecond/.docs/instructions.md
@@ -1,6 +1,5 @@
# Instructions
-Given a moment, determine the moment that would be after a gigasecond
-has passed.
+Given a moment, determine the moment that would be after a gigasecond has passed.
A gigasecond is 10^9 (1,000,000,000) seconds.
diff --git a/exercises/practice/go-counting/.docs/instructions.md b/exercises/practice/go-counting/.docs/instructions.md
index d231a09c7a9..15fdab20ba0 100644
--- a/exercises/practice/go-counting/.docs/instructions.md
+++ b/exercises/practice/go-counting/.docs/instructions.md
@@ -2,21 +2,17 @@
Count the scored points on a Go board.
-In the game of go (also known as baduk, igo, cờ vây and wéiqí) points
-are gained by completely encircling empty intersections with your
-stones. The encircled intersections of a player are known as its
-territory.
+In the game of go (also known as baduk, igo, cờ vây and wéiqí) points are gained by completely encircling empty intersections with your stones.
+The encircled intersections of a player are known as its territory.
-Write a function that determines the territory of each player. You may
-assume that any stones that have been stranded in enemy territory have
-already been taken off the board.
+Write a function that determines the territory of each player.
+You may assume that any stones that have been stranded in enemy territory have already been taken off the board.
Write a function that determines the territory which includes a specified coordinate.
-Multiple empty intersections may be encircled at once and for encircling
-only horizontal and vertical neighbors count. In the following diagram
-the stones which matter are marked "O" and the stones that don't are
-marked "I" (ignored). Empty spaces represent empty intersections.
+Multiple empty intersections may be encircled at once and for encircling only horizontal and vertical neighbors count.
+In the following diagram the stones which matter are marked "O" and the stones that don't are marked "I" (ignored).
+Empty spaces represent empty intersections.
```text
+----+
@@ -27,10 +23,9 @@ marked "I" (ignored). Empty spaces represent empty intersections.
+----+
```
-To be more precise an empty intersection is part of a player's territory
-if all of its neighbors are either stones of that player or empty
-intersections that are part of that player's territory.
+To be more precise an empty intersection is part of a player's territory if all of its neighbors are either stones of that player or empty intersections that are part of that player's territory.
-For more information see
-[wikipedia](https://en.wikipedia.org/wiki/Go_%28game%29) or [Sensei's
-Library](http://senseis.xmp.net/).
+For more information see [wikipedia][go-wikipedia] or [Sensei's Library][go-sensei].
+
+[go-wikipedia]: https://en.wikipedia.org/wiki/Go_%28game%29
+[go-sensei]: https://senseis.xmp.net/
diff --git a/exercises/practice/grade-school/.docs/instructions.md b/exercises/practice/grade-school/.docs/instructions.md
index 012e7add8b1..9a63e398d85 100644
--- a/exercises/practice/grade-school/.docs/instructions.md
+++ b/exercises/practice/grade-school/.docs/instructions.md
@@ -1,7 +1,6 @@
# Instructions
-Given students' names along with the grade that they are in, create a roster
-for the school.
+Given students' names along with the grade that they are in, create a roster for the school.
In the end, you should be able to:
@@ -11,31 +10,12 @@ In the end, you should be able to:
- Get a list of all students enrolled in a grade
- "Which students are in grade 2?"
- "We've only got Jim just now."
-- Get a sorted list of all students in all grades. Grades should sort
- as 1, 2, 3, etc., and students within a grade should be sorted
- alphabetically by name.
+- Get a sorted list of all students in all grades.
+ Grades should sort as 1, 2, 3, etc., and students within a grade should be sorted alphabetically by name.
- "Who all is enrolled in school right now?"
- - "Let me think. We have
- Anna, Barb, and Charlie in grade 1,
- Alex, Peter, and Zoe in grade 2
- and Jim in grade 5.
- So the answer is: Anna, Barb, Charlie, Alex, Peter, Zoe and Jim"
+ - "Let me think.
+ We have Anna, Barb, and Charlie in grade 1, Alex, Peter, and Zoe in grade 2 and Jim in grade 5.
+ So the answer is: Anna, Barb, Charlie, Alex, Peter, Zoe and Jim"
-Note that all our students only have one name (It's a small town, what
-do you want?) and each student cannot be added more than once to a grade or the
-roster.
-In fact, when a test attempts to add the same student more than once, your
-implementation should indicate that this is incorrect.
-
-## For bonus points
-
-Did you get the tests passing and the code clean? If you want to, these
-are some additional things you could try:
-
-- If you're working in a language with mutable data structures and your
- implementation allows outside code to mutate the school's internal DB
- directly, see if you can prevent this. Feel free to introduce additional
- tests.
-
-Then please share your thoughts in a comment on the submission. Did this
-experiment make the code better? Worse? Did you learn anything from it?
+Note that all our students only have one name (It's a small town, what do you want?) and each student cannot be added more than once to a grade or the roster.
+In fact, when a test attempts to add the same student more than once, your implementation should indicate that this is incorrect.
diff --git a/exercises/practice/grains/.docs/instructions.md b/exercises/practice/grains/.docs/instructions.md
index d955f12230e..df479fc0a17 100644
--- a/exercises/practice/grains/.docs/instructions.md
+++ b/exercises/practice/grains/.docs/instructions.md
@@ -1,13 +1,11 @@
# Instructions
-Calculate the number of grains of wheat on a chessboard given that the number
-on each square doubles.
+Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.
-There once was a wise servant who saved the life of a prince. The king
-promised to pay whatever the servant could dream up. Knowing that the
-king loved chess, the servant told the king he would like to have grains
-of wheat. One grain on the first square of a chess board, with the number
-of grains doubling on each successive square.
+There once was a wise servant who saved the life of a prince.
+The king promised to pay whatever the servant could dream up.
+Knowing that the king loved chess, the servant told the king he would like to have grains of wheat.
+One grain on the first square of a chess board, with the number of grains doubling on each successive square.
There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on).
@@ -15,14 +13,3 @@ Write code that shows:
- how many grains were on a given square, and
- the total number of grains on the chessboard
-
-## For bonus points
-
-Did you get the tests passing and the code clean? If you want to, these
-are some additional things you could try:
-
-- Optimize for speed.
-- Optimize for readability.
-
-Then please share your thoughts in a comment on the submission. Did this
-experiment make the code better? Worse? Did you learn anything from it?
diff --git a/exercises/practice/grep/.docs/instructions.md b/exercises/practice/grep/.docs/instructions.md
index 602ea29dbb5..004f28acd55 100644
--- a/exercises/practice/grep/.docs/instructions.md
+++ b/exercises/practice/grep/.docs/instructions.md
@@ -1,77 +1,27 @@
# Instructions
-Search a file for lines matching a regular expression pattern. Return the line
-number and contents of each matching line.
+Search files for lines matching a search string and return all matching lines.
-The Unix [`grep`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html) command can be used to search for lines in one or more files
-that match a user-provided search query (known as the *pattern*).
+The Unix [`grep`][grep] command searches files for lines that match a regular expression.
+Your task is to implement a simplified `grep` command, which supports searching for fixed strings.
The `grep` command takes three arguments:
-1. The pattern used to match lines in a file.
-2. Zero or more flags to customize the matching behavior.
-3. One or more files in which to search for matching lines.
+1. The string to search for.
+2. Zero or more flags for customizing the command's behavior.
+3. One or more files to search in.
-Your task is to implement the `grep` function: given a list of files, find all
-lines that match the specified pattern.
-Return the lines in the order they appear in the files.
-You'll also have to handle options (given as flags), which control how matching
-is done and how the results are to be reported.
-
-As an example, suppose there is a file named "input.txt" with the following contents:
-
-```text
-hello
-world
-hello again
-```
-
-If we were to call `grep "hello" input.txt`, the result should be:
-
-```text
-hello
-hello again
-```
-
-If given multiple files, `grep` should prefix each found line with the file it was found in.
-As an example:
-
-```text
-input.txt:hello
-input.txt:hello again
-greeting.txt:hello world
-```
-
-If given just one file, this prefix is not present.
+It then reads the contents of the specified files (in the order specified), finds the lines that contain the search string, and finally returns those lines in the order in which they were found.
+When searching in multiple files, each matching line is prepended by the file name and a colon (':').
## Flags
-As said earlier, the `grep` command should also support the following flags:
-
-- `-n` Prefix each matching line with its line number within its file.
- When multiple files are present, this prefix goes *after* the filename prefix.
-- `-l` Print only the names of files that contain at least one matching line.
-- `-i` Match line using a case-insensitive comparison.
-- `-v` Invert the program -- collect all lines that fail to match the pattern.
-- `-x` Only match entire lines, instead of lines that contain a match.
-
-If we run `grep -n "hello" input.txt`, the `-n` flag will require the matching
-lines to be prefixed with its line number:
-
-```text
-1:hello
-3:hello again
-```
-
-And if we run `grep -i "HELLO" input.txt`, we'll do a case-insensitive match,
-and the output will be:
-
-```text
-hello
-hello again
-```
+The `grep` command supports the following flags:
-The `grep` command should support multiple flags at once.
+- `-n` Prepend the line number and a colon (':') to each line in the output, placing the number after the filename (if present).
+- `-l` Output only the names of the files that contain at least one matching line.
+- `-i` Match using a case-insensitive comparison.
+- `-v` Invert the program -- collect all lines that fail to match.
+- `-x` Search only for lines where the search string matches the entire line.
-For example, running `grep -l -v "hello" file1.txt file2.txt` should
-print the names of files that do not contain the string "hello".
+[grep]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html
diff --git a/exercises/practice/hamming/.docs/instructions.md b/exercises/practice/hamming/.docs/instructions.md
index 12381074f5e..020fdd02d4e 100644
--- a/exercises/practice/hamming/.docs/instructions.md
+++ b/exercises/practice/hamming/.docs/instructions.md
@@ -2,11 +2,17 @@
Calculate the Hamming Distance between two DNA strands.
-Your body is made up of cells that contain DNA. Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime!
+Your body is made up of cells that contain DNA.
+Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells.
+In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime!
-When cells divide, their DNA replicates too. Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred. This is known as the "Hamming Distance".
+When cells divide, their DNA replicates too.
+Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information.
+If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred.
+This is known as the "Hamming Distance".
-We read DNA using the letters C,A,G and T. Two strands might look like this:
+We read DNA using the letters C,A,G and T.
+Two strands might look like this:
GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
@@ -18,6 +24,4 @@ The Hamming Distance is useful for lots of things in science, not just biology,
## Implementation notes
-The Hamming distance is only defined for sequences of equal length, so
-an attempt to calculate it between sequences of different lengths should
-not work.
+The Hamming distance is only defined for sequences of equal length, so an attempt to calculate it between sequences of different lengths should not work.
diff --git a/exercises/practice/hangman/.docs/instructions.md b/exercises/practice/hangman/.docs/instructions.md
index 2b0f335670b..227e73175c2 100644
--- a/exercises/practice/hangman/.docs/instructions.md
+++ b/exercises/practice/hangman/.docs/instructions.md
@@ -2,17 +2,13 @@
Implement the logic of the hangman game using functional reactive programming.
-[Hangman][] is a simple word guessing game.
+[Hangman][hangman] is a simple word guessing game.
-[Functional Reactive Programming][frp] is a way to write interactive
-programs. It differs from the usual perspective in that instead of
-saying "when the button is pressed increment the counter", you write
-"the value of the counter is the sum of the number of times the button
-is pressed."
+[Functional Reactive Programming][frp] is a way to write interactive programs.
+It differs from the usual perspective in that instead of saying "when the button is pressed increment the counter", you write "the value of the counter is the sum of the number of times the button is pressed."
-Implement the basic logic behind hangman using functional reactive
-programming. You'll need to install an FRP library for this, this will
-be described in the language/track specific files of the exercise.
+Implement the basic logic behind hangman using functional reactive programming.
+You'll need to install an FRP library for this, this will be described in the language/track specific files of the exercise.
-[Hangman]: https://en.wikipedia.org/wiki/Hangman_%28game%29
+[hangman]: https://en.wikipedia.org/wiki/Hangman_%28game%29
[frp]: https://en.wikipedia.org/wiki/Functional_reactive_programming
diff --git a/exercises/practice/hello-world/.docs/instructions.md b/exercises/practice/hello-world/.docs/instructions.md
index 0342bf0a4c5..c9570e48a97 100644
--- a/exercises/practice/hello-world/.docs/instructions.md
+++ b/exercises/practice/hello-world/.docs/instructions.md
@@ -1,10 +1,9 @@
# Instructions
-The classical introductory exercise. Just say "Hello, World!".
+The classical introductory exercise.
+Just say "Hello, World!".
-["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is
-the traditional first program for beginning programming in a new language
-or environment.
+["Hello, World!"][hello-world] is the traditional first program for beginning programming in a new language or environment.
The objectives are simple:
@@ -13,3 +12,5 @@ The objectives are simple:
- Submit your solution and check it at the website.
If everything goes well, you will be ready to fetch your first real exercise.
+
+[hello-world]: https://en.wikipedia.org/wiki/%22Hello,_world!%22_program
diff --git a/exercises/practice/house/.docs/instructions.md b/exercises/practice/house/.docs/instructions.md
index 92174617f32..88928c5fa22 100644
--- a/exercises/practice/house/.docs/instructions.md
+++ b/exercises/practice/house/.docs/instructions.md
@@ -2,14 +2,11 @@
Recite the nursery rhyme 'This is the House that Jack Built'.
-> [The] process of placing a phrase of clause within another phrase of
-> clause is called embedding. It is through the processes of recursion
-> and embedding that we are able to take a finite number of forms (words
-> and phrases) and construct an infinite number of expressions.
-> Furthermore, embedding also allows us to construct an infinitely long
-> structure, in theory anyway.
+> [The] process of placing a phrase of clause within another phrase of clause is called embedding.
+> It is through the processes of recursion and embedding that we are able to take a finite number of forms (words and phrases) and construct an infinite number of expressions.
+> Furthermore, embedding also allows us to construct an infinitely long structure, in theory anyway.
-- [papyr.com](http://papyr.com/hypertextbooks/grammar/ph_noun.htm)
+- [papyr.com][papyr]
The nursery rhyme reads as follows:
@@ -104,3 +101,5 @@ that killed the rat
that ate the malt
that lay in the house that Jack built.
```
+
+[papyr]: https://papyr.com/hypertextbooks/grammar/ph_noun.htm
diff --git a/exercises/practice/isbn-verifier/.docs/instructions.md b/exercises/practice/isbn-verifier/.docs/instructions.md
index ff94a6614e6..4a0244e5523 100644
--- a/exercises/practice/isbn-verifier/.docs/instructions.md
+++ b/exercises/practice/isbn-verifier/.docs/instructions.md
@@ -1,11 +1,13 @@
# Instructions
-The [ISBN-10 verification process](https://en.wikipedia.org/wiki/International_Standard_Book_Number) is used to validate book identification
-numbers. These normally contain dashes and look like: `3-598-21508-8`
+The [ISBN-10 verification process][isbn-verification] is used to validate book identification numbers.
+These normally contain dashes and look like: `3-598-21508-8`
## ISBN
-The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only). In the case the check character is an X, this represents the value '10'. These may be communicated with or without hyphens, and can be checked for their validity by the following formula:
+The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only).
+In the case the check character is an X, this represents the value '10'.
+These may be communicated with or without hyphens, and can be checked for their validity by the following formula:
```text
(d₁ * 10 + d₂ * 9 + d₃ * 8 + d₄ * 7 + d₅ * 6 + d₆ * 5 + d₇ * 4 + d₈ * 3 + d₉ * 2 + d₁₀ * 1) mod 11 == 0
@@ -15,7 +17,8 @@ If the result is 0, then it is a valid ISBN-10, otherwise it is invalid.
## Example
-Let's take the ISBN-10 `3-598-21508-8`. We plug it in to the formula, and get:
+Let's take the ISBN-10 `3-598-21508-8`.
+We plug it in to the formula, and get:
```text
(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
@@ -33,10 +36,7 @@ The program should be able to verify ISBN-10 both with and without separating da
## Caveats
Converting from strings to numbers can be tricky in certain languages.
-Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10'). For instance `3-598-21507-X` is a valid ISBN-10.
+Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10').
+For instance `3-598-21507-X` is a valid ISBN-10.
-## Bonus tasks
-
-* Generate a valid ISBN-13 from the input ISBN-10 (and maybe verify it again with a derived verifier).
-
-* Generate valid ISBN, maybe even from a given starting ISBN.
+[isbn-verification]: https://en.wikipedia.org/wiki/International_Standard_Book_Number
diff --git a/exercises/practice/kindergarten-garden/.docs/instructions.md b/exercises/practice/kindergarten-garden/.docs/instructions.md
index ba89ff9b05f..472ee26f6c4 100644
--- a/exercises/practice/kindergarten-garden/.docs/instructions.md
+++ b/exercises/practice/kindergarten-garden/.docs/instructions.md
@@ -3,9 +3,8 @@
Given a diagram, determine which plants each child in the kindergarten class is
responsible for.
-The kindergarten class is learning about growing plants. The teacher
-thought it would be a good idea to give them actual seeds, plant them in
-actual dirt, and grow actual plants.
+The kindergarten class is learning about growing plants.
+The teacher thought it would be a good idea to give them actual seeds, plant them in actual dirt, and grow actual plants.
They've chosen to grow grass, clover, radishes, and violets.
@@ -25,8 +24,8 @@ There are 12 children in the class:
- Eve, Fred, Ginny, Harriet,
- Ileana, Joseph, Kincaid, and Larry.
-Each child gets 4 cups, two on each row. Their teacher assigns cups to
-the children alphabetically by their names.
+Each child gets 4 cups, two on each row.
+Their teacher assigns cups to the children alphabetically by their names.
The following diagram represents Alice's plants:
@@ -36,12 +35,11 @@ VR......................
RG......................
```
-In the first row, nearest the windows, she has a violet and a radish. In the
-second row she has a radish and some grass.
+In the first row, nearest the windows, she has a violet and a radish.
+In the second row she has a radish and some grass.
-Your program will be given the plants from left-to-right starting with
-the row nearest the windows. From this, it should be able to determine
-which plants belong to each student.
+Your program will be given the plants from left-to-right starting with the row nearest the windows.
+From this, it should be able to determine which plants belong to each student.
For example, if it's told that the garden looks like so:
diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md
index 812d1010d1d..1dbbca91c28 100644
--- a/exercises/practice/knapsack/.docs/instructions.md
+++ b/exercises/practice/knapsack/.docs/instructions.md
@@ -2,21 +2,18 @@
In this exercise, let's try to solve a classic problem.
-Bob is a thief. After months of careful planning, he finally manages to
-crack the security systems of a high-class apartment.
+Bob is a thief.
+After months of careful planning, he finally manages to crack the security systems of a high-class apartment.
-In front of him are many items, each with a value (v) and weight (w). Bob,
-of course, wants to maximize the total value he can get; he would gladly
-take all of the items if he could. However, to his horror, he realizes that
-the knapsack he carries with him can only hold so much weight (W).
+In front of him are many items, each with a value (v) and weight (w).
+Bob, of course, wants to maximize the total value he can get; he would gladly take all of the items if he could.
+However, to his horror, he realizes that the knapsack he carries with him can only hold so much weight (W).
-Given a knapsack with a specific carrying capacity (W), help Bob determine
-the maximum value he can get from the items in the house. Note that Bob can
-take only one of each item.
+Given a knapsack with a specific carrying capacity (W), help Bob determine the maximum value he can get from the items in the house.
+Note that Bob can take only one of each item.
-All values given will be strictly positive. Items will be represented as a
-list of pairs, `wi` and `vi`, where the first element `wi` is the weight of
-the *i*th item and `vi` is the value for that item.
+All values given will be strictly positive.
+Items will be represented as a list of pairs, `wi` and `vi`, where the first element `wi` is the weight of the *i*th item and `vi` is the value for that item.
For example:
@@ -29,9 +26,7 @@ Items: [
Knapsack Limit: 10
-For the above, the first item has weight 5 and value 10, the second item has
-weight 4 and value 40, and so on.
+For the above, the first item has weight 5 and value 10, the second item has weight 4 and value 40, and so on.
-In this example, Bob should take the second and fourth item to maximize his
-value, which, in this case, is 90. He cannot get more than 90 as his
-knapsack has a weight limit of 10.
+In this example, Bob should take the second and fourth item to maximize his value, which, in this case, is 90.
+He cannot get more than 90 as his knapsack has a weight limit of 10.
diff --git a/exercises/practice/largest-series-product/.docs/instructions.md b/exercises/practice/largest-series-product/.docs/instructions.md
index 5a3c18de5c7..08586dd5933 100644
--- a/exercises/practice/largest-series-product/.docs/instructions.md
+++ b/exercises/practice/largest-series-product/.docs/instructions.md
@@ -1,14 +1,10 @@
# Instructions
-Given a string of digits, calculate the largest product for a contiguous
-substring of digits of length n.
+Given a string of digits, calculate the largest product for a contiguous substring of digits of length n.
-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).
+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)`.
-Note that these series are only required to occupy *adjacent positions*
-in the input; the digits need not be *numerically consecutive*.
+Note that these series are only required to occupy *adjacent positions* in the input; the digits need not be *numerically consecutive*.
For the input `'73167176531330624919225119674426574742355349194934'`,
the largest product for a series of 6 digits is 23520.
diff --git a/exercises/practice/leap/.docs/instructions.md b/exercises/practice/leap/.docs/instructions.md
index dc7b4e81641..a83826b2e0e 100644
--- a/exercises/practice/leap/.docs/instructions.md
+++ b/exercises/practice/leap/.docs/instructions.md
@@ -10,15 +10,13 @@ on every year that is evenly divisible by 4
unless the year is also evenly divisible by 400
```
-For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap
-year, but 2000 is.
+For example, 1997 is not a leap year, but 1996 is.
+1900 is not a leap year, but 2000 is.
## Notes
-Though our exercise adopts some very simple rules, there is more to
-learn!
+Though our exercise adopts some very simple rules, there is more to learn!
-For a delightful, four minute explanation of the whole leap year
-phenomenon, go watch [this youtube video][video].
+For a delightful, four minute explanation of the whole leap year phenomenon, go watch [this youtube video][video].
-[video]: http://www.youtube.com/watch?v=xX96xng7sAE
+[video]: https://www.youtube.com/watch?v=xX96xng7sAE
diff --git a/exercises/practice/ledger/.docs/instructions.md b/exercises/practice/ledger/.docs/instructions.md
index 266054c36e6..a53e5c15e3e 100644
--- a/exercises/practice/ledger/.docs/instructions.md
+++ b/exercises/practice/ledger/.docs/instructions.md
@@ -2,14 +2,13 @@
Refactor a ledger printer.
-The ledger exercise is a refactoring exercise. There is code that prints a
-nicely formatted ledger, given a locale (American or Dutch) and a currency (US
-dollar or euro). The code however is rather badly written, though (somewhat
-surprisingly) it consistently passes the test suite.
+The ledger exercise is a refactoring exercise.
+There is code that prints a nicely formatted ledger, given a locale (American or Dutch) and a currency (US dollar or euro).
+The code however is rather badly written, though (somewhat surprisingly) it consistently passes the test suite.
-Rewrite this code. Remember that in refactoring the trick is to make small steps
-that keep the tests passing. That way you can always quickly go back to a
-working version. Version control tools like git can help here as well.
+Rewrite this code.
+Remember that in refactoring the trick is to make small steps that keep the tests passing.
+That way you can always quickly go back to a working version.
+Version control tools like git can help here as well.
-Please keep a log of what changes you've made and make a comment on the exercise
-containing that log, this will help reviewers.
+Please keep a log of what changes you've made and make a comment on the exercise containing that log, this will help reviewers.
diff --git a/exercises/practice/linked-list/.docs/instructions.md b/exercises/practice/linked-list/.docs/instructions.md
index d1bd87551b6..3d949d39357 100644
--- a/exercises/practice/linked-list/.docs/instructions.md
+++ b/exercises/practice/linked-list/.docs/instructions.md
@@ -2,27 +2,25 @@
Implement a doubly 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.
-
-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.
-
-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:
-
-* `push` (*insert value at back*);
-* `pop` (*remove value at back*);
-* `shift` (*remove value at front*).
-* `unshift` (*insert value at front*);
-
-To keep your implementation simple, the tests will not cover error
-conditions. Specifically: `pop` or `shift` will never be called on an
-empty list.
-
-If you want to know more about linked lists, check [Wikipedia](https://en.wikipedia.org/wiki/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.
+
+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.
+
+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:
+
+- `push` (*insert value at back*);
+- `pop` (*remove value at back*);
+- `shift` (*remove value at front*).
+- `unshift` (*insert value at front*);
+
+To keep your implementation simple, the tests will not cover error conditions.
+Specifically: `pop` or `shift` will never be called on an empty list.
+
+Read more about [linked lists on Wikipedia][linked-lists].
+
+[linked-lists]: https://en.wikipedia.org/wiki/Linked_list
diff --git a/exercises/practice/list-ops/.docs/instructions.md b/exercises/practice/list-ops/.docs/instructions.md
index b5b20ff20a2..d34533387ae 100644
--- a/exercises/practice/list-ops/.docs/instructions.md
+++ b/exercises/practice/list-ops/.docs/instructions.md
@@ -2,19 +2,16 @@
Implement basic list operations.
-In functional languages list operations like `length`, `map`, and
-`reduce` are very common. Implement a series of basic list operations,
-without using existing functions.
+In functional languages list operations like `length`, `map`, and `reduce` are very common.
+Implement a series of basic list operations, without using existing functions.
-The precise number and names of the operations to be implemented will be
-track dependent to avoid conflicts with existing names, but the general
-operations you will implement include:
+The precise number and names of the operations to be implemented will be track dependent to avoid conflicts with existing names, but the general operations you will implement include:
-* `append` (*given two lists, add all items in the second list to the end of the first list*);
-* `concatenate` (*given a series of lists, combine all items in all lists into one flattened list*);
-* `filter` (*given a predicate and a list, return the list of all items for which `predicate(item)` is True*);
-* `length` (*given a list, return the total number of items within it*);
-* `map` (*given a function and a list, return the list of the results of applying `function(item)` on all items*);
-* `foldl` (*given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left using `function(accumulator, item)`*);
-* `foldr` (*given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right using `function(item, accumulator)`*);
-* `reverse` (*given a list, return a list with all the original items, but in reversed order*);
+- `append` (*given two lists, add all items in the second list to the end of the first list*);
+- `concatenate` (*given a series of lists, combine all items in all lists into one flattened list*);
+- `filter` (*given a predicate and a list, return the list of all items for which `predicate(item)` is True*);
+- `length` (*given a list, return the total number of items within it*);
+- `map` (*given a function and a list, return the list of the results of applying `function(item)` on all items*);
+- `foldl` (*given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left using `function(accumulator, item)`*);
+- `foldr` (*given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right using `function(item, accumulator)`*);
+- `reverse` (*given a list, return a list with all the original items, but in reversed order*);
diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md
index f1215dd38cd..8cbe791fc23 100644
--- a/exercises/practice/luhn/.docs/instructions.md
+++ b/exercises/practice/luhn/.docs/instructions.md
@@ -2,18 +2,15 @@
Given a number determine whether or not it is valid per the Luhn formula.
-The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is
-a simple checksum formula used to validate a variety of identification
-numbers, such as credit card numbers and Canadian Social Insurance
-Numbers.
+The [Luhn algorithm][luhn] is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers.
The task is to check if a given string is valid.
## Validating a Number
-Strings of length 1 or less are not valid. Spaces are allowed in the input,
-but they should be stripped before checking. All other non-digit characters
-are disallowed.
+Strings of length 1 or less are not valid.
+Spaces are allowed in the input, but they should be stripped before checking.
+All other non-digit characters are disallowed.
### Example 1: valid credit card number
@@ -21,15 +18,15 @@ are disallowed.
4539 3195 0343 6467
```
-The first step of the Luhn algorithm is to double every second digit,
-starting from the right. We will be doubling
+The first step of the Luhn algorithm is to double every second digit, starting from the right.
+We will be doubling
```text
4_3_ 3_9_ 0_4_ 6_6_
```
-If doubling the number results in a number greater than 9 then subtract 9
-from the product. The results of our doubling:
+If doubling the number results in a number greater than 9 then subtract 9 from the product.
+The results of our doubling:
```text
8569 6195 0383 3437
@@ -41,7 +38,8 @@ Then sum all of the digits:
8+5+6+9+6+1+9+5+0+3+8+3+3+4+3+7 = 80
```
-If the sum is evenly divisible by 10, then the number is valid. This number is valid!
+If the sum is evenly divisible by 10, then the number is valid.
+This number is valid!
### Example 2: invalid credit card number
@@ -62,3 +60,5 @@ Sum the digits
```
57 is not evenly divisible by 10, so this number is not valid.
+
+[luhn]: https://en.wikipedia.org/wiki/Luhn_algorithm
diff --git a/exercises/practice/markdown/.docs/instructions.md b/exercises/practice/markdown/.docs/instructions.md
index 4819b6c2ff4..9b756d9917b 100644
--- a/exercises/practice/markdown/.docs/instructions.md
+++ b/exercises/practice/markdown/.docs/instructions.md
@@ -2,14 +2,12 @@
Refactor a Markdown parser.
-The markdown exercise is a refactoring exercise. There is code that parses a
-given string with [Markdown
-syntax](https://guides.github.com/features/mastering-markdown/) and returns the
-associated HTML for that string. Even though this code is confusingly written
-and hard to follow, somehow it works and all the tests are passing! Your
-challenge is to re-write this code to make it easier to read and maintain
-while still making sure that all the tests keep passing.
-
-It would be helpful if you made notes of what you did in your refactoring in
-comments so reviewers can see that, but it isn't strictly necessary. The most
-important thing is to make the code better!
+The markdown exercise is a refactoring exercise.
+There is code that parses a given string with [Markdown syntax][markdown] and returns the associated HTML for that string.
+Even though this code is confusingly written and hard to follow, somehow it works and all the tests are passing!
+Your challenge is to re-write this code to make it easier to read and maintain while still making sure that all the tests keep passing.
+
+It would be helpful if you made notes of what you did in your refactoring in comments so reviewers can see that, but it isn't strictly necessary.
+The most important thing is to make the code better!
+
+[markdown]: https://guides.github.com/features/mastering-markdown/
diff --git a/exercises/practice/matching-brackets/.docs/instructions.md b/exercises/practice/matching-brackets/.docs/instructions.md
index 364ecad2139..ca7c8d838e8 100644
--- a/exercises/practice/matching-brackets/.docs/instructions.md
+++ b/exercises/practice/matching-brackets/.docs/instructions.md
@@ -1,5 +1,3 @@
# Instructions
-Given a string containing brackets `[]`, braces `{}`, parentheses `()`,
-or any combination thereof, verify that any and all pairs are matched
-and nested correctly.
+Given a string containing brackets `[]`, braces `{}`, parentheses `()`, or any combination thereof, verify that any and all pairs are matched and nested correctly.
diff --git a/exercises/practice/matrix/.docs/instructions.md b/exercises/practice/matrix/.docs/instructions.md
index 1b2d0f84b08..dadea8acb59 100644
--- a/exercises/practice/matrix/.docs/instructions.md
+++ b/exercises/practice/matrix/.docs/instructions.md
@@ -1,7 +1,6 @@
# Instructions
-Given a string representing a matrix of numbers, return the rows and columns of
-that matrix.
+Given a string representing a matrix of numbers, return the rows and columns of that matrix.
So given a string with embedded newlines like:
@@ -23,10 +22,8 @@ representing this matrix:
your code should be able to spit out:
-- A list of the rows, reading each row left-to-right while moving
- top-to-bottom across the rows,
-- A list of the columns, reading each column top-to-bottom while moving
- from left-to-right.
+- A list of the rows, reading each row left-to-right while moving top-to-bottom across the rows,
+- A list of the columns, reading each column top-to-bottom while moving from left-to-right.
The rows for our example matrix:
diff --git a/exercises/practice/meetup/.docs/instructions.md b/exercises/practice/meetup/.docs/instructions.md
index bff409f8aab..0694ef583c2 100644
--- a/exercises/practice/meetup/.docs/instructions.md
+++ b/exercises/practice/meetup/.docs/instructions.md
@@ -1,19 +1,51 @@
# Instructions
-In this exercise, you will be given a general description of a meetup date and then asked to find the actual meetup date.
+Recurring monthly meetups are generally scheduled on the given weekday of a given week each month.
+In this exercise you will be given the recurring schedule, along with a month and year, and then asked to find the exact date of the meetup.
-Examples of general descriptions are:
+For example a meetup might be scheduled on the _first Monday_ of every month.
+You might then be asked to find the date that this meetup will happen in January 2018.
+In other words, you need to determine the date of the first Monday of January 2018.
-- First Monday of January 2022
-- Third Tuesday of August 2021
-- Teenth Wednesday of May 2022
-- Teenth Sunday of July 2021
-- Last Thursday of November 2021
+Similarly, you might be asked to find:
-The descriptors you are expected to process are: `first`, `second`, `third`, `fourth`, `fifth`, `last`, `teenth`.
+- the third Tuesday of August 2019 (August 20, 2019)
+- the teenth Wednesday of May 2020 (May 13, 2020)
+- the fourth Sunday of July 2021 (July 25, 2021)
+- the last Thursday of November 2022 (November 24, 2022)
+
+The descriptors you are expected to process are: `first`, `second`, `third`, `fourth`, `last`, `teenth`.
Note that descriptor `teenth` is a made-up word.
-There are exactly seven numbered days in a month that end with "teenth" ("thirteenth" to "nineteenth").
-Therefore, it is guaranteed that each day of the week (Monday, Tuesday, ...) will have exactly one numbered day ending with "teenth" each month.
-For example, if given "First Monday of January 2022", the correct meetup date is January 3, 2022.
+It refers to the seven numbers that end in '-teen' in English: 13, 14, 15, 16, 17, 18, and 19.
+But general descriptions of dates use ordinal numbers, e.g. the _first_ Monday, the _third_ Tuesday.
+
+For the numbers ending in '-teen', that becomes:
+
+- 13th (thirteenth)
+- 14th (fourteenth)
+- 15th (fifteenth)
+- 16th (sixteenth)
+- 17th (seventeenth)
+- 18th (eighteenth)
+- 19th (nineteenth)
+
+So there are seven numbers ending in '-teen'.
+And there are also seven weekdays (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday).
+Therefore, it is guaranteed that each day of the week (Monday, Tuesday, ...) will have exactly one numbered day ending with "teen" each month.
+
+If asked to find the teenth Saturday of August, 1953 (or, alternately the "Saturteenth" of August, 1953), we need to look at the calendar for August 1953:
+
+```plaintext
+ August 1953
+Su Mo Tu We Th Fr Sa
+ 1
+ 2 3 4 5 6 7 8
+ 9 10 11 12 13 14 15
+16 17 18 19 20 21 22
+23 24 25 26 27 28 29
+30 31
+```
+
+The Saturday that has a number ending in '-teen' is August 15, 1953.
diff --git a/exercises/practice/minesweeper/.docs/instructions.md b/exercises/practice/minesweeper/.docs/instructions.md
index d1f99c9a9c3..f5f918bdff9 100644
--- a/exercises/practice/minesweeper/.docs/instructions.md
+++ b/exercises/practice/minesweeper/.docs/instructions.md
@@ -2,23 +2,18 @@
Add the mine counts to a completed Minesweeper board.
-Minesweeper is a popular game where the user has to find the mines using
-numeric hints that indicate how many mines are directly adjacent
-(horizontally, vertically, diagonally) to a square.
+Minesweeper is a popular game where the user has to find the mines using numeric hints that indicate how many mines are directly adjacent (horizontally, vertically, diagonally) to a square.
-In this exercise you have to create some code that counts the number of
-mines adjacent to a given empty square and replaces that square with the
-count.
+In this exercise you have to create some code that counts the number of mines adjacent to a given empty square and replaces that square with the count.
-The board is a rectangle composed of blank space (' ') characters. A mine
-is represented by an asterisk ('\*') character.
+The board is a rectangle composed of blank space (' ') characters.
+A mine is represented by an asterisk (`*`) character.
If a given space has no adjacent mines at all, leave that square blank.
## Examples
-For example you may receive a 5 x 4 board like this (empty spaces are
-represented here with the '·' character for display on screen):
+For example you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen):
```text
·*·*·
diff --git a/exercises/practice/nth-prime/.docs/instructions.md b/exercises/practice/nth-prime/.docs/instructions.md
index 30a75216fd5..065e323ab2c 100644
--- a/exercises/practice/nth-prime/.docs/instructions.md
+++ b/exercises/practice/nth-prime/.docs/instructions.md
@@ -2,8 +2,6 @@
Given a number n, determine what the nth prime is.
-By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that
-the 6th prime is 13.
+By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
-If your language provides methods in the standard library to deal with prime
-numbers, pretend they don't exist and implement them yourself.
+If your language provides methods in the standard library to deal with prime numbers, pretend they don't exist and implement them yourself.
diff --git a/exercises/practice/ocr-numbers/.docs/instructions.md b/exercises/practice/ocr-numbers/.docs/instructions.md
index c3a3c514bf4..7beb2577957 100644
--- a/exercises/practice/ocr-numbers/.docs/instructions.md
+++ b/exercises/practice/ocr-numbers/.docs/instructions.md
@@ -1,7 +1,6 @@
# Instructions
-Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is
-represented, or whether it is garbled.
+Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or whether it is garbled.
## Step One
@@ -59,7 +58,8 @@ Is converted to "1234567890"
## Step Four
-Update your program to handle multiple numbers, one per line. When converting several lines, join the lines with commas.
+Update your program to handle multiple numbers, one per line.
+When converting several lines, join the lines with commas.
```text
_ _
@@ -76,4 +76,4 @@ Update your program to handle multiple numbers, one per line. When converting se
```
-Is converted to "123,456,789"
+Is converted to "123,456,789".
diff --git a/exercises/practice/paasio/.docs/instructions.md b/exercises/practice/paasio/.docs/instructions.md
index 67aaefaaa76..5956447487e 100644
--- a/exercises/practice/paasio/.docs/instructions.md
+++ b/exercises/practice/paasio/.docs/instructions.md
@@ -2,13 +2,12 @@
Report network IO statistics.
-You are writing a [PaaS][], and you need a way to bill customers based
-on network and filesystem usage.
+You are writing a [PaaS][paas], and you need a way to bill customers based on network and filesystem usage.
-Create a wrapper for network connections and files that can report IO
-statistics. The wrapper must report:
+Create a wrapper for network connections and files that can report IO statistics.
+The wrapper must report:
- The total number of bytes read/written.
- The total number of read/write operations.
-[PaaS]: http://en.wikipedia.org/wiki/Platform_as_a_service
+[paas]: https://en.wikipedia.org/wiki/Platform_as_a_service
diff --git a/exercises/practice/palindrome-products/.docs/instructions.md b/exercises/practice/palindrome-products/.docs/instructions.md
index fd9a441247b..aac66521ce7 100644
--- a/exercises/practice/palindrome-products/.docs/instructions.md
+++ b/exercises/practice/palindrome-products/.docs/instructions.md
@@ -2,15 +2,14 @@
Detect palindrome products in a given range.
-A palindromic number is a number that remains the same when its digits are
-reversed. For example, `121` is a palindromic number but `112` is not.
+A palindromic number is a number that remains the same when its digits are reversed.
+For example, `121` is a palindromic number but `112` is not.
Given a range of numbers, find the largest and smallest palindromes which
are products of two numbers within that range.
-Your solution should return the largest and smallest palindromes, along with the
-factors of each within the range. If the largest or smallest palindrome has more
-than one pair of factors within the range, then return all the pairs.
+Your solution should return the largest and smallest palindromes, along with the factors of each within the range.
+If the largest or smallest palindrome has more than one pair of factors within the range, then return all the pairs.
## Example 1
@@ -22,12 +21,16 @@ And given the list of all possible products within this range:
The palindrome products are all single digit numbers (in this case):
`[1, 2, 3, 4, 5, 6, 7, 8, 9]`
-The smallest palindrome product is `1`. Its factors are `(1, 1)`.
-The largest palindrome product is `9`. Its factors are `(1, 9)` and `(3, 3)`.
+The smallest palindrome product is `1`.
+Its factors are `(1, 1)`.
+The largest palindrome product is `9`.
+Its factors are `(1, 9)` and `(3, 3)`.
## Example 2
Given the range `[10, 99]` (both inclusive)...
-The smallest palindrome product is `121`. Its factors are `(11, 11)`.
-The largest palindrome product is `9009`. Its factors are `(91, 99)`.
+The smallest palindrome product is `121`.
+Its factors are `(11, 11)`.
+The largest palindrome product is `9009`.
+Its factors are `(91, 99)`.
diff --git a/exercises/practice/pangram/.docs/instructions.md b/exercises/practice/pangram/.docs/instructions.md
index 3957ae542e1..de83d54eb61 100644
--- a/exercises/practice/pangram/.docs/instructions.md
+++ b/exercises/practice/pangram/.docs/instructions.md
@@ -1,9 +1,9 @@
# Instructions
-Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma,
-"every letter") is a sentence using every letter of the alphabet at least once.
+Determine if a sentence is a pangram.
+A pangram (Greek: παν γράμμα, pan gramma, "every letter") is a sentence using every letter of the alphabet at least once.
The best known English pangram is:
+
> The quick brown fox jumps over the lazy dog.
-The alphabet used consists of letters `a` to `z`, inclusive, and is case
-insensitive.
+The alphabet used consists of letters `a` to `z`, inclusive, and is case insensitive.
diff --git a/exercises/practice/perfect-numbers/.docs/instructions.md b/exercises/practice/perfect-numbers/.docs/instructions.md
index 144c9133e45..0dae8867ffd 100644
--- a/exercises/practice/perfect-numbers/.docs/instructions.md
+++ b/exercises/practice/perfect-numbers/.docs/instructions.md
@@ -3,7 +3,9 @@
Determine if a number is perfect, abundant, or deficient based on
Nicomachus' (60 - 120 CE) classification scheme for positive integers.
-The Greek mathematician [Nicomachus](https://en.wikipedia.org/wiki/Nicomachus) devised a classification scheme for positive integers, identifying each as belonging uniquely to the categories of **perfect**, **abundant**, or **deficient** based on their [aliquot sum](https://en.wikipedia.org/wiki/Aliquot_sum). The aliquot sum is defined as the sum of the factors of a number not including the number itself. For example, the aliquot sum of 15 is (1 + 3 + 5) = 9
+The Greek mathematician [Nicomachus][nicomachus] devised a classification scheme for positive integers, identifying each as belonging uniquely to the categories of **perfect**, **abundant**, or **deficient** based on their [aliquot sum][aliquot-sum].
+The aliquot sum is defined as the sum of the factors of a number not including the number itself.
+For example, the aliquot sum of 15 is (1 + 3 + 5) = 9
- **Perfect**: aliquot sum = number
- 6 is a perfect number because (1 + 2 + 3) = 6
@@ -15,4 +17,8 @@ The Greek mathematician [Nicomachus](https://en.wikipedia.org/wiki/Nicomachus) d
- 8 is a deficient number because (1 + 2 + 4) = 7
- Prime numbers are deficient
-Implement a way to determine whether a given number is **perfect**. Depending on your language track, you may also need to implement a way to determine whether a given number is **abundant** or **deficient**.
+Implement a way to determine whether a given number is **perfect**.
+Depending on your language track, you may also need to implement a way to determine whether a given number is **abundant** or **deficient**.
+
+[nicomachus]: https://en.wikipedia.org/wiki/Nicomachus
+[aliquot-sum]: https://en.wikipedia.org/wiki/Aliquot_sum
diff --git a/exercises/practice/phone-number/.docs/instructions.md b/exercises/practice/phone-number/.docs/instructions.md
index 9da4a519601..6d3275cdf2b 100644
--- a/exercises/practice/phone-number/.docs/instructions.md
+++ b/exercises/practice/phone-number/.docs/instructions.md
@@ -2,9 +2,11 @@
Clean up user-entered phone numbers so that they can be sent SMS messages.
-The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. All NANP-countries share the same international country code: `1`.
+The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda.
+All NANP-countries share the same international country code: `1`.
-NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as *area code*, followed by a seven-digit local number. The first three digits of the local number represent the *exchange code*, followed by the unique four-digit number which is the *subscriber number*.
+NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as *area code*, followed by a seven-digit local number.
+The first three digits of the local number represent the *exchange code*, followed by the unique four-digit number which is the *subscriber number*.
The format is usually represented as
diff --git a/exercises/practice/pig-latin/.docs/instructions.md b/exercises/practice/pig-latin/.docs/instructions.md
index bcb1251176c..c9de5ca1865 100644
--- a/exercises/practice/pig-latin/.docs/instructions.md
+++ b/exercises/practice/pig-latin/.docs/instructions.md
@@ -2,17 +2,18 @@
Implement a program that translates from English to Pig Latin.
-Pig Latin is a made-up children's language that's intended to be
-confusing. It obeys a few simple rules (below), but when it's spoken
-quickly it's really difficult for non-children (and non-native speakers)
-to understand.
+Pig Latin is a made-up children's language that's intended to be confusing.
+It obeys a few simple rules (below), but when it's spoken quickly it's really difficult for non-children (and non-native speakers) to understand.
-- **Rule 1**: If a word begins with a vowel sound, add an "ay" sound to the end of the word. Please note that "xr" and "yt" at the beginning of a word make vowel sounds (e.g. "xray" -> "xrayay", "yttria" -> "yttriaay").
-- **Rule 2**: If a word begins with a consonant sound, move it to the end of the word and then add an "ay" sound to the end of the word. Consonant sounds can be made up of multiple consonants, a.k.a. a consonant cluster (e.g. "chair" -> "airchay").
+- **Rule 1**: If a word begins with a vowel sound, add an "ay" sound to the end of the word.
+ Please note that "xr" and "yt" at the beginning of a word make vowel sounds (e.g. "xray" -> "xrayay", "yttria" -> "yttriaay").
+- **Rule 2**: If a word begins with a consonant sound, move it to the end of the word and then add an "ay" sound to the end of the word.
+ Consonant sounds can be made up of multiple consonants, a.k.a. a consonant cluster (e.g. "chair" -> "airchay").
- **Rule 3**: If a word starts with a consonant sound followed by "qu", move it to the end of the word, and then add an "ay" sound to the end of the word (e.g. "square" -> "aresquay").
- **Rule 4**: If a word contains a "y" after a consonant cluster or as the second letter in a two letter word it makes a vowel sound (e.g. "rhythm" -> "ythmrhay", "my" -> "ymay").
-There are a few more rules for edge cases, and there are regional
-variants too.
+There are a few more rules for edge cases, and there are regional variants too.
-See for more details.
+Read more about [Pig Latin on Wikipedia][pig-latin].
+
+[pig-latin]: https://en.wikipedia.org/wiki/Pig_latin
diff --git a/exercises/practice/poker/.docs/instructions.md b/exercises/practice/poker/.docs/instructions.md
index 6a38cf4bc74..492fc4c9e00 100644
--- a/exercises/practice/poker/.docs/instructions.md
+++ b/exercises/practice/poker/.docs/instructions.md
@@ -2,5 +2,6 @@
Pick the best hand(s) from a list of poker hands.
-See [wikipedia](https://en.wikipedia.org/wiki/List_of_poker_hands) for an
-overview of poker hands.
+See [wikipedia][poker-hands] for an overview of poker hands.
+
+[poker-hands]: https://en.wikipedia.org/wiki/List_of_poker_hands
diff --git a/exercises/practice/pov/.docs/instructions.md b/exercises/practice/pov/.docs/instructions.md
index 8c89be730af..0fdeed2250f 100644
--- a/exercises/practice/pov/.docs/instructions.md
+++ b/exercises/practice/pov/.docs/instructions.md
@@ -2,13 +2,11 @@
Reparent a tree on a selected node.
-A [tree][wiki-tree] is a special type of [graph][wiki-graph] where all nodes
-are connected but there are no cycles. That means, there is exactly one path to
-get from one node to another for any pair of nodes.
+A [tree][wiki-tree] is a special type of [graph][wiki-graph] where all nodes are connected but there are no cycles.
+That means, there is exactly one path to get from one node to another for any pair of nodes.
-This exercise is all about re-orientating a tree to see things from a different
-point of view. For example family trees are usually presented from the
-ancestor's perspective:
+This exercise is all about re-orientating a tree to see things from a different point of view.
+For example family trees are usually presented from the ancestor's perspective:
```text
+------0------+
@@ -18,10 +16,9 @@ ancestor's perspective:
4 5 6 7 8 9
```
-But there is no inherent direction in a tree. The same information can be
-presented from the perspective of any other node in the tree, by pulling it up
-to the root and dragging its relationships along with it. So the same tree
-from 6's perspective would look like:
+But there is no inherent direction in a tree.
+The same information can be presented from the perspective of any other node in the tree, by pulling it up to the root and dragging its relationships along with it.
+So the same tree from 6's perspective would look like:
```text
6
@@ -35,12 +32,10 @@ from 6's perspective would look like:
4 5 8 9
```
-This lets us more simply describe the paths between two nodes. So for example
-the path from 6-9 (which in the first tree goes up to the root and then down to
-a different leaf node) can be seen to follow the path 6-2-0-3-9.
+This lets us more simply describe the paths between two nodes.
+So for example the path from 6-9 (which in the first tree goes up to the root and then down to a different leaf node) can be seen to follow the path 6-2-0-3-9.
-This exercise involves taking an input tree and re-orientating it from the point
-of view of one of the nodes.
+This exercise involves taking an input tree and re-orientating it from the point of view of one of the nodes.
[wiki-graph]: https://en.wikipedia.org/wiki/Tree_(graph_theory)
[wiki-tree]: https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)
diff --git a/exercises/practice/prime-factors/.docs/instructions.md b/exercises/practice/prime-factors/.docs/instructions.md
index 494d3dfc5d6..252cc8ee185 100644
--- a/exercises/practice/prime-factors/.docs/instructions.md
+++ b/exercises/practice/prime-factors/.docs/instructions.md
@@ -10,21 +10,27 @@ Note that 1 is not a prime number.
What are the prime factors of 60?
-- Our first divisor is 2. 2 goes into 60, leaving 30.
+- Our first divisor is 2.
+ 2 goes into 60, leaving 30.
- 2 goes into 30, leaving 15.
- - 2 doesn't go cleanly into 15. So let's move on to our next divisor, 3.
+ - 2 doesn't go cleanly into 15.
+ So let's move on to our next divisor, 3.
- 3 goes cleanly into 15, leaving 5.
- - 3 does not go cleanly into 5. The next possible factor is 4.
- - 4 does not go cleanly into 5. The next possible factor is 5.
+ - 3 does not go cleanly into 5.
+ The next possible factor is 4.
+ - 4 does not go cleanly into 5.
+ The next possible factor is 5.
- 5 does go cleanly into 5.
- We're left only with 1, so now, we're done.
-Our successful divisors in that computation represent the list of prime
-factors of 60: 2, 2, 3, and 5.
+Our successful divisors in that computation represent the list of prime factors of 60: 2, 2, 3, and 5.
You can check this yourself:
-- 2 \* 2 \* 3 * 5
-- = 4 * 15
-- = 60
-- Success!
+```text
+2 * 2 * 3 * 5
+= 4 * 15
+= 60
+```
+
+Success!
diff --git a/exercises/practice/protein-translation/.docs/instructions.md b/exercises/practice/protein-translation/.docs/instructions.md
index c211345ed98..d9b9054cf5e 100644
--- a/exercises/practice/protein-translation/.docs/instructions.md
+++ b/exercises/practice/protein-translation/.docs/instructions.md
@@ -11,7 +11,8 @@ Codons: `"AUG", "UUU", "UCU"`
Protein: `"Methionine", "Phenylalanine", "Serine"`
-There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise. If it works for one codon, the program should work for all of them.
+There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise.
+If it works for one codon, the program should work for all of them.
However, feel free to expand the list in the test suite to include them all.
There are also three terminating codons (also known as 'STOP' codons); if any of these codons are encountered (by the ribosome), all translation ends and the protein is terminated.
@@ -39,4 +40,6 @@ UGU, UGC | Cysteine
UGG | Tryptophan
UAA, UAG, UGA | STOP
-Learn more about [protein translation on Wikipedia](http://en.wikipedia.org/wiki/Translation_(biology))
+Learn more about [protein translation on Wikipedia][protein-translation].
+
+[protein-translation]: https://en.wikipedia.org/wiki/Translation_(biology)
diff --git a/exercises/practice/pythagorean-triplet/.docs/instructions.md b/exercises/practice/pythagorean-triplet/.docs/instructions.md
index d74ee4c1796..1c1a8aea61c 100644
--- a/exercises/practice/pythagorean-triplet/.docs/instructions.md
+++ b/exercises/practice/pythagorean-triplet/.docs/instructions.md
@@ -1,7 +1,6 @@
# Instructions
-A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for
-which,
+A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for which,
```text
a² + b² = c²
@@ -16,7 +15,7 @@ a < b < c
For example,
```text
-3² + 4² = 9 + 16 = 25 = 5².
+3² + 4² = 5².
```
Given an input integer N, find all Pythagorean triplets for which `a + b + c = N`.
diff --git a/exercises/practice/rail-fence-cipher/.docs/instructions.md b/exercises/practice/rail-fence-cipher/.docs/instructions.md
index 0e75a2bf70b..e311de6cdfa 100644
--- a/exercises/practice/rail-fence-cipher/.docs/instructions.md
+++ b/exercises/practice/rail-fence-cipher/.docs/instructions.md
@@ -2,15 +2,13 @@
Implement encoding and decoding for the rail fence cipher.
-The Rail Fence cipher is a form of transposition cipher that gets its name from
-the way in which it's encoded. It was already used by the ancient Greeks.
+The Rail Fence cipher is a form of transposition cipher that gets its name from the way in which it's encoded.
+It was already used by the ancient Greeks.
-In the Rail Fence cipher, the message is written downwards on successive "rails"
-of an imaginary fence, then moving up when we get to the bottom (like a zig-zag).
+In the Rail Fence cipher, the message is written downwards on successive "rails" of an imaginary fence, then moving up when we get to the bottom (like a zig-zag).
Finally the message is then read off in rows.
-For example, using three "rails" and the message "WE ARE DISCOVERED FLEE AT ONCE",
-the cipherer writes out:
+For example, using three "rails" and the message "WE ARE DISCOVERED FLEE AT ONCE", the cipherer writes out:
```text
W . . . E . . . C . . . R . . . L . . . T . . . E
diff --git a/exercises/practice/raindrops/.docs/instructions.md b/exercises/practice/raindrops/.docs/instructions.md
index bf09afa33b1..fc61d36e99b 100644
--- a/exercises/practice/raindrops/.docs/instructions.md
+++ b/exercises/practice/raindrops/.docs/instructions.md
@@ -1,6 +1,8 @@
# Instructions
-Your task is to convert a number into a string that contains raindrop sounds corresponding to certain potential factors. A factor is a number that evenly divides into another number, leaving no remainder. The simplest way to test if one number is a factor of another is to use the [modulo operation](https://en.wikipedia.org/wiki/Modulo_operation).
+Your task is to convert a number into a string that contains raindrop sounds corresponding to certain potential factors.
+A factor is a number that evenly divides into another number, leaving no remainder.
+The simplest way to test if one number is a factor of another is to use the [modulo operation][modulo].
The rules of `raindrops` are that if a given number:
@@ -14,3 +16,5 @@ The rules of `raindrops` are that if a given number:
- 28 has 7 as a factor, but not 3 or 5, so the result would be "Plong".
- 30 has both 3 and 5 as factors, but not 7, so the result would be "PlingPlang".
- 34 is not factored by 3, 5, or 7, so the result would be "34".
+
+[modulo]: https://en.wikipedia.org/wiki/Modulo_operation
diff --git a/exercises/practice/rational-numbers/.docs/instructions.md b/exercises/practice/rational-numbers/.docs/instructions.md
index c06841e4d1b..f64fc0f28e5 100644
--- a/exercises/practice/rational-numbers/.docs/instructions.md
+++ b/exercises/practice/rational-numbers/.docs/instructions.md
@@ -31,7 +31,12 @@ Implement the following operations:
- addition, subtraction, multiplication and division of two rational numbers,
- absolute value, exponentiation of a given rational number to an integer power, exponentiation of a given rational number to a real (floating-point) power, exponentiation of a real number to a rational number.
-Your implementation of rational numbers should always be reduced to lowest terms. For example, `4/4` should reduce to `1/1`, `30/60` should reduce to `1/2`, `12/8` should reduce to `3/2`, etc. To reduce a rational number `r = a/b`, divide `a` and `b` by the greatest common divisor (gcd) of `a` and `b`. So, for example, `gcd(12, 8) = 4`, so `r = 12/8` can be reduced to `(12/4)/(8/4) = 3/2`.
-The reduced form of a rational number should be in "standard form" (the denominator should always be a positive integer). If a denominator with a negative integer is present, multiply both numerator and denominator by `-1` to ensure standard form is reached. For example, `3/-4` should be reduced to `-3/4`
+Your implementation of rational numbers should always be reduced to lowest terms.
+For example, `4/4` should reduce to `1/1`, `30/60` should reduce to `1/2`, `12/8` should reduce to `3/2`, etc.
+To reduce a rational number `r = a/b`, divide `a` and `b` by the greatest common divisor (gcd) of `a` and `b`.
+So, for example, `gcd(12, 8) = 4`, so `r = 12/8` can be reduced to `(12/4)/(8/4) = 3/2`.
+The reduced form of a rational number should be in "standard form" (the denominator should always be a positive integer).
+If a denominator with a negative integer is present, multiply both numerator and denominator by `-1` to ensure standard form is reached.
+For example, `3/-4` should be reduced to `-3/4`
Assume that the programming language you are using does not have an implementation of rational numbers.
diff --git a/exercises/practice/react/.docs/instructions.md b/exercises/practice/react/.docs/instructions.md
index 5cad8825f05..1b9a175d0bc 100644
--- a/exercises/practice/react/.docs/instructions.md
+++ b/exercises/practice/react/.docs/instructions.md
@@ -2,15 +2,10 @@
Implement a basic reactive system.
-Reactive programming is a programming paradigm that focuses on how values
-are computed in terms of each other to allow a change to one value to
-automatically propagate to other values, like in a spreadsheet.
+Reactive programming is a programming paradigm that focuses on how values are computed in terms of each other to allow a change to one value to automatically propagate to other values, like in a spreadsheet.
-Implement a basic reactive system with cells with settable values ("input"
-cells) and cells with values computed in terms of other cells ("compute"
-cells). Implement updates so that when an input value is changed, values
-propagate to reach a new stable system state.
+Implement a basic reactive system with cells with settable values ("input" cells) and cells with values computed in terms of other cells ("compute" cells).
+Implement updates so that when an input value is changed, values propagate to reach a new stable system state.
-In addition, compute cells should allow for registering change notification
-callbacks. Call a cell’s callbacks when the cell’s value in a new stable
-state has changed from the previous stable state.
+In addition, compute cells should allow for registering change notification callbacks.
+Call a cell’s callbacks when the cell’s value in a new stable state has changed from the previous stable state.
diff --git a/exercises/practice/rectangles/.docs/instructions.md b/exercises/practice/rectangles/.docs/instructions.md
index 84fc9e5e234..8eb4ed470e6 100644
--- a/exercises/practice/rectangles/.docs/instructions.md
+++ b/exercises/practice/rectangles/.docs/instructions.md
@@ -60,5 +60,4 @@ The above diagram contains these 6 rectangles:
```
-You may assume that the input is always a proper rectangle (i.e. the length of
-every line equals the length of the first line).
+You may assume that the input is always a proper rectangle (i.e. the length of every line equals the length of the first line).
diff --git a/exercises/practice/resistor-color-duo/.docs/instructions.md b/exercises/practice/resistor-color-duo/.docs/instructions.md
index 68550f78039..bdcd549b1a2 100644
--- a/exercises/practice/resistor-color-duo/.docs/instructions.md
+++ b/exercises/practice/resistor-color-duo/.docs/instructions.md
@@ -3,8 +3,8 @@
If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
For this exercise, you need to know two 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.
+- 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 has a position and a numeric value.
@@ -17,16 +17,16 @@ The program will take color names as input and output a two digit number, even i
The band colors are encoded as follows:
-* Black: 0
-* Brown: 1
-* Red: 2
-* Orange: 3
-* Yellow: 4
-* Green: 5
-* Blue: 6
-* Violet: 7
-* Grey: 8
-* White: 9
+- Black: 0
+- Brown: 1
+- Red: 2
+- Orange: 3
+- Yellow: 4
+- Green: 5
+- Blue: 6
+- Violet: 7
+- Grey: 8
+- White: 9
From the example above:
brown-green should return 15
diff --git a/exercises/practice/resistor-color/.docs/instructions.md b/exercises/practice/resistor-color/.docs/instructions.md
index 41ece3f8091..646c14398f0 100644
--- a/exercises/practice/resistor-color/.docs/instructions.md
+++ b/exercises/practice/resistor-color/.docs/instructions.md
@@ -31,6 +31,9 @@ The goal of this exercise is to create a way:
- to look up the numerical value associated with a particular color band
- to list the different band colors
-Mnemonics map the colors to the numbers, that, when stored as an array, happen to map to their index in the array: Better Be Right Or Your Great Big Values Go Wrong.
+Mnemonics map the colors to the numbers, that, when stored as an array, happen to map to their index in the array:
+Better Be Right Or Your Great Big Values Go Wrong.
-More information on the color encoding of resistors can be found in the [Electronic color code Wikipedia article](https://en.wikipedia.org/wiki/Electronic_color_code)
+More information on the color encoding of resistors can be found in the [Electronic color code Wikipedia article][e-color-code].
+
+[e-color-code]: https://en.wikipedia.org/wiki/Electronic_color_code
diff --git a/exercises/practice/rest-api/.docs/instructions.md b/exercises/practice/rest-api/.docs/instructions.md
index c4c0a89aaef..f3b226a73d9 100644
--- a/exercises/practice/rest-api/.docs/instructions.md
+++ b/exercises/practice/rest-api/.docs/instructions.md
@@ -4,7 +4,7 @@ Implement a RESTful API for tracking IOUs.
Four roommates have a habit of borrowing money from each other frequently, and have trouble remembering who owes whom, and how much.
-Your task is to implement a simple [RESTful API](https://en.wikipedia.org/wiki/Representational_state_transfer) that receives [IOU](https://en.wikipedia.org/wiki/IOU)s as POST requests, and can deliver specified summary information via GET requests.
+Your task is to implement a simple [RESTful API][restful-wikipedia] that receives [IOU][iou]s as POST requests, and can deliver specified summary information via GET requests.
## API Specification
@@ -36,7 +36,13 @@ Your task is to implement a simple [RESTful API](https://en.wikipedia.org/wiki/R
## Other Resources
-- [https://restfulapi.net/](https://restfulapi.net/)
+- [REST API Tutorial][restfulapi]
- Example RESTful APIs
- - [GitHub](https://developer.github.com/v3/)
- - [Reddit](https://www.reddit.com/dev/api/)
+ - [GitHub][github-rest]
+ - [Reddit][reddit-rest]
+
+[restful-wikipedia]: https://en.wikipedia.org/wiki/Representational_state_transfer
+[iou]: https://en.wikipedia.org/wiki/IOU
+[github-rest]: https://developer.github.com/v3/
+[reddit-rest]: https://www.reddit.com/dev/api/
+[restfulapi]: https://restfulapi.net/
diff --git a/exercises/practice/rna-transcription/.docs/instructions.md b/exercises/practice/rna-transcription/.docs/instructions.md
index 9e86efea9ee..851bdb49db7 100644
--- a/exercises/practice/rna-transcription/.docs/instructions.md
+++ b/exercises/practice/rna-transcription/.docs/instructions.md
@@ -4,16 +4,13 @@ Given a DNA strand, return its RNA complement (per RNA transcription).
Both DNA and RNA strands are a sequence of nucleotides.
-The four nucleotides found in DNA are adenine (**A**), cytosine (**C**),
-guanine (**G**) and thymine (**T**).
+The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), guanine (**G**) and thymine (**T**).
-The four nucleotides found in RNA are adenine (**A**), cytosine (**C**),
-guanine (**G**) and uracil (**U**).
+The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), guanine (**G**) and uracil (**U**).
-Given a DNA strand, its transcribed RNA strand is formed by replacing
-each nucleotide with its complement:
+Given a DNA strand, its transcribed RNA strand is formed by replacing each nucleotide with its complement:
-* `G` -> `C`
-* `C` -> `G`
-* `T` -> `A`
-* `A` -> `U`
+- `G` -> `C`
+- `C` -> `G`
+- `T` -> `A`
+- `A` -> `U`
diff --git a/exercises/practice/robot-name/.docs/instructions.md b/exercises/practice/robot-name/.docs/instructions.md
index a0079a341e8..fca3a41aecc 100644
--- a/exercises/practice/robot-name/.docs/instructions.md
+++ b/exercises/practice/robot-name/.docs/instructions.md
@@ -4,13 +4,11 @@ Manage robot factory settings.
When a robot comes off the factory floor, it has no name.
-The first time you turn on a robot, a random name is generated in the format
-of two uppercase letters followed by three digits, such as RX837 or BC811.
+The first time you turn on a robot, a random name is generated in the format of two uppercase letters followed by three digits, such as RX837 or BC811.
-Every once in a while we need to reset a robot to its factory settings,
-which means that its name gets wiped. The next time you ask, that robot will
-respond with a new random name.
+Every once in a while we need to reset a robot to its factory settings, which means that its name gets wiped.
+The next time you ask, that robot will respond with a new random name.
The names must be random: they should not follow a predictable sequence.
-Using random names means a risk of collisions. Your solution must ensure that
-every existing robot has a unique name.
+Using random names means a risk of collisions.
+Your solution must ensure that every existing robot has a unique name.
diff --git a/exercises/practice/robot-simulator/.docs/instructions.md b/exercises/practice/robot-simulator/.docs/instructions.md
index 83be50ccc5b..0ac96ce0bdf 100644
--- a/exercises/practice/robot-simulator/.docs/instructions.md
+++ b/exercises/practice/robot-simulator/.docs/instructions.md
@@ -10,13 +10,10 @@ The robots have three possible movements:
- turn left
- advance
-Robots are placed on a hypothetical infinite grid, facing a particular
-direction (north, east, south, or west) at a set of {x,y} coordinates,
+Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates,
e.g., {3,8}, with coordinates increasing to the north and east.
-The robot then receives a number of instructions, at which point the
-testing facility verifies the robot's new position, and in which
-direction it is pointing.
+The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing.
- The letter-string "RAALAL" means:
- Turn right
@@ -24,5 +21,5 @@ direction it is pointing.
- Turn left
- Advance once
- Turn left yet again
-- Say a robot starts at {7, 3} facing north. Then running this stream
- of instructions should leave it at {9, 4} facing west.
+- Say a robot starts at {7, 3} facing north.
+ Then running this stream of instructions should leave it at {9, 4} facing west.
diff --git a/exercises/practice/roman-numerals/.docs/instructions.md b/exercises/practice/roman-numerals/.docs/instructions.md
index 621565cf644..bb7e909dbf2 100644
--- a/exercises/practice/roman-numerals/.docs/instructions.md
+++ b/exercises/practice/roman-numerals/.docs/instructions.md
@@ -2,17 +2,15 @@
Write a function to convert from normal numbers to Roman Numerals.
-The Romans were a clever bunch. They conquered most of Europe and ruled
-it for hundreds of years. They invented concrete and straight roads and
-even bikinis. One thing they never discovered though was the number
-zero. This made writing and dating extensive histories of their exploits
-slightly more challenging, but the system of numbers they came up with
-is still in use today. For example the BBC uses Roman numerals to date
-their programs.
-
-The Romans wrote numbers using letters - I, V, X, L, C, D, M. (notice
-these letters have lots of straight lines and are hence easy to hack
-into stone tablets).
+The Romans were a clever bunch.
+They conquered most of Europe and ruled it for hundreds of years.
+They invented concrete and straight roads and even bikinis.
+One thing they never discovered though was the number zero.
+This made writing and dating extensive histories of their exploits slightly more challenging, but the system of numbers they came up with is still in use today.
+For example the BBC uses Roman numerals to date their programs.
+
+The Romans wrote numbers using letters - I, V, X, L, C, D, M.
+(notice these letters have lots of straight lines and are hence easy to hack into stone tablets).
```text
1 => I
@@ -20,12 +18,10 @@ into stone tablets).
7 => VII
```
-There is no need to be able to convert numbers larger than about 3000.
+The maximum number supported by this notation is 3,999.
(The Romans themselves didn't tend to go any higher)
-Wikipedia says: Modern Roman numerals ... are written by expressing each
-digit separately starting with the left most digit and skipping any
-digit with a value of zero.
+Wikipedia says: Modern Roman numerals ... are written by expressing each digit separately starting with the left most digit and skipping any digit with a value of zero.
To see this in practice, consider the example of 1990.
@@ -40,4 +36,6 @@ In Roman numerals 1990 is MCMXC:
2000=MM
8=VIII
-See also: [http://www.novaroma.org/via_romana/numbers.html](http://www.novaroma.org/via_romana/numbers.html)
+Learn more about [Roman numberals on Wikipedia][roman-numerals].
+
+[roman-numerals]: https://wiki.imperivm-romanvm.com/wiki/Roman_Numerals
diff --git a/exercises/practice/rotational-cipher/.docs/instructions.md b/exercises/practice/rotational-cipher/.docs/instructions.md
index dbf6276f379..4dee51b3555 100644
--- a/exercises/practice/rotational-cipher/.docs/instructions.md
+++ b/exercises/practice/rotational-cipher/.docs/instructions.md
@@ -2,11 +2,9 @@
Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.
-The Caesar cipher is a simple shift cipher that relies on
-transposing all the letters in the alphabet using an integer key
-between `0` and `26`. Using a key of `0` or `26` will always yield
-the same output due to modular arithmetic. The letter is shifted
-for as many values as the value of the key.
+The Caesar cipher is a simple shift cipher that relies on transposing all the letters in the alphabet using an integer key between `0` and `26`.
+Using a key of `0` or `26` will always yield the same output due to modular arithmetic.
+The letter is shifted for as many values as the value of the key.
The general notation for rotational ciphers is `ROT + `.
The most commonly used rotational cipher is `ROT13`.
diff --git a/exercises/practice/run-length-encoding/.docs/instructions.md b/exercises/practice/run-length-encoding/.docs/instructions.md
index 95f7a9d69cf..fc8ce056942 100644
--- a/exercises/practice/run-length-encoding/.docs/instructions.md
+++ b/exercises/practice/run-length-encoding/.docs/instructions.md
@@ -2,8 +2,7 @@
Implement run-length encoding and decoding.
-Run-length encoding (RLE) is a simple form of data compression, where runs
-(consecutive data elements) are replaced by just one data value and count.
+Run-length encoding (RLE) is a simple form of data compression, where runs (consecutive data elements) are replaced by just one data value and count.
For example we can represent the original 53 characters with only 13.
@@ -11,14 +10,11 @@ For example we can represent the original 53 characters with only 13.
"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB"
```
-RLE allows the original data to be perfectly reconstructed from
-the compressed data, which makes it a lossless data compression.
+RLE allows the original data to be perfectly reconstructed from the compressed data, which makes it a lossless data compression.
```text
"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE"
```
-For simplicity, you can assume that the unencoded string will only contain
-the letters A through Z (either lower or upper case) and whitespace. This way
-data to be encoded will never contain any numbers and numbers inside data to
-be decoded always represent the count for the following character.
+For simplicity, you can assume that the unencoded string will only contain the letters A through Z (either lower or upper case) and whitespace.
+This way data to be encoded will never contain any numbers and numbers inside data to be decoded always represent the count for the following character.
diff --git a/exercises/practice/saddle-points/.docs/instructions.md b/exercises/practice/saddle-points/.docs/instructions.md
index 3a22509d94d..920ecffed98 100644
--- a/exercises/practice/saddle-points/.docs/instructions.md
+++ b/exercises/practice/saddle-points/.docs/instructions.md
@@ -14,16 +14,12 @@ So say you have a matrix like so:
It has a saddle point at row 2, column 1.
-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.
+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.
A matrix may have zero or more saddle points.
-Your code should be able to provide the (possibly empty) list of all the
-saddle points for any given matrix.
+Your code should be able to provide the (possibly empty) list of all the saddle points for any given matrix.
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.
+Note that you may find other definitions of matrix saddle points online, but the tests for this exercise follow the above unambiguous definition.
diff --git a/exercises/practice/satellite/.docs/instructions.md b/exercises/practice/satellite/.docs/instructions.md
index 43ebe63ffef..fbbf14f4395 100644
--- a/exercises/practice/satellite/.docs/instructions.md
+++ b/exercises/practice/satellite/.docs/instructions.md
@@ -1,17 +1,15 @@
# Instructions
-Imagine you need to transmit a binary tree to a satellite approaching Alpha
-Centauri and you have limited bandwidth. Since the tree has no repeating
-items it can be uniquely represented by its [pre-order and in-order traversals][wiki].
+Imagine you need to transmit a binary tree to a satellite approaching Alpha Centauri and you have limited bandwidth.
+Since the tree has no repeating items it can be uniquely represented by its [pre-order and in-order traversals][wiki].
Write the software for the satellite to rebuild the tree from the traversals.
-A pre-order traversal reads the value of the current node before (hence "pre")
-reading the left subtree in pre-order. Afterwards the right subtree is read
-in pre-order.
+A pre-order traversal reads the value of the current node before (hence "pre") reading the left subtree in pre-order.
+Afterwards the right subtree is read in pre-order.
-An in-order traversal reads the left subtree in-order then the current node and
-finally the right subtree in-order. So in order from left to right.
+An in-order traversal reads the left subtree in-order then the current node and finally the right subtree in-order.
+So in order from left to right.
For example the pre-order traversal of this tree is [a, i, x, f, r].
The in-order traversal of this tree is [i, a, f, x, r]
diff --git a/exercises/practice/say/.docs/instructions.md b/exercises/practice/say/.docs/instructions.md
index 727b0186db4..aa2e7687fe6 100644
--- a/exercises/practice/say/.docs/instructions.md
+++ b/exercises/practice/say/.docs/instructions.md
@@ -6,11 +6,9 @@ Given a number from 0 to 999,999,999,999, spell out that number in English.
Handle the basic case of 0 through 99.
-If the input to the program is `22`, then the output should be
-`'twenty-two'`.
+If the input to the program is `22`, then the output should be `'twenty-two'`.
-Your program should complain loudly if given a number outside the
-blessed range.
+Your program should complain loudly if given a number outside the blessed range.
Some good test cases for this program are:
@@ -23,15 +21,14 @@ Some good test cases for this program are:
### Extension
-If you're on a Mac, shell out to Mac OS X's `say` program to talk out
-loud. If you're on Linux or Windows, eSpeakNG may be available with the command `espeak`.
+If you're on a Mac, shell out to Mac OS X's `say` program to talk out loud.
+If you're on Linux or Windows, eSpeakNG may be available with the command `espeak`.
## Step 2
Implement breaking a number up into chunks of thousands.
-So `1234567890` should yield a list like 1, 234, 567, and 890, while the
-far simpler `1000` should yield just 1 and 0.
+So `1234567890` should yield a list like 1, 234, 567, and 890, while the far simpler `1000` should yield just 1 and 0.
The program must also report any values that are out of range.
@@ -41,8 +38,8 @@ Now handle inserting the appropriate scale word between those chunks.
So `1234567890` should yield `'1 billion 234 million 567 thousand 890'`
-The program must also report any values that are out of range. It's
-fine to stop at "trillion".
+The program must also report any values that are out of range.
+It's fine to stop at "trillion".
## Step 4
diff --git a/exercises/practice/scale-generator/.docs/instructions.md b/exercises/practice/scale-generator/.docs/instructions.md
index 8a952e29c66..23e06e1ab2f 100644
--- a/exercises/practice/scale-generator/.docs/instructions.md
+++ b/exercises/practice/scale-generator/.docs/instructions.md
@@ -2,23 +2,19 @@
## Chromatic Scales
-Scales in Western music are based on the chromatic (12-note) scale. This
-scale can be expressed as the following group of pitches:
+Scales in Western music are based on the chromatic (12-note) scale.
+This scale can be expressed as the following group of pitches:
> A, A♯, B, C, C♯, D, D♯, E, F, F♯, G, G♯
-A given sharp note (indicated by a ♯) can also be expressed as the flat
-of the note above it (indicated by a ♭) so the chromatic scale can also be
-written like this:
+A given sharp note (indicated by a ♯) can also be expressed as the flat of the note above it (indicated by a ♭) so the chromatic scale can also be written like this:
> A, B♭, B, C, D♭, D, E♭, E, F, G♭, G, A♭
-The major and minor scale and modes are subsets of this twelve-pitch
-collection. They have seven pitches, and are called diatonic scales.
-The collection of notes in these scales is written with either sharps or
-flats, depending on the tonic (starting note). Here is a table indicating
-whether the flat expression or sharp expression of the scale would be used for
-a given tonic:
+The major and minor scale and modes are subsets of this twelve-pitch collection.
+They have seven pitches, and are called diatonic scales.
+The collection of notes in these scales is written with either sharps or flats, depending on the tonic (starting note).
+Here is a table indicating whether the flat expression or sharp expression of the scale would be used for a given tonic:
| Key Signature | Major | Minor |
| ------------- | --------------------- | -------------------- |
@@ -26,46 +22,35 @@ a given tonic:
| Sharp | G, D, A, E, B, F♯ | e, b, f♯, c♯, g♯, d♯ |
| Flat | F, B♭, E♭, A♭, D♭, G♭ | d, g, c, f, b♭, e♭ |
-Note that by common music theory convention the natural notes "C" and "a"
-follow the sharps scale when ascending and the flats scale when descending.
+Note that by common music theory convention the natural notes "C" and "a" follow the sharps scale when ascending and the flats scale when descending.
For the scope of this exercise the scale is only ascending.
### Task
Given a tonic, generate the 12 note chromatic scale starting with the tonic.
-- Shift the base scale appropriately so that all 12 notes are returned
-starting with the given tonic.
-- For the given tonic, determine if the scale is to be returned with flats
-or sharps.
-- Return all notes in uppercase letters (except for the `b` for flats)
-irrespective of the casing of the given tonic.
+- Shift the base scale appropriately so that all 12 notes are returned starting with the given tonic.
+- For the given tonic, determine if the scale is to be returned with flats or sharps.
+- Return all notes in uppercase letters (except for the `b` for flats) irrespective of the casing of the given tonic.
## Diatonic Scales
-The diatonic scales, and all other scales that derive from the
-chromatic scale, are built upon intervals. An interval is the space
-between two pitches.
+The diatonic scales, and all other scales that derive from the chromatic scale, are built upon intervals.
+An interval is the space between two pitches.
-The simplest interval is between two adjacent notes, and is called a
-"half step", or "minor second" (sometimes written as a lower-case "m").
-The interval between two notes that have an interceding note is called
-a "whole step" or "major second" (written as an upper-case "M"). The
-diatonic scales are built using only these two intervals between
-adjacent notes.
+The simplest interval is between two adjacent notes, and is called a "half step", or "minor second" (sometimes written as a lower-case "m").
+The interval between two notes that have an interceding note is called a "whole step" or "major second" (written as an upper-case "M").
+The diatonic scales are built using only these two intervals between adjacent notes.
-Non-diatonic scales can contain other intervals. An "augmented second"
-interval, written "A", has two interceding notes (e.g., from A to C or D♭ to E)
-or a "whole step" plus a "half step". There are also smaller and larger
-intervals, but they will not figure into this exercise.
+Non-diatonic scales can contain other intervals.
+An "augmented second" interval, written "A", has two interceding notes (e.g., from A to C or D♭ to E) or a "whole step" plus a "half step".
+There are also smaller and larger intervals, but they will not figure into this exercise.
### Task
-Given a tonic and a set of intervals, generate the musical scale starting with
-the tonic and following the specified interval pattern.
+Given a tonic and a set of intervals, generate the musical scale starting with the tonic and following the specified interval pattern.
-This is similar to generating chromatic scales except that instead of returning
-12 notes, you will return N+1 notes for N intervals.
+This is similar to generating chromatic scales except that instead of returning 12 notes, you will return N+1 notes for N intervals.
The first note is always the given tonic.
Then, for each interval in the pattern, the next note is determined by starting from the previous note and skipping the number of notes indicated by the interval.
diff --git a/exercises/practice/secret-handshake/.docs/instructions.md b/exercises/practice/secret-handshake/.docs/instructions.md
index d4d57b80d57..2d6937ae964 100644
--- a/exercises/practice/secret-handshake/.docs/instructions.md
+++ b/exercises/practice/secret-handshake/.docs/instructions.md
@@ -3,8 +3,7 @@
> There are 10 types of people in the world: Those who understand
> binary, and those who don't.
-You and your fellow cohort of those in the "know" when it comes to
-binary decide to come up with a secret "handshake".
+You and your fellow cohort of those in the "know" when it comes to binary decide to come up with a secret "handshake".
```text
00001 = wink
@@ -12,7 +11,6 @@ binary decide to come up with a secret "handshake".
00100 = close your eyes
01000 = jump
-
10000 = Reverse the order of the operations in the secret handshake.
```
@@ -20,8 +18,7 @@ Given a decimal number, convert it to the appropriate sequence of events for a s
Here's a couple of examples:
-Given the decimal input 3, the function would return the array
-["wink", "double blink"] because the decimal number 3 is 2+1 in powers of two and thus `11` in binary.
+Given the decimal input 3, the function would return the array ["wink", "double blink"] because the decimal number 3 is 2+1 in powers of two and thus `11` in binary.
Let's now examine the input 19 which is 16+2+1 in powers of two and thus `10011` in binary.
Recalling that the addition of 16 (`10000` in binary) reverses an array and that we already know what array is returned given input 3, the array returned for input 19 is ["double blink", "wink"].
diff --git a/exercises/practice/series/.docs/instructions.md b/exercises/practice/series/.docs/instructions.md
index 3f9d371fa25..e32cc38c676 100644
--- a/exercises/practice/series/.docs/instructions.md
+++ b/exercises/practice/series/.docs/instructions.md
@@ -1,7 +1,6 @@
# Instructions
-Given a string of digits, output all the contiguous substrings of length `n` in
-that string in the order that they appear.
+Given a string of digits, output all the contiguous substrings of length `n` in that string in the order that they appear.
For example, the string "49142" has the following 3-digit series:
@@ -14,8 +13,7 @@ And the following 4-digit series:
- "4914"
- "9142"
-And if you ask for a 6-digit series from a 5-digit string, you deserve
-whatever you get.
+And if you ask for a 6-digit series from a 5-digit string, you deserve whatever you get.
-Note that these series are only required to occupy *adjacent positions*
-in the input; the digits need not be *numerically consecutive*.
+Note that these series are only required to occupy *adjacent positions* in the input;
+the digits need not be *numerically consecutive*.
diff --git a/exercises/practice/sgf-parsing/.docs/instructions.md b/exercises/practice/sgf-parsing/.docs/instructions.md
index 7fc8e7f371a..d38b341fc09 100644
--- a/exercises/practice/sgf-parsing/.docs/instructions.md
+++ b/exercises/practice/sgf-parsing/.docs/instructions.md
@@ -2,14 +2,15 @@
Parsing a Smart Game Format string.
-[SGF](https://en.wikipedia.org/wiki/Smart_Game_Format) is a standard format for
-storing board game files, in particular go.
+[SGF][sgf] is a standard format for storing board game files, in particular go.
SGF is a fairly simple format. An SGF file usually contains a single
tree of nodes where each node is a property list. The property list
contains key value pairs, each key can only occur once but may have
multiple values.
+The exercise will have you parse an SGF string and return a tree structure of properties.
+
An SGF file may look like this:
```text
@@ -53,6 +54,24 @@ A key can have multiple values associated with it. For example:
Here `AB` (add black) is used to add three black stones to the board.
+All property values will be the [SGF Text type][sgf-text].
+You don't need to implement any other value type.
+Although you can read the [full documentation of the Text type][sgf-text], a summary of the important points is below:
+
+- Newlines are removed if they come immediately after a `\`, otherwise they remain as newlines.
+- All whitespace characters other than newline are converted to spaces.
+- `\` is the escape character.
+ Any non-whitespace character after `\` is inserted as-is.
+ Any whitespace character after `\` follows the above rules.
+ Note that SGF does **not** have escape sequences for whitespace characters such as `\t` or `\n`.
+
+Be careful not to get confused between:
+
+- The string as it is represented in a string literal in the tests
+- The string that is passed to the SGF parser
+
+Escape sequences in the string literals may have already been processed by the programming language's parser before they are passed to the SGF parser.
+
There are a few more complexities to SGF (and parsing in general), which
you can mostly ignore. You should assume that the input is encoded in
UTF-8, the tests won't contain a charset property, so don't worry about
@@ -60,7 +79,5 @@ that. Furthermore you may assume that all newlines are unix style (`\n`,
no `\r` or `\r\n` will be in the tests) and that no optional whitespace
between properties, nodes, etc will be in the tests.
-The exercise will have you parse an SGF string and return a tree
-structure of properties. You do not need to encode knowledge about the
-data types of properties, just use the rules for the
-[text](http://www.red-bean.com/sgf/sgf4.html#text) type everywhere.
+[sgf]: https://en.wikipedia.org/wiki/Smart_Game_Format
+[sgf-text]: https://www.red-bean.com/sgf/sgf4.html#text
diff --git a/exercises/practice/sieve/.docs/instructions.md b/exercises/practice/sieve/.docs/instructions.md
index 8384059d84d..c3c0abeb840 100644
--- a/exercises/practice/sieve/.docs/instructions.md
+++ b/exercises/practice/sieve/.docs/instructions.md
@@ -3,12 +3,12 @@
Use the Sieve of Eratosthenes to find all the primes from 2 up to a given
number.
-The Sieve of Eratosthenes is a simple, ancient algorithm for finding all
-prime numbers up to any given limit. It does so by iteratively marking as
-composite (i.e. not prime) the multiples of each prime, starting with the
-multiples of 2. It does not use any division or remainder operation.
+The Sieve of Eratosthenes is a simple, ancient algorithm for finding all prime numbers up to any given limit.
+It does so by iteratively marking as composite (i.e. not prime) the multiples of each prime, starting with the multiples of 2.
+It does not use any division or remainder operation.
-Create your range, starting at two and continuing up to and including the given limit. (i.e. [2, limit])
+Create your range, starting at two and continuing up to and including the given limit.
+(i.e. [2, limit])
The algorithm consists of repeating the following over and over:
@@ -20,11 +20,9 @@ Repeat until you have processed each number in your range.
When the algorithm terminates, all the numbers in the list that have not
been marked are prime.
-The wikipedia article has a useful graphic that explains the algorithm:
-[https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)
+[This wikipedia article][eratosthenes] has a useful graphic that explains the algorithm.
-Notice that this is a very specific algorithm, and the tests don't check
-that you've implemented the algorithm, only that you've come up with the
-correct list of primes. A good first test is to check that you do not use
-division or remainder operations (div, /, mod or % depending on the
-language).
+Notice that this is a very specific algorithm, and the tests don't check that you've implemented the algorithm, only that you've come up with the correct list of primes.
+A good first test is to check that you do not use division or remainder operations (div, /, mod or % depending on the language).
+
+[eratosthenes]: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
diff --git a/exercises/practice/simple-cipher/.docs/instructions.md b/exercises/practice/simple-cipher/.docs/instructions.md
index 22a7e4d4bd0..9167a1d33ad 100644
--- a/exercises/practice/simple-cipher/.docs/instructions.md
+++ b/exercises/practice/simple-cipher/.docs/instructions.md
@@ -4,42 +4,34 @@ Implement a simple shift cipher like Caesar and a more secure substitution ciphe
## Step 1
-"If he had anything confidential to say, he wrote it in cipher, that is,
-by so changing the order of the letters of the alphabet, that not a word
-could be made out. If anyone wishes to decipher these, and get at their
-meaning, he must substitute the fourth letter of the alphabet, namely D,
-for A, and so with the others."
+"If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out.
+If anyone wishes to decipher these, and get at their meaning, he must substitute the fourth letter of the alphabet, namely D, for A, and so with the others."
—Suetonius, Life of Julius Caesar
-Ciphers are very straight-forward algorithms that allow us to render
-text less readable while still allowing easy deciphering. They are
-vulnerable to many forms of cryptanalysis, but we are lucky that
-generally our little sisters are not cryptanalysts.
+Ciphers are very straight-forward algorithms that allow us to render text less readable while still allowing easy deciphering.
+They are vulnerable to many forms of cryptanalysis, but we are lucky that generally our little sisters are not cryptanalysts.
-The Caesar Cipher was used for some messages from Julius Caesar that
-were sent afield. Now Caesar knew that the cipher wasn't very good, but
-he had one ally in that respect: almost nobody could read well. So even
-being a couple letters off was sufficient so that people couldn't
-recognize the few words that they did know.
+The Caesar Cipher was used for some messages from Julius Caesar that were sent afield.
+Now Caesar knew that the cipher wasn't very good, but he had one ally in that respect: almost nobody could read well.
+So even being a couple letters off was sufficient so that people couldn't recognize the few words that they did know.
Your task is to create a simple shift cipher like the Caesar Cipher.
This image is a great example of the Caesar Cipher:
-![Caesar Cipher][1]
+![Caesar Cipher][img-caesar-cipher]
For example:
-Giving "iamapandabear" as input to the encode function returns the cipher "ldpdsdqgdehdu". Obscure enough to keep our message secret in transit.
+Giving "iamapandabear" as input to the encode function returns the cipher "ldpdsdqgdehdu".
+Obscure enough to keep our message secret in transit.
-When "ldpdsdqgdehdu" is put into the decode function it would return
-the original "iamapandabear" letting your friend read your original
-message.
+When "ldpdsdqgdehdu" is put into the decode function it would return the original "iamapandabear" letting your friend read your original message.
## Step 2
-Shift ciphers are no fun though when your kid sister figures it out. Try
-amending the code to allow us to specify a key and use that for the
-shift distance. This is called a substitution cipher.
+Shift ciphers are no fun though when your kid sister figures it out.
+Try amending the code to allow us to specify a key and use that for the shift distance.
+This is called a substitution cipher.
Here's an example:
@@ -49,31 +41,26 @@ would return the original "iamapandabear".
Given the key "ddddddddddddddddd", encoding our string "iamapandabear"
would return the obscured "ldpdsdqgdehdu"
-In the example above, we've set a = 0 for the key value. So when the
-plaintext is added to the key, we end up with the same message coming
-out. So "aaaa" is not an ideal key. But if we set the key to "dddd", we
-would get the same thing as the Caesar Cipher.
+In the example above, we've set a = 0 for the key value.
+So when the plaintext is added to the key, we end up with the same message coming out.
+So "aaaa" is not an ideal key.
+But if we set the key to "dddd", we would get the same thing as the Caesar Cipher.
## Step 3
-The weakest link in any cipher is the human being. Let's make your
-substitution cipher a little more fault tolerant by providing a source
-of randomness and ensuring that the key contains only lowercase letters.
+The weakest link in any cipher is the human being.
+Let's make your substitution cipher a little more fault tolerant by providing a source of randomness and ensuring that the key contains only lowercase letters.
-If someone doesn't submit a key at all, generate a truly random key of
-at least 100 lowercase characters in length.
+If someone doesn't submit a key at all, generate a truly random key of at least 100 lowercase characters in length.
## Extensions
-Shift ciphers work by making the text slightly odd, but are vulnerable
-to frequency analysis. Substitution ciphers help that, but are still
-very vulnerable when the key is short or if spaces are preserved. Later
-on you'll see one solution to this problem in the exercise
-"crypto-square".
+Shift ciphers work by making the text slightly odd, but are vulnerable to frequency analysis.
+Substitution ciphers help that, but are still very vulnerable when the key is short or if spaces are preserved.
+Later on you'll see one solution to this problem in the exercise "crypto-square".
-If you want to go farther in this field, the questions begin to be about
-how we can exchange keys in a secure way. Take a look at [Diffie-Hellman
-on Wikipedia][dh] for one of the first implementations of this scheme.
+If you want to go farther in this field, the questions begin to be about how we can exchange keys in a secure way.
+Take a look at [Diffie-Hellman on Wikipedia][dh] for one of the first implementations of this scheme.
-[1]: https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Caesar_cipher_left_shift_of_3.svg/320px-Caesar_cipher_left_shift_of_3.svg.png
-[dh]: http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
+[img-caesar-cipher]: https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Caesar_cipher_left_shift_of_3.svg/320px-Caesar_cipher_left_shift_of_3.svg.png
+[dh]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
diff --git a/exercises/practice/simple-linked-list/.docs/instructions.md b/exercises/practice/simple-linked-list/.docs/instructions.md
index 1c9d0b3de9e..4d845fac06c 100644
--- a/exercises/practice/simple-linked-list/.docs/instructions.md
+++ b/exercises/practice/simple-linked-list/.docs/instructions.md
@@ -2,21 +2,14 @@
Write a simple linked list implementation that uses Elements and a List.
-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.
+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.
-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.
+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.
-This variant of linked lists is often used to represent sequences or
-push-down stacks (also called a LIFO stack; Last In, First Out).
+This variant of linked lists is often used to represent sequences or push-down stacks (also called a LIFO stack; Last In, First Out).
-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.
+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.
-When implementing this in a language with built-in linked lists,
-implement your own abstract data type.
+When implementing this in a language with built-in linked lists, implement your own abstract data type.
diff --git a/exercises/practice/space-age/.docs/instructions.md b/exercises/practice/space-age/.docs/instructions.md
index 9e48f0ecd1e..405a6dfb46b 100644
--- a/exercises/practice/space-age/.docs/instructions.md
+++ b/exercises/practice/space-age/.docs/instructions.md
@@ -14,5 +14,6 @@ Given an age in seconds, calculate how old someone would be on:
So if you were told someone were 1,000,000,000 seconds old, you should
be able to say that they're 31.69 Earth-years old.
-If you're wondering why Pluto didn't make the cut, go watch [this
-YouTube video](http://www.youtube.com/watch?v=Z_2gbGXzFbs).
+If you're wondering why Pluto didn't make the cut, go watch [this YouTube video][pluto-video].
+
+[pluto-video]: https://www.youtube.com/watch?v=Z_2gbGXzFbs
diff --git a/exercises/practice/spiral-matrix/.docs/instructions.md b/exercises/practice/spiral-matrix/.docs/instructions.md
index 0e7674ff115..ba99e12c731 100644
--- a/exercises/practice/spiral-matrix/.docs/instructions.md
+++ b/exercises/practice/spiral-matrix/.docs/instructions.md
@@ -2,9 +2,7 @@
Given the size, return a square matrix of numbers in spiral order.
-The matrix should be filled with natural numbers, starting from 1
-in the top-left corner, increasing in an inward, clockwise spiral order,
-like these examples:
+The matrix should be filled with natural numbers, starting from 1 in the top-left corner, increasing in an inward, clockwise spiral order, like these examples:
## Examples
diff --git a/exercises/practice/sum-of-multiples/.docs/instructions.md b/exercises/practice/sum-of-multiples/.docs/instructions.md
index bb512396aa3..ff7fdffd86a 100644
--- a/exercises/practice/sum-of-multiples/.docs/instructions.md
+++ b/exercises/practice/sum-of-multiples/.docs/instructions.md
@@ -1,9 +1,7 @@
# Instructions
-Given a number, find the sum of all the unique multiples of particular numbers up to
-but not including that number.
+Given a number, find the sum of all the unique multiples of particular numbers up to but not including that number.
-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.
+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.
The sum of these multiples is 78.
diff --git a/exercises/practice/tournament/.docs/instructions.md b/exercises/practice/tournament/.docs/instructions.md
index 8831dd195eb..e5ca237385f 100644
--- a/exercises/practice/tournament/.docs/instructions.md
+++ b/exercises/practice/tournament/.docs/instructions.md
@@ -2,8 +2,7 @@
Tally the results of a small football competition.
-Based on an input file containing which team played against which and what the
-outcome was, create a file with a table like this:
+Based on an input file containing which team played against which and what the outcome was, create a file with a table like this:
```text
Team | MP | W | D | L | P
@@ -21,9 +20,12 @@ What do those abbreviations mean?
- L: Matches Lost
- P: Points
-A win earns a team 3 points. A draw earns 1. A loss earns 0.
+A win earns a team 3 points.
+A draw earns 1.
+A loss earns 0.
-The outcome should be ordered by points, descending. In case of a tie, teams are ordered alphabetically.
+The outcome is ordered by points, descending.
+In case of a tie, teams are ordered alphabetically.
## Input
@@ -38,7 +40,8 @@ Blithering Badgers;Devastating Donkeys;loss
Allegoric Alaskans;Courageous Californians;win
```
-The result of the match refers to the first team listed. So this line:
+The result of the match refers to the first team listed.
+So this line:
```text
Allegoric Alaskans;Blithering Badgers;win
diff --git a/exercises/practice/transpose/.docs/instructions.md b/exercises/practice/transpose/.docs/instructions.md
index c0e1d14a541..6033af745f4 100644
--- a/exercises/practice/transpose/.docs/instructions.md
+++ b/exercises/practice/transpose/.docs/instructions.md
@@ -17,7 +17,8 @@ BE
CF
```
-Rows become columns and columns become rows. See .
+Rows become columns and columns become rows.
+See [transpose][].
If the input has rows of different lengths, this is to be solved as follows:
@@ -55,5 +56,6 @@ BE
```
In general, all characters from the input should also be present in the transposed output.
-That means that if a column in the input text contains only spaces on its bottom-most row(s),
-the corresponding output row should contain the spaces in its right-most column(s).
+That means that if a column in the input text contains only spaces on its bottom-most row(s), the corresponding output row should contain the spaces in its right-most column(s).
+
+[transpose]: https://en.wikipedia.org/wiki/Transpose
diff --git a/exercises/practice/tree-building/.docs/instructions.md b/exercises/practice/tree-building/.docs/instructions.md
index 32c6087b43d..0148e8a010e 100644
--- a/exercises/practice/tree-building/.docs/instructions.md
+++ b/exercises/practice/tree-building/.docs/instructions.md
@@ -2,17 +2,14 @@
Refactor a tree building algorithm.
-Some web-forums have a tree layout, so posts are presented as a tree. However
-the posts are typically stored in a database as an unsorted set of records. Thus
-when presenting the posts to the user the tree structure has to be
-reconstructed.
+Some web-forums have a tree layout, so posts are presented as a tree.
+However the posts are typically stored in a database as an unsorted set of records.
+Thus when presenting the posts to the user the tree structure has to be reconstructed.
-Your job will be to refactor a working but slow and ugly piece of code that
-implements the tree building logic for highly abstracted records. The records
-only contain an ID number and a parent ID number. The ID number is always
-between 0 (inclusive) and the length of the record list (exclusive). All records
-have a parent ID lower than their own ID, except for the root record, which has
-a parent ID that's equal to its own ID.
+Your job will be to refactor a working but slow and ugly piece of code that implements the tree building logic for highly abstracted records.
+The records only contain an ID number and a parent ID number.
+The ID number is always between 0 (inclusive) and the length of the record list (exclusive).
+All records have a parent ID lower than their own ID, except for the root record, which has a parent ID that's equal to its own ID.
An example tree:
diff --git a/exercises/practice/triangle/.docs/instructions.md b/exercises/practice/triangle/.docs/instructions.md
index af1060f6eab..ac39008726d 100644
--- a/exercises/practice/triangle/.docs/instructions.md
+++ b/exercises/practice/triangle/.docs/instructions.md
@@ -4,9 +4,8 @@ Determine if a triangle is equilateral, isosceles, or scalene.
An _equilateral_ triangle has all three sides the same length.
-An _isosceles_ triangle has at least two sides the same length. (It is sometimes
-specified as having exactly two sides the same length, but for the purposes of
-this exercise we'll say at least two.)
+An _isosceles_ triangle has at least two sides the same length.
+(It is sometimes specified as having exactly two sides the same length, but for the purposes of this exercise we'll say at least two.)
A _scalene_ triangle has all sides of different lengths.
@@ -16,7 +15,8 @@ For a shape to be a triangle at all, all sides have to be of length > 0, and the
In equations:
-Let `a`, `b`, and `c` be sides of the triangle. Then all three of the following expressions must be true:
+Let `a`, `b`, and `c` be sides of the triangle.
+Then all three of the following expressions must be true:
```text
a + b ≥ c
@@ -24,10 +24,6 @@ b + c ≥ a
a + c ≥ b
```
-See [Triangle Inequality](https://en.wikipedia.org/wiki/Triangle_inequality).
+See [Triangle Inequality][triangle-inequality]
-## Dig Deeper
-
-The case where the sum of the lengths of two sides _equals_ that of the
-third is known as a _degenerate_ triangle - it has zero area and looks like
-a single line. Feel free to add your own code/tests to check for degenerate triangles.
+[triangle-inequality]: https://en.wikipedia.org/wiki/Triangle_inequality
diff --git a/exercises/practice/twelve-days/.docs/instructions.md b/exercises/practice/twelve-days/.docs/instructions.md
index ce43aa3034e..83bb6e1926b 100644
--- a/exercises/practice/twelve-days/.docs/instructions.md
+++ b/exercises/practice/twelve-days/.docs/instructions.md
@@ -2,7 +2,8 @@
Your task in this exercise is to write code that returns the lyrics of the song: "The Twelve Days of Christmas."
-"The Twelve Days of Christmas" is a common English Christmas carol. Each subsequent verse of the song builds on the previous verse.
+"The Twelve Days of Christmas" is a common English Christmas carol.
+Each subsequent verse of the song builds on the previous verse.
The lyrics your code returns should _exactly_ match the full song text shown below.
diff --git a/exercises/practice/two-bucket/.docs/instructions.md b/exercises/practice/two-bucket/.docs/instructions.md
index 01c58ad773e..7249deb361c 100644
--- a/exercises/practice/two-bucket/.docs/instructions.md
+++ b/exercises/practice/two-bucket/.docs/instructions.md
@@ -29,9 +29,18 @@ Your program should determine:
Note: any time a change is made to either or both buckets counts as one (1) action.
Example:
-Bucket one can hold up to 7 liters, and bucket two can hold up to 11 liters. Let's say at a given step, bucket one is holding 7 liters and bucket two is holding 8 liters (7,8). If you empty bucket one and make no change to bucket two, leaving you with 0 liters and 8 liters respectively (0,8), that counts as one action. Instead, if you had poured from bucket one into bucket two until bucket two was full, resulting in 4 liters in bucket one and 11 liters in bucket two (4,11), that would also only count as one action.
+Bucket one can hold up to 7 liters, and bucket two can hold up to 11 liters.
+Let's say at a given step, bucket one is holding 7 liters and bucket two is holding 8 liters (7,8).
+If you empty bucket one and make no change to bucket two, leaving you with 0 liters and 8 liters respectively (0,8), that counts as one action.
+Instead, if you had poured from bucket one into bucket two until bucket two was full, resulting in 4 liters in bucket one and 11 liters in bucket two (4,11), that would also only count as one action.
Another Example:
-Bucket one can hold 3 liters, and bucket two can hold up to 5 liters. You are told you must start with bucket one. So your first action is to fill bucket one. You choose to empty bucket one for your second action. For your third action, you may not fill bucket two, because this violates the third rule -- you may not end up in a state after any action where the starting bucket is empty and the other bucket is full.
+Bucket one can hold 3 liters, and bucket two can hold up to 5 liters.
+You are told you must start with bucket one.
+So your first action is to fill bucket one.
+You choose to empty bucket one for your second action.
+For your third action, you may not fill bucket two, because this violates the third rule -- you may not end up in a state after any action where the starting bucket is empty and the other bucket is full.
-Written with <3 at [Fullstack Academy](http://www.fullstackacademy.com/) by Lindsay Levine.
+Written with <3 at [Fullstack Academy][fullstack] by Lindsay Levine.
+
+[fullstack]: https://www.fullstackacademy.com/
diff --git a/exercises/practice/two-fer/.docs/instructions.md b/exercises/practice/two-fer/.docs/instructions.md
index f4853c54dea..bdd72bde111 100644
--- a/exercises/practice/two-fer/.docs/instructions.md
+++ b/exercises/practice/two-fer/.docs/instructions.md
@@ -1,6 +1,7 @@
# Instructions
-`Two-fer` or `2-fer` is short for two for one. One for you and one for me.
+`Two-fer` or `2-fer` is short for two for one.
+One for you and one for me.
Given a name, return a string with the message:
diff --git a/exercises/practice/variable-length-quantity/.docs/instructions.md b/exercises/practice/variable-length-quantity/.docs/instructions.md
index eadce28d0e4..50125482681 100644
--- a/exercises/practice/variable-length-quantity/.docs/instructions.md
+++ b/exercises/practice/variable-length-quantity/.docs/instructions.md
@@ -2,10 +2,10 @@
Implement variable length quantity encoding and decoding.
-The goal of this exercise is to implement [VLQ](https://en.wikipedia.org/wiki/Variable-length_quantity) encoding/decoding.
+The goal of this exercise is to implement [VLQ][vlq] encoding/decoding.
In short, the goal of this encoding is to encode integer values in a way that would save bytes.
-Only the first 7 bits of each byte is significant (right-justified; sort of like an ASCII byte).
+Only the first 7 bits of each byte are significant (right-justified; sort of like an ASCII byte).
So, if you have a 32-bit value, you have to unpack it into a series of 7-bit bytes.
Of course, you will have a variable number of bytes depending upon your integer.
To indicate which is the last byte of the series, you leave bit #7 clear.
@@ -30,3 +30,5 @@ Here are examples of integers as 32-bit values, and the variable length quantiti
08000000 C0 80 80 00
0FFFFFFF FF FF FF 7F
```
+
+[vlq]: https://en.wikipedia.org/wiki/Variable-length_quantity
diff --git a/exercises/practice/word-count/.docs/instructions.md b/exercises/practice/word-count/.docs/instructions.md
index d3548e5d888..8b7f03ede7a 100644
--- a/exercises/practice/word-count/.docs/instructions.md
+++ b/exercises/practice/word-count/.docs/instructions.md
@@ -12,7 +12,7 @@ When counting words you can assume the following rules:
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 ignored
+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", " ")
For example, for the phrase `"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` the count would be:
diff --git a/exercises/practice/word-search/.docs/instructions.md b/exercises/practice/word-search/.docs/instructions.md
index 345fa592eff..e2d08aa9ee6 100644
--- a/exercises/practice/word-search/.docs/instructions.md
+++ b/exercises/practice/word-search/.docs/instructions.md
@@ -1,7 +1,6 @@
# Instructions
-In word search puzzles you get a square of letters and have to find specific
-words in them.
+In word search puzzles you get a square of letters and have to find specific words in them.
For example:
@@ -20,8 +19,6 @@ clojurermt
There are several programming languages hidden in the above square.
-Words can be hidden in all kinds of directions: left-to-right, right-to-left,
-vertical and diagonal.
+Words can be hidden in all kinds of directions: left-to-right, right-to-left, vertical and diagonal.
-Given a puzzle and a list of words return the location of the first and last
-letter of each word.
+Given a puzzle and a list of words return the location of the first and last letter of each word.
diff --git a/exercises/practice/wordy/.docs/instructions.md b/exercises/practice/wordy/.docs/instructions.md
index f65b05acf50..0b9e67b6ca8 100644
--- a/exercises/practice/wordy/.docs/instructions.md
+++ b/exercises/practice/wordy/.docs/instructions.md
@@ -40,8 +40,7 @@ Now, perform the other three operations.
Handle a set of operations, in sequence.
-Since these are verbal word problems, evaluate the expression from
-left-to-right, _ignoring the typical order of operations._
+Since these are verbal word problems, evaluate the expression from left-to-right, _ignoring the typical order of operations._
> What is 5 plus 13 plus 6?
@@ -55,14 +54,6 @@ left-to-right, _ignoring the typical order of operations._
The parser should reject:
-* Unsupported operations ("What is 52 cubed?")
-* Non-math questions ("Who is the President of the United States")
-* Word problems with invalid syntax ("What is 1 plus plus 2?")
-
-## Bonus — Exponentials
-
-If you'd like, handle exponentials.
-
-> What is 2 raised to the 5th power?
-
-32
+- Unsupported operations ("What is 52 cubed?")
+- Non-math questions ("Who is the President of the United States")
+- Word problems with invalid syntax ("What is 1 plus plus 2?")
diff --git a/exercises/practice/yacht/.docs/instructions.md b/exercises/practice/yacht/.docs/instructions.md
index d5b2b283ead..163ba3792c4 100644
--- a/exercises/practice/yacht/.docs/instructions.md
+++ b/exercises/practice/yacht/.docs/instructions.md
@@ -1,10 +1,8 @@
# Instructions
-The dice game [Yacht](https://en.wikipedia.org/wiki/Yacht_(dice_game)) is from
-the same family as Poker Dice, Generala and particularly Yahtzee, of which it
-is a precursor. In the game, five dice are rolled and the result can be entered
-in any of twelve categories. The score of a throw of the dice depends on
-category chosen.
+The dice game [Yacht][yacht] is from the same family as Poker Dice, Generala and particularly Yahtzee, of which it is a precursor.
+In the game, five dice are rolled and the result can be entered in any of twelve categories.
+The score of a throw of the dice depends on category chosen.
## Scores in Yacht
@@ -24,13 +22,14 @@ category chosen.
| Yacht | 50 points | All five dice showing the same face | 4 4 4 4 4 scores 50 |
If the dice do not satisfy the requirements of a category, the score is zero.
-If, for example, *Four Of A Kind* is entered in the *Yacht* category, zero
-points are scored. A *Yacht* scores zero if entered in the *Full House* category.
+If, for example, *Four Of A Kind* is entered in the *Yacht* category, zero points are scored.
+A *Yacht* scores zero if entered in the *Full House* category.
## Task
-Given a list of values for five dice and a category, your solution should return
-the score of the dice for that category. If the dice do not satisfy the requirements
-of the category your solution should return 0. You can assume that five values
-will always be presented, and the value of each will be between one and six
-inclusively. You should not assume that the dice are ordered.
+Given a list of values for five dice and a category, your solution should return the score of the dice for that category.
+If the dice do not satisfy the requirements of the category your solution should return 0.
+You can assume that five values will always be presented, and the value of each will be between one and six inclusively.
+You should not assume that the dice are ordered.
+
+[yacht]: https://en.wikipedia.org/wiki/Yacht_(dice_game)
diff --git a/exercises/practice/zebra-puzzle/.docs/instructions.md b/exercises/practice/zebra-puzzle/.docs/instructions.md
index 18a8da1d873..6d62d18e4cc 100644
--- a/exercises/practice/zebra-puzzle/.docs/instructions.md
+++ b/exercises/practice/zebra-puzzle/.docs/instructions.md
@@ -18,9 +18,7 @@ Solve the zebra puzzle.
14. The Japanese smokes Parliaments.
15. The Norwegian lives next to the blue house.
-Each of the five houses is painted a different color, and their
-inhabitants are of different national extractions, own different pets,
-drink different beverages and smoke different brands of cigarettes.
+Each of the five houses is painted a different color, and their inhabitants are of different national extractions, own different pets, drink different beverages and smoke different brands of cigarettes.
Which of the residents drinks water?
Who owns the zebra?
diff --git a/exercises/practice/zipper/.docs/instructions.md b/exercises/practice/zipper/.docs/instructions.md
index d760aa0dd8a..5445db00357 100644
--- a/exercises/practice/zipper/.docs/instructions.md
+++ b/exercises/practice/zipper/.docs/instructions.md
@@ -2,13 +2,10 @@
Creating a zipper for a binary tree.
-[Zippers](https://en.wikipedia.org/wiki/Zipper_%28data_structure%29) are
-a purely functional way of navigating within a data structure and
-manipulating it. They essentially contain a data structure and a
-pointer into that data structure (called the focus).
+[Zippers][zipper] are a purely functional way of navigating within a data structure and manipulating it.
+They essentially contain a data structure and a pointer into that data structure (called the focus).
-For example given a rose tree (where each node contains a value and a
-list of child nodes) a zipper might support these operations:
+For example given a rose tree (where each node contains a value and a list of child nodes) a zipper might support these operations:
- `from_tree` (get a zipper out of a rose tree, the focus is on the root node)
- `to_tree` (get the rose tree out of the zipper)
@@ -26,3 +23,5 @@ list of child nodes) a zipper might support these operations:
- `delete` (removes the focus node and all subtrees, focus moves to the
`next` node if possible otherwise to the `prev` node if possible,
otherwise to the parent node, returns a new zipper)
+
+[zipper]: https://en.wikipedia.org/wiki/Zipper_%28data_structure%29
From 40de059a6a4b67409e2b8be8163fd22a228cdedd Mon Sep 17 00:00:00 2001
From: Corey
Date: Sun, 20 Nov 2022 10:25:55 -0500
Subject: [PATCH 133/932] Corrected parameter type stated in docstring
---
exercises/concept/cater-waiter/sets.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/cater-waiter/sets.py b/exercises/concept/cater-waiter/sets.py
index faa0114d9c5..b0202e6a5fb 100644
--- a/exercises/concept/cater-waiter/sets.py
+++ b/exercises/concept/cater-waiter/sets.py
@@ -14,7 +14,7 @@ def clean_ingredients(dish_name, dish_ingredients):
"""Remove duplicates from `dish_ingredients`.
:param dish_name: str - containing the dish name.
- :param dish_ingredients: set - dish ingredients.
+ :param dish_ingredients: list - dish ingredients.
:return: tuple - containing (dish_name, ingredient set).
This function should return a `tuple` with the name of the dish as the first item,
From 51b91d3796a63463982506361bf511825cb558c6 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Thu, 24 Nov 2022 08:30:37 +0100
Subject: [PATCH 134/932] [New Concept Exercise] : Linus the Locomotive
Engineer (Packing, Unpacking and Multiple Assignment) (#3212)
* Update introduction.md
* Update about.md
* Added authers
* Added filles for exercise
* Progress
* Merged 2 and 3
* typo fix
* Fix file_names and added tests for 1 exericse
* Update locomotive_engineer.py
* Added zip, added tests for exercise 2
* Adds test for exercise for 3 and 4
* fixes
* Added fifth exercise
* Fix typo
* Adding Exercise Slug to Config.json
Added exercise to track config.
* Blurb and Design Doc
Expanded the blurb and added in the design notes.
* Placeholder for concept intro and task changes
Put in placeholder for concept intro (still needs editing down).
Re-arranged task 2.
Attempted to re-arrange task 5 (still working on it)
* fixes
* Further updates to task 5
* fix
* Updated test for exericse 5
* Update instructions.md
* uypdated exercise 3
* Updated task instructions
* removed notes
* Added more tests for exercise 5
* Updated exercise intro and concept intro
* Added some more hints
* Added more hints
* some more
* Added blurb
* Added Links
Also edited exemplar and a few other things.
* Small updates
Rephrased the unpacking definition across about.md, introduction.md, and the concept blurb.
* spell fixes
* spell fix
* Replaced array with list
* fix typo
* Swapped although
* fix typos
* fix
* Grammar and Rephrasing for About.md
* additional edits for *args **kwargs
* Adjustments line 273 and 145
* Update about.md
* Final Edits
Final edits before community review.
Includes multiple potential solutions to task 5.
When done reviewing/selecting the code for the exemplar, please delete the code in the stub file and un-comment the stubbed functions and docstrings. Thanks!
* Blurb Edit
`dictionary` to `dict`.
* UnEdit
Forgot we can't use backticks in a blurb, so needed to remove them.
* Clean up
* Uncomment stub
Co-authored-by: BethanyG
---
.../.meta/config.json | 4 +-
.../about.md | 355 +++++++++++++++++-
.../introduction.md | 22 +-
.../links.json | 24 +-
config.json | 8 +
.../locomotive-engineer/.docs/hints.md | 35 ++
.../locomotive-engineer/.docs/instructions.md | 110 ++++++
.../locomotive-engineer/.docs/introduction.md | 289 ++++++++++++++
.../locomotive-engineer/.meta/config.json | 11 +
.../locomotive-engineer/.meta/design.md | 69 ++++
.../locomotive-engineer/.meta/exemplar.py | 48 +++
.../locomotive_engineer.py | 38 ++
.../locomotive_engineer_test.py | 82 ++++
13 files changed, 1083 insertions(+), 12 deletions(-)
create mode 100644 exercises/concept/locomotive-engineer/.docs/hints.md
create mode 100644 exercises/concept/locomotive-engineer/.docs/instructions.md
create mode 100644 exercises/concept/locomotive-engineer/.docs/introduction.md
create mode 100644 exercises/concept/locomotive-engineer/.meta/config.json
create mode 100644 exercises/concept/locomotive-engineer/.meta/design.md
create mode 100644 exercises/concept/locomotive-engineer/.meta/exemplar.py
create mode 100644 exercises/concept/locomotive-engineer/locomotive_engineer.py
create mode 100644 exercises/concept/locomotive-engineer/locomotive_engineer_test.py
diff --git a/concepts/unpacking-and-multiple-assignment/.meta/config.json b/concepts/unpacking-and-multiple-assignment/.meta/config.json
index 9b9e8da5a9b..ecf013c7acc 100644
--- a/concepts/unpacking-and-multiple-assignment/.meta/config.json
+++ b/concepts/unpacking-and-multiple-assignment/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "cmccandless"],
+ "blurb": "Unpacking is the process of extracting individual elements of a collection, such as a list, tuple, or dictionary by iterating over them. Unpacked values can be assigned to variables within the same step. Multiple assignment is the ability to assign values to multiple variables in one line.",
+ "authors": ["meatball","bethanyg"],
"contributors": []
}
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index c628150d565..68c8e0662bd 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -1,2 +1,355 @@
-#TODO: Add about for this concept.
+# Unpacking and Multiple Assignment
+Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration.
+Unpacked values can be assigned to variables within the same step.
+With unpacking, there are some special operators used: `*` and `**`.
+When unpacking a list or tuple, the `*` operator can be used to assign all the remaining elements to a variable.
+When unpacking a dictionary, the `**` operator can be used to assign all the remaining key-value pairs to a variable.
+
+When these operators are used without a collection they will _pack_ a number of values into a `list`, `tuple`, or dictionary.
+It is common to use this kind of behavior when creating functions that take an arbitrary number of arguments.
+
+Multiple assignment is the ability to assign multiple variables in one line.
+This is done by separating the variables with a comma.
+
+```exercism/caution
+`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
+```
+
+## Multiple assignment
+
+[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one line.
+This allows for code to be more concise and readable.
+There has to be x number of variables on the left side of the `=` sign and x number of values on the right side of the `=` sign.
+To separate the values, use a comma `,`:
+
+```python
+>>> a, b = 1, 2
+>>> a
+1
+```
+
+Multiple assignment is not limited to one data type but can instead be used with any data type.
+For example:
+
+```python
+>>> x, y, z = 1, "Hello", True
+>>> x
+1
+
+>>> y
+'Hello'
+
+>>> z
+True
+```
+
+Multiple assignment also allows for the swapping of variables in `lists`.
+This practice is pretty common in [linear sorting algorithms][sorting algorithms].
+For example:
+
+```python
+>>> numbers = [1, 2]
+>>> numbers[0], numbers[1] = numbers[1], numbers[0]
+>>> numbers
+[2, 1]
+```
+
+It is also possible to assign multiple variables to the same value:
+
+```python
+>>> a = b = 1
+>>> a
+1
+>>> b
+1
+```
+
+## Unpacking
+
+```exercism/note
+The examples below use lists but the same concepts apply to tuples.
+```
+
+In Python, it is possible to [unpack a `list`/`tuple`/`dictionary`][unpacking] into distinct variables.
+Since values appear within lists in a specific order, it is therefore possible to _unpack_ a `list` into variables in the same order.
+
+Unpacking a list into variables:
+
+```python
+>>> fruits = ["apple", "banana", "cherry"]
+>>> x, y, z = fruits
+>>> x
+"apple"
+```
+
+If there are values that are not needed then you can use `_` to ignore those values:
+
+```python
+>>> fruits = ["apple", "banana", "cherry"]
+>>> _, _, z = fruits
+>>> z
+"cherry"
+```
+
+You can also do [deep unpacking][deep unpacking] on a `list`, which assigns values from a `list` within a `list` (_this is also known as nested list unpacking_):
+
+```python
+>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
+>>> [[a, b], [c, d]] = fruits_vegetables
+>>> a
+"apple"
+
+>>> d
+"potato"
+```
+
+Deep unpacking and normal unpacking can be mixed together:
+
+```python
+>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
+>>> [a, [c, d]] = fruits_vegetables
+>>> a
+["apple", "banana"]
+
+>>> c
+"carrot"
+```
+
+If the unpacking has variables with incorrect placement and/or an incorrect number of values, you will get a `ValueError`:
+
+```python
+>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
+>>> [[a, b], [d]] = fruits_vegetables
+
+ValueError: too many values to unpack (expected 1)
+```
+
+### Unpacking a list/tuple with `*`
+
+When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the remainder values.
+This can be used instead of slicing the `list`/`tuple`, which in some situations could be more readable.
+For example, we can extract the first element below and then pack the remaining values into a new `list` without the first element:
+
+```python
+>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+>>> x, *last = fruits
+>>> x
+"apple"
+
+>>> last
+["banana", "cherry", "orange", "kiwi", "melon", "mango"]
+```
+
+We can also extract the values at the beginning and end of the `list` while grouping all the values in the middle:
+
+```python
+>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+>>> x, *middle, y, z = fruits
+>>> y
+"melon"
+
+>>> middle
+["banana", "cherry", "orange", "kiwi"]
+```
+
+We can also use `*` in deep unpacking:
+
+```python
+>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
+>>> [[a, *rest], b] = fruits_vegetables
+>>> a
+"apple"
+
+>>> rest
+["banana", "melon"]
+```
+
+### Unpacking a dictionary
+
+[Unpacking a dictionary][packing and unpacking] is a bit different than unpacking a `list`/`tuple`.
+Iteration over dictionaries defaults to the `keys`.
+So when unpacking a `dict`, you can only unpack the `keys` and not the `values`:
+
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> x, y, z = fruits_inventory
+>>> x
+"apple"
+```
+
+If you want to unpack the values then you can use the `values()` method:
+
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> x, y, z = fruits_inventory.values()
+>>> x
+6
+```
+
+If both `keys` and `values` are needed, use the `items()` method.
+Using `items()` will generate tuples with `key-value` pairs.
+This is because [`dict.items()` generates a `tuple`][items] and within it there is a `tuple` for each `key-value` pair:.
+
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> x, y, z = fruits_inventory.items()
+>>> x
+("apple", 6)
+```
+
+## Packing
+
+As with unpacking, _packing_ uses the same `*` and `**` operators.
+[Packing][packing and unpacking]] is the ability to group multiple values into one variable.
+This is useful for when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable.
+It also makes it possible to perform merges on 2 or more `lists`/`tuples`/`dicts`.
+
+### Packing a list/tuple with `*`
+
+Packing a `list`/`tuple` is done by using the `*` operator
+This will pack all the variables into a list/tuple.
+
+```python
+>>> fruits = ["apple", "banana", "cherry"]
+>>> more_fruits = ["orange", "kiwi", "melon", "mango"]
+>>> combined_fruits_lists = [*fruits, *more_fruits]
+
+>>> combined_fruits_lists
+["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+```
+
+### Packing a dictionary with `**`
+
+Packing a dictionary is done by using the `**` operator.
+This will pack all the variables into a dictionary.
+
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
+>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
+
+>>> combined_fruits_inventory
+{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
+```
+
+## Usage of `*` and `**` with a function
+
+### Packing with function parameters
+
+When you have a function that accepts an arbitrary or large number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] to pack or group those parameters together.
+`*args` is used for packing/signaling an arbitrary number of positional (non-keyworded) arguments.
+`**kwargs` is used for packing/signaling an arbitrary number of keyword arguments to a function.
+
+Usage of `*args`:
+
+```python
+>>> def my_function(*args):
+... print(args)
+
+>>> my_function(1, 2, 3)
+(1, 2, 3)
+
+>>> my_function("Hello", "World")
+("Hello", "World")
+
+>>> my_function(1, 2, 3, "Hello", "Mars")
+(1, 2, 3, "Hello", "Mars")
+```
+
+Usage of `**kwargs`:
+
+```python
+>>> def my_function(**kwargs):
+... print(kwargs)
+
+>>> my_function(a=1, b=2, c=3)
+{"a": 1, "b": 2, "c": 3}
+```
+
+`*args` and `**kwargs` can also be used in combination with one another:
+
+```python
+>>> def my_function(*args, **kwargs):
+... print(sum(args))
+... for key, value in kwargs.items():
+... print(f"{key} = {value}")
+
+>>> my_function(1, 2, 3, a=1, b=2, c=3)
+6
+a = 1
+b = 2
+c = 3
+```
+
+You can also write parameters before `*args` to allow for specific positional arguments.
+Individual keyword arguments then have to appear before `**kwargs`.
+
+```exercism/caution
+[Arguments have to be structured][Positional and keyword arguments] like this:
+
+`def my_function(, *args, , **kwargs)`
+
+If you don't follow this order then you will get an error.
+```
+
+```python
+>>> def my_function(a, b, *args):
+... print(a)
+... print(b)
+... print(args)
+
+>>> my_function(1, 2, 3, 4, 5)
+1
+2
+(3, 4, 5)
+```
+
+Writing arguments in an incorrect order will result in an error:
+
+```python
+>>>def my_function(*args, a, b):
+... print(args)
+
+>>>my_function(1, 2, 3, 4, 5)
+Traceback (most recent call last):
+ File "c:\something.py", line 3, in
+ my_function(1, 2, 3, 4, 5)
+TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'
+```
+
+### Unpacking into function calls
+
+You can use `*` to unpack a `list`/`tuple` of arguments into a function call.
+This is very useful for functions that don't accept an `iterable` or `iterator`:
+
+```python
+>>> def my_function(a, b, c):
+... print(c)
+... print(b)
+... print(a)
+
+numbers = [1, 2, 3]
+>>> my_function(*numbers)
+3
+2
+1
+```
+
+Using `*` unpacking with the `zip()` function is another common use case.
+Since `zip()` takes multiple iterables and returns a list of tuples with the values from each iterable grouped:
+
+```python
+>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
+>>> a, *rest = zip(*values)
+>>> rest
+[('y', 2, False), ('z', 3, True)]
+```
+
+[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
+[sorting algorithms]: https://realpython.com/sorting-algorithms-python/
+[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
+[deep unpacking]: https://mathspp.com/blog/pydonts/deep-unpacking#deep-unpacking
+[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
+[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
+[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
diff --git a/concepts/unpacking-and-multiple-assignment/introduction.md b/concepts/unpacking-and-multiple-assignment/introduction.md
index fcde74642ca..0bb09dee5cc 100644
--- a/concepts/unpacking-and-multiple-assignment/introduction.md
+++ b/concepts/unpacking-and-multiple-assignment/introduction.md
@@ -1,2 +1,22 @@
-#TODO: Add introduction for this concept.
+# Unpacking and Multiple Assignment
+Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration.
+Unpacked values can be assigned to variables within the same step.
+With unpacking, there are some special operators used: `*` and `**`.
+
+
+When unpacking a list or tuple, the `*` operator can be used to assign all the remaining elements to a variable.
+When unpacking a dictionary, the `**` operator can be used to assign all the remaining key-value pairs to a variable.
+When these operators are used without a collection, they _pack_ a number of values into a `list`, `tuple`, or `dict`.
+
+It is common in Python to also exploit this unpacking/packing behavior when defining functions that take an arbitrary number of positional or keyword arguments.
+You will often see these "special" parameters defined as `def some_function(*args, **kwargs)`
+
+[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one line.
+This allows for code to be more concise and readable, and is done by separating the variables with a comma.
+
+```exercism/caution
+`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
+```
+
+[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
diff --git a/concepts/unpacking-and-multiple-assignment/links.json b/concepts/unpacking-and-multiple-assignment/links.json
index eb5fb7c38a5..8caff2f9cd0 100644
--- a/concepts/unpacking-and-multiple-assignment/links.json
+++ b/concepts/unpacking-and-multiple-assignment/links.json
@@ -1,18 +1,26 @@
[
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/",
+ "description": "Trey Hunner: Astrisks in Python - What they are and How to Use Them."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/",
+ "description": "Trey Hunner: Tuple Unpacking Improves Python Code Readability."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://stackabuse.com/unpacking-in-python-beyond-parallel-assignment/",
+ "description": "Stack Abuse: Unpacking in Python Beyond Parallel Assignment."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://dbader.org/blog/python-nested-unpacking",
+ "description": "Dan Bader: Python Nested Unpacking."
+ },
+ {
+ "url": "https://peps.python.org/pep-3132/",
+ "description": "Pep 3132: Extended Iterable Unpacking."
+ },
+ {
+ "url": "https://peps.python.org/pep-0448/",
+ "description": "PEP 0448: Additional Unpacking Generalizations."
}
]
diff --git a/config.json b/config.json
index bb349eede74..61cc1285f03 100644
--- a/config.json
+++ b/config.json
@@ -136,6 +136,14 @@
"prerequisites": ["loops", "lists", "tuples"],
"status": "beta"
},
+ {
+ "slug": "locomotive-engineer",
+ "name": "Locomotive Engineer",
+ "uuid": "e1b8b9c9-21c3-47b1-b645-5938b3110c78",
+ "concepts": ["unpacking-and-multiple-assignment"],
+ "prerequisites": ["loops", "lists", "tuples", "dicts"],
+ "status": "wip"
+ },
{
"slug": "cater-waiter",
"name": "Cater Waiter",
diff --git a/exercises/concept/locomotive-engineer/.docs/hints.md b/exercises/concept/locomotive-engineer/.docs/hints.md
new file mode 100644
index 00000000000..929448f286d
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/.docs/hints.md
@@ -0,0 +1,35 @@
+# Hints
+
+## General
+
+- To extract multiple arguments in the function parameters so can you pack them with the `*args` operator for list or tuples or `kwargs` for keyword-based arguments.
+- To pack or unpack use the `*` or `**` operator.
+
+## 1. Create a list of all wagons
+
+- Multiple arguments in the function parameters can be packed with the `*args` operator.
+
+## 2. Fix list of wagons
+
+- Using unpacking with the `*` operator, lets you extract the first two elements of a list while keeping the rest intact.
+- To add another list into an existing list, you can use the `*` operator to "spread" the list.
+
+## 3. Add missing stops
+
+- Using `**kwargs` as a function parameter will allow an arbitrary amount of keyword arguments to be passed.
+- Using `**(dict)` as an argument will unpack a dictionary into keyword arguments.
+- You can put keyword arguments in a `{}` or `dict()`.
+- To get the values out of a dictionary, you can use the `.values()` method.
+
+## 4. Extend routing information
+
+- Using `**(dict)` as an argument will unpack a dictionary into keyword arguments.
+- You can put keyword arguments in a `{}` or `dict()`.
+
+## 5. Fix the wagon depot
+
+- `zip(*iterators)` can use used to transpose a nested list.
+- To extract data from zipped iterators, you can use a for loop.
+- you can also unpack zipped iterators using `*`.
+ `[*content] = zip(iterator_1, iterator_2)` will unzip the `tuple` produced by `zip()` into a `list`.
+
diff --git a/exercises/concept/locomotive-engineer/.docs/instructions.md b/exercises/concept/locomotive-engineer/.docs/instructions.md
new file mode 100644
index 00000000000..9b80b9ff32b
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/.docs/instructions.md
@@ -0,0 +1,110 @@
+# Instructions
+
+Your friend Linus is a Locomotive Engineer who drives cargo trains between cities.
+Your friend is great at handling trains, although they aren't amazing at handling the logistics computers and would like your programming help organizing the train and correcting mistakes in the data.
+
+```exercism/note
+This exercise could easily be solved using `list` slicing, indexing, and `dict` methods.
+However, we'd like you to practice using Unpacking and Multiple Assignment to solve each of the tasks below.
+```
+
+## 1. Create a list of all wagons
+
+Your friend has been keeping track of each wagon identifier, but they're never sure how many wagons they are going to have to process at any given time. It would be much easier for the rest of the logistics program to have the data to be returned as a `list`.
+
+Implement a function `get_list_of_wagons` that accepts an arbitrary amount of positive integers which are the IDs of each wagon.
+It should then return the given IDs as a `list`.
+
+```python
+>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
+[1, 7, 12, 3, 14, 8, 3]
+```
+
+## 2. Fix the list of wagons
+
+At this point, you are starting to get a feel for your friend's data and how it's used in the logistics program.
+The train ID system works by assigning the locomotive an ID of `1` and then assigning the remainder of the wagons a randomly chosen ID greater than `1`.
+
+But then your friend had to connect two new wagons to the train and forgot to update the system!
+Now the first two wagons in the `list` have to be moved to the back of the train, or everything will be out of order.
+
+Additionally, your friend just found a second `list` that appears to contain missing wagon IDs, and would like you to merge it together with the main wagon ID `list`.
+All they can remember is that once the new wagons are moved to the end, the values from the second list should be placed directly after the designated locomotive.
+
+Your friend would be really grateful to you for fixing their mistakes and consolidating the data.
+
+Implement a function `fix_list_of_wagons` that takes two `lists` containing wagon IDs as the arguments.
+It should reposition the first two items of the first list to the end, and then insert the values from the second list behind the locomotive ID (`1`).
+The function should then `return` the `list` with the modifications.
+
+```python
+>>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15])
+[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5]
+```
+
+## 3. Add missing stops
+
+Now that all the wagon data is correct, your friend would like you to update the system's routing information.
+During the journey, the train will stop at a few stations to pick up and/or drop off cargo.
+Each journey will have a different amount of stops. To simplify setting up the routing program your friend would like you to add the missing stops to the routing `dict`.
+
+Implement a function `add_missing_stops` that accepts a routing `dict` followed by an arbitrary amount of additional keyword arguments. These arguments could be in the form of a `dict` which holds one or more stops in order, or a number of `stop_number=city` pairs.
+The function should then return the routing `dict`, updated with a key that holds a list of all the stops in order.
+
+```python
+>>> add_missing_stops({"from": "Berlin", "to": "Hamburg"}, {"stop_1": "Hamburg", "stop_2": "Hannover", "stop_3": "Frankfurt"})
+{"from": "Berlin", "to": "Hamburg", "stops": ["Hamburg", "Hannover", "Frankfurt"]}
+
+>>> add_missing_stops({"from": "New York", "to": "Miami"}, stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta", stop_4="Jacksonville", stop_5="Orlando")
+{"from": "New York", "to": "Miami", "stops": ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]}
+```
+
+## 4. Extend routing information
+
+Your friend has been working on the routing program and has noticed that the routing information is missing some important details.
+Initial routing information has been constructed as a `dict`, and your friend would like you to update it with the additions provided.
+Every route requires slightly different information, so your friend would really prefer a generic solution.
+
+Implement a function `extend_route_information` that accepts two `dicts`.
+The first `dict` contains which cities the train route moves between.
+
+The second `dict` contains other routing details such as train speed or length.
+The function should return a consolidated `dict` with all routing information.
+
+```exercism/note
+The second dict can contain different properties than the ones shown in the example.
+```
+
+```python
+>>> extend_route_information({"from": "Berlin", "to": "Hamburg"}, {"length": "100", "speed": "50"})
+{"from": "Berlin", "to": "Hamburg", "length": "100", "speed": "50"}
+```
+
+## 5. Fix the wagon depot
+
+When your friend was surveying the wagon depot they noticed that the wagons were not getting stored in the correct order.
+In addition to an ID, each wagon has a color that corresponds to the type of cargo it carries.
+Wagons are stored in the depot in grids, with each column in the grid grouped by wagon color.
+
+In the control system, it appears that the lists of wagons to be stored in the depot have their _rows_ grouped by color. But for the storage grid to work correctly, each row has to have three different colors, so that the columns align properly.
+Your friend would like you to help them sort out the wagon depot lists, so that the wagons get stored correctly.
+
+Implement a function `fix_wagon_depot` that accepts a nested `list`.
+The first `list` contains the first row of wagons, the second `list` contains the second row of wagons and the third `list` contains the third row of wagons. All rows are of equal length.
+Every wagon within a row is represented by a `tuple` with (``, ``).
+
+Your function should return a `list` with the three row `lists` reordered with the wagons swapped into their correct positions.
+
+```python
+>>> fix_wagon_depot([
+ [(2, "red"), (4, "red"),(8, "red")],
+ [(5, "blue"),(9, "blue"),(13,"blue")],
+ [(3, "orange"),(7, "orange"), (11, "orange")],
+ ])
+
+[
+[(2, "red"),(5, "blue"),(3, "orange")],
+[(4, "red"),(9, "blue"),(7, "orange")],
+[(8, "red"),(13,"blue"),(11, "orange")]
+]
+```
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
new file mode 100644
index 00000000000..779a6e0504b
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -0,0 +1,289 @@
+# Unpacking and Multiple Assignment
+
+Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration.
+Unpacked values can be assigned to variables within the same step.
+With unpacking, there are some special operators used: `*` and `**`.
+When unpacking a list or tuple, the `*` operator can be used to assign all the remaining elements to a variable.
+When unpacking a dictionary, the `**` operator can be used to assign all the remaining key-value pairs to a variable.
+
+When these operators are used without a collection they will _pack_ a number of values into a `list`, `tuple`, or dictionary.
+It is common to use this kind of behavior when creating functions that take an arbitrary number of arguments.
+
+Multiple assignment is the ability to assign multiple variables in one line.
+This is done by separating the variables with a comma.
+
+```exercism/caution
+`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
+```
+
+## Multiple assignment
+
+[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one line.
+This allows for code to be more concise and readable.
+There has to be x number of variables on the left side of the `=` sign and x number of values on the right side of the `=` sign.
+To separate the values, use a comma `,`:
+
+```python
+>>> a, b = 1, 2
+>>> a
+1
+```
+
+Multiple assignment is not limited to one data type but can instead be used with any data type.
+For example:
+
+```python
+>>> a, b, c = 1, "Hello", True
+>>> a
+1
+
+>>> b
+'Hello'
+
+>>> c
+True
+```
+
+## Unpacking
+
+```exercism/note
+The examples below use lists but the same concepts apply to tuples.
+```
+
+In Python, it is possible to [unpack a `list`/`tuple`/`dictionary`][unpacking] into distinct variables.
+Since values appear within lists in a specific order, it is therefore possible to _unpack_ a `list` into variables in the same order.
+
+Unpacking a list into variables:
+
+```python
+>>> fruits = ["apple", "banana", "cherry"]
+>>> x, y, z = fruits
+>>> x
+"apple"
+```
+
+If there are values that are not needed then you can use `_` to ignore those values:
+
+```python
+>>> fruits = ["apple", "banana", "cherry"]
+>>> _, _, z = fruits
+>>> z
+"cherry"
+```
+
+You can also do [deep unpacking][deep unpacking] on a `list`, which assigns values from a `list` within a `list` (_this is also known as nested list unpacking_):
+
+```python
+>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
+>>> [[a, b], [c, d]] = fruits_vegetables
+>>> a
+"apple"
+
+>>> d
+"potato"
+```
+
+Deep unpacking and normal unpacking can be mixed together:
+
+```python
+>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
+>>> [a, [c, d]] = fruits_vegetables
+>>> a
+["apple", "banana"]
+
+>>> c
+"carrot"
+```
+
+If the unpacking has variables with incorrect placement and/or an incorrect number of values, you will get a `ValueError`:
+
+```python
+>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
+>>> [[a, b], [d]] = fruits_vegetables
+
+ValueError: too many values to unpack (expected 1)
+```
+
+### Unpacking a list/tuple with `*`
+
+When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the remainder values.
+This can be used instead of slicing the `list`/`tuple`, which in some situations could be more readable.
+For example, we can extract the first element below and then pack the remaining values into a new `list` without the first element:
+
+```python
+>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+>>> x, *last = fruits
+>>> x
+"apple"
+
+>>> last
+["banana", "cherry", "orange", "kiwi", "melon", "mango"]
+```
+
+We can also extract the values at the beginning and end of the `list` while grouping all the values in the middle:
+
+```python
+>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+>>> x, *middle, y, z = fruits
+>>> y
+"melon"
+
+>>> middle
+["banana", "cherry", "orange", "kiwi"]
+```
+
+### Unpacking a dictionary
+
+[Unpacking a dictionary][packing and unpacking] is a bit different than unpacking a `list`/`tuple`.
+Iteration over dictionaries defaults to the `keys`.
+So when unpacking a `dict`, you can only unpack the `keys` and not the `values`:
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> x, y, z = fruits_inventory
+>>> x
+"apple"
+```
+
+If you want to unpack the values then you can use the `values()` method:
+
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> x, y, z = fruits_inventory.values()
+>>> x
+6
+```
+
+## Packing
+
+As with unpacking, _packing_ uses the same `*` and `**` operators.
+[Packing][packing and unpacking]] is the ability to group multiple values into one variable.
+This is useful for when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable.
+It also makes it possible to perform merges on 2 or more `lists`/`tuples`/`dicts`.
+
+### Packing a list/tuple with `*`
+
+Packing a `list`/`tuple` is done by using the `*` operator
+This will pack all the variables into a list/tuple.
+
+```python
+>>> fruits = ["apple", "banana", "cherry"]
+>>> more_fruits = ["orange", "kiwi", "melon", "mango"]
+>>> combined_fruits_lists = [*fruits, *more_fruits]
+
+>>> combined_fruits_lists
+["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+```
+
+### Packing a dictionary with `**`
+
+Packing a dictionary is done by using the `**` operator.
+This will pack all the variables into a dictionary.
+
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
+>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
+
+>>> combined_fruits_inventory
+{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
+```
+
+## Usage of `*` and `**` with a function
+
+### Packing with function parameters
+
+When you have a function that accepts an arbitrary or large number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] to pack or group those parameters together.
+`*args` is used for packing/signaling an arbitrary number of positional (non-keyworded) arguments.
+`**kwargs` is used for packing/signaling an arbitrary number of keyword arguments to a function.
+
+Usage of `*args`:
+
+```python
+>>> def my_function(*args):
+... print(args)
+
+>>> my_function(1, 2, 3)
+(1, 2, 3)
+
+>>> my_function("Hello", "World")
+("Hello", "World")
+
+>>> my_function(1, 2, 3, "Hello", "Mars")
+(1, 2, 3, "Hello", "Mars")
+```
+
+Usage of `**kwargs`:
+
+```python
+>>> def my_function(**kwargs):
+... print(kwargs)
+
+>>> my_function(a=1, b=2, c=3)
+{"a": 1, "b": 2, "c": 3}
+```
+
+```exercism/caution
+[Arguments have to be structured][Positional and keyword arguments] like this:
+
+`def my_function(, *args, , **kwargs)`
+
+If you don't follow this order then you will get an error.
+```
+
+```python
+>>> def my_function(a, b, *args):
+... print(a)
+... print(b)
+... print(args)
+
+>>> my_function(1, 2, 3, 4, 5)
+1
+2
+(3, 4, 5)
+```
+
+Writing arguments in an incorrect order will result in an error:
+
+```python
+>>>def my_function(*args, a, b):
+... print(args)
+
+>>>my_function(1, 2, 3, 4, 5)
+Traceback (most recent call last):
+ File "c:\something.py", line 3, in
+ my_function(1, 2, 3, 4, 5)
+TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'
+```
+
+### Unpacking into function calls
+
+You can use `*` to unpack a `list`/`tuple` of arguments into a function call.
+This is very useful for functions that don't accept an `iterable` or `iterator`:
+```python
+>>> def my_function(a, b, c):
+... print(c)
+... print(b)
+... print(a)
+
+numbers = [1, 2, 3]
+>>> my_function(*numbers)
+3
+2
+1
+```
+
+Using `*` unpacking with the `zip()` function is another common use case.
+Since `zip()` takes multiple iterables and returns a list of tuples with the values from each iterable grouped:
+
+```python
+>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
+>>> a, *rest = zip(*values)
+>>> rest
+[('y', 2, False), ('z', 3, True)]
+```
+
+[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
+[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
+[deep unpacking]: https://mathspp.com/blog/pydonts/deep-unpacking#deep-unpacking
+[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
+[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
diff --git a/exercises/concept/locomotive-engineer/.meta/config.json b/exercises/concept/locomotive-engineer/.meta/config.json
new file mode 100644
index 00000000000..76ecea037cc
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/.meta/config.json
@@ -0,0 +1,11 @@
+{
+ "blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system.",
+ "icon": "tracks-on-tracks-on-tracks",
+ "authors": ["meatball","BethanyG"],
+ "files": {
+ "solution": ["locomotive_engineer.py"],
+ "test": ["locomotive_engineer_test.py"],
+ "exemplar": [".meta/exemplar.py"]
+ },
+ "forked_from": ["javascript/train-driver"]
+}
diff --git a/exercises/concept/locomotive-engineer/.meta/design.md b/exercises/concept/locomotive-engineer/.meta/design.md
new file mode 100644
index 00000000000..af4109a9b0c
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/.meta/design.md
@@ -0,0 +1,69 @@
+# Design
+
+## TODO: Add introduction for this concept.
+## Goal
+
+This concept exercise is meant to teach an understanding/use of `unpacking` and the `*` (splat) and `**` (double splat) operators in Python.
+
+
+
+## Learning objectives
+
+- Understand/use `unpacking` through the use of `*` and `**` _prefix_ operators in various scenarios
+ - `*` and `**` as _prefixes_ ..... not to be confused with `*` (_multiply_) and `**` (_exponentiation_) as _infix_, or mathematical operators (**consider a link in the links doc or a mention in dig deeper.**)
+ - ~~what happens in the process of "unpacking" - form, ordering, & iteration~~ (this will go in a **dig deeper** or the link docs.)
+ - use in arguments to `functions`
+ - use in argument _capture_ for `functions` (_aka passing an arbitrary number of arguments -- *args * & **kwargs_)
+ - ~~use in defining `keyword only arguments`~~ (_topic moved to arguments exercise_).
+ - use in iterable (_mainly `tuple` and `list`_) unpacking & packing
+ - use in `dict` unpacking & packing
+- Understand/use `unpacking` via `multiple assignment`
+ - using `multiple assignment ` in place of `indexing`
+ - using `multiple assigment` + `*` in place of `slicing`
+ - ~~using "list-like" syntax & "tuple-like" syntax~~
+ - unpacking plus "leftovers" via `*`
+- Differences between straight `multiple assignment` and `*` & `**`
+- Deep unpacking
+
+
+## Concepts
+
+- `unpacking`
+- `unpacking generalizations`
+- `multiple assignment`
+
+
+## Topics that are Out of scope
+
+- `classes`
+- `comprehensions`
+- `comprehensions` in `lambdas`
+- `map()`, `filter()` or `functools.reduce()` in a `comprehension`
+- `function-arguments` beyond explaining briefly how `*`, `**` work in function arguments.
+- `functools` beyond `functools.reduce()`(_this will get its own exercise_)
+- `generators`
+- using an `assignment expression` or "walrus" operator (`:=`) alone or in a `lambda`
+
+
+## Prerequisites
+
+- `basics`
+- `bools`
+- `comparisons`
+- `dicts`
+- `lists`
+- `numbers`
+- `strings`
+- `tuples`
+
+
+## Representer
+
+This exercise does not require any specific logic to be added to the [representer][representer]
+
+## Analyzer
+
+This exercise does not require any specific logic to be added to the [analyzer][analyzer].
+
+[analyzer]: https://github.com/exercism/python-analyzer
+[representer]: https://github.com/exercism/python-representer
\ No newline at end of file
diff --git a/exercises/concept/locomotive-engineer/.meta/exemplar.py b/exercises/concept/locomotive-engineer/.meta/exemplar.py
new file mode 100644
index 00000000000..861cce2fde9
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/.meta/exemplar.py
@@ -0,0 +1,48 @@
+"""Functions which helps the locomotive engineer to keep track of the train."""
+
+
+def get_list_of_wagons(*args):
+ return list(args)
+
+
+def fix_list_of_wagons(each_wagons_id, missing_wagons):
+ """Fix the list of wagons.
+
+ :param each_wagons_id: list - the list of wagons.
+ :param missing_wagons: list - the list of missing wagons.
+ :return: list - list of wagons.
+ """
+ first, second, loctomotive, *rest = each_wagons_id
+ return [loctomotive, *missing_wagons, *rest, first, second]
+
+
+def add_missing_stops(route, **kwargs):
+ """Add missing stops to route dict.
+
+ :param route: dict - the dict of routing information.
+ :param **kwards: arbitrary number of stops.
+ :return: dict - updated route dictionary.
+ """
+ return {**route, "stops": list(kwargs.values())}
+
+
+def extend_route_information(route, more_route_information):
+ """Extend the route information with the more_route_information.
+
+ :param route: dict - the route information.
+ :param more_route_information: dict - extra route information.
+ :return: dict - extended route information.
+ """
+ return {**route, **more_route_information}
+
+
+def fix_wagon_depot(wagons_rows):
+ """Fix the list of rows of wagons.
+
+ :param wagons_rows: list[tuple] - the list of rows of wagons.
+ :return: list[tuple] - list of rows of wagons.
+ """
+
+ [*row_one], [*row_two], [*row_three] = zip(*wagons_rows)
+
+ return [row_one, row_two, row_three]
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer.py b/exercises/concept/locomotive-engineer/locomotive_engineer.py
new file mode 100644
index 00000000000..6809958b3f4
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer.py
@@ -0,0 +1,38 @@
+"""Functions which helps the locomotive engineer to keep track of the train."""
+
+# TODO: define the 'get_list_of_wagons' function
+
+
+# TODO: define the 'fixListOfWagons()' function
+def fix_list_of_wagons(each_wagons_id, missing_wagons):
+ """Fix the list of wagons.
+
+ :parm each_wagons_id: list - the list of wagons.
+ :parm missing_wagons: list - the list of missing wagons.
+ :return: list - list of wagons.
+ """
+ pass
+
+
+# TODO: define the 'add_missing_stops()' function
+
+
+# TODO: define the 'extend_route_information()' function
+def extend_route_information(route, more_route_information):
+ """Extend the route information with the more_route_information.
+
+ :param route: dict - the route information.
+ :param more_route_information: dict - extra route information.
+ :return: dict - extended route information.
+ """
+ pass
+
+
+# TODO: define the 'fix_wagon_depot()' function
+def fix_wagon_depot(wagons_rows):
+ """Fix the list of rows of wagons.
+
+ :param wagons_rows: list[tuple] - the list of rows of wagons.
+ :return: list[tuple] - list of rows of wagons.
+ """
+ pass
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
new file mode 100644
index 00000000000..537ac75b0e1
--- /dev/null
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
@@ -0,0 +1,82 @@
+import unittest
+import pytest
+from locomotive_engineer import (get_list_of_wagons,
+ fix_list_of_wagons,
+ add_missing_stops,
+ extend_route_information,
+ fix_wagon_depot)
+
+class InventoryTest(unittest.TestCase):
+
+ @pytest.mark.task(taskno=1)
+ def test_get_list_of_wagons(self):
+ input_data = [(1,5,2,7,4), (1,5), (1,), (1,9,3), (1,10,6,3,9,8,4,14,24,7)]
+ output_data = [[1,5,2,7,4], [1,5], [1], [1,9,3], [1,10,6,3,9,8,4,14,24,7]]
+
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ self.assertEqual(get_list_of_wagons(*input_data), output_data)
+
+ @pytest.mark.task(taskno=2)
+ def test_fix_list_of_wagons(self): # One extra case needed at first
+ input_data = [([3, 27, 1, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19], [8, 10, 5, 9, 36, 7, 20]),
+ ([4, 2, 1], [8, 6, 15]),
+ ([3, 14, 1, 25, 7, 19, 10], [8, 6, 4, 5, 9, 21, 2, 13])
+ ]
+ output_data = [[1, 8, 10, 5, 9, 36, 7, 20, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19, 3, 27],
+ [1, 8, 6, 15, 4, 2],
+ [1, 8, 6, 4, 5, 9, 21, 2, 13, 25, 7, 19, 10, 3, 14]
+ ]
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ self.assertEqual(fix_list_of_wagons(input_data[0], input_data[1]), output_data)
+
+ @pytest.mark.task(taskno=3)
+ def test_add_missing_stops(self):
+ input_data = (({"from": "Berlin", "to": "Hamburg"}, {"stop_1": "Lepzig", "stop_2": "Hannover", "stop_3": "Frankfurt"}),
+ ({"from": "Paris", "to": "London"}, {"stop_1": "Lille"}),
+ ({"from": "New York", "to": "Philadelphia"},{}),
+ ({"from": "Gothenburg", "to": "Copenhagen"}, {"stop_1": "Kungsbacka", "stop_2": "Varberg", "stop_3": "Halmstad", "stop_4": "Angelholm", "stop_5": "Lund", "stop_6": "Malmo"})
+ )
+ output_data = [{"from": "Berlin", "to": "Hamburg", "stops": ["Lepzig", "Hannover", "Frankfurt"]},
+ {"from": "Paris", "to": "London", "stops": ["Lille"]},
+ {"from": "New York", "to": "Philadelphia", "stops": []},
+ {"from": "Gothenburg", "to": "Copenhagen", "stops": ["Kungsbacka", "Varberg", "Halmstad", "Angelholm", "Lund", "Malmo"]}
+ ]
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ self.assertEqual(add_missing_stops(input_data[0], **input_data[1]), output_data)
+
+ @pytest.mark.task(taskno=4)
+ def test_extend_route_information(self):
+ input_data = [({"from": "Berlin", "to": "Hamburg"}, {"timeOfArrival": "12:00", "precipitation": "10", "temperature": "5", "caboose": "yes"}),
+ ({"from": "Paris", "to": "London"}, {"timeOfArrival": "10:30", "temperature": "20", "length": 15}),
+ ({"from": "Gothenburg", "to": "Copenhagen"}, {"precipitation": "1", "timeOfArrival": "21:20", "temperature": "-6"})]
+ output_data = [{"from": "Berlin", "to": "Hamburg", "timeOfArrival": "12:00", "precipitation": "10", "temperature": "5", "caboose": "yes"},
+ {"from": "Paris", "to": "London", "timeOfArrival": "10:30", "temperature": "20", "length": 15},
+ {"from": "Gothenburg", "to": "Copenhagen", "precipitation": "1", "timeOfArrival": "21:20", "temperature": "-6"}
+ ]
+
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ self.assertEqual(extend_route_information(input_data[0], input_data[1]), output_data)
+
+ @pytest.mark.task(taskno=5)
+ def test_fix_wagon_depot(self):
+ input_data = (
+ [[(2, "red"), (4, "red"), (8, "red")], [(5, "blue"), (9, "blue"), (13, "blue")], [(3, "orange"), (7, "orange"), (11, "orange")]],
+ [[(6, "blue"), (10, "blue"), (14, "blue")], [(7, "red"), (4, "red"), (2, "red")], [(3, "orange"), (11, "orange"), (15, "orange")]],
+ [[(7, "pink"), (4, "pink"), (2, "pink")],[(10, "green"), (6, "green"), (14, "green")], [(9, "yellow"), (5, "yellow"), (13, "yellow")]],
+ [[(3, "purple"), (11, "purple"), (15, "purple")], [(20, "black"), (16, "black"), (12, "black")], [(19, "white"), (17, "white"), (18, "white")]]
+ )
+
+ output_data = (
+ [[(2, "red"), (5, "blue"), (3, "orange")],[(4, "red"), (9, "blue"), (7, "orange")], [(8, "red"), (13, "blue"), (11, "orange")]],
+ [[(6, "blue"), (7, "red"), (3, "orange")],[(10, "blue"), (4, "red"), (11, "orange")], [(14, "blue"), (2, "red"), (15, "orange")]],
+ [[(7, "pink"), (10, "green"), (9, "yellow")], [(4, "pink"), (6, "green"), (5, "yellow")], [(2, "pink"), (14, "green"), (13, "yellow")]],
+ [[(3, "purple"), (20, "black"), (19, "white")], [(11, "purple"), (16, "black"), (17, "white")], [(15, "purple"), (12, "black"), (18, "white")]]
+ )
+
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ self.assertEqual(fix_wagon_depot(input_data), output_data)
\ No newline at end of file
From 5b3b0aaa7555a9aead9e5e2f8e9a01547e99e3da Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Thu, 24 Nov 2022 08:40:54 +0100
Subject: [PATCH 135/932] add author
---
concepts/unpacking-and-multiple-assignment/.meta/config.json | 2 +-
exercises/concept/locomotive-engineer/.meta/config.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/.meta/config.json b/concepts/unpacking-and-multiple-assignment/.meta/config.json
index ecf013c7acc..f984160fe31 100644
--- a/concepts/unpacking-and-multiple-assignment/.meta/config.json
+++ b/concepts/unpacking-and-multiple-assignment/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "Unpacking is the process of extracting individual elements of a collection, such as a list, tuple, or dictionary by iterating over them. Unpacked values can be assigned to variables within the same step. Multiple assignment is the ability to assign values to multiple variables in one line.",
- "authors": ["meatball","bethanyg"],
+ "authors": ["meatball133","bethanyg"],
"contributors": []
}
diff --git a/exercises/concept/locomotive-engineer/.meta/config.json b/exercises/concept/locomotive-engineer/.meta/config.json
index 76ecea037cc..86a8a8b6a35 100644
--- a/exercises/concept/locomotive-engineer/.meta/config.json
+++ b/exercises/concept/locomotive-engineer/.meta/config.json
@@ -1,7 +1,7 @@
{
"blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system.",
"icon": "tracks-on-tracks-on-tracks",
- "authors": ["meatball","BethanyG"],
+ "authors": ["meatball133","BethanyG"],
"files": {
"solution": ["locomotive_engineer.py"],
"test": ["locomotive_engineer_test.py"],
From 92f2550c7f4401f532484f924a480fa867a60932 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 25 Nov 2022 23:01:11 +0000
Subject: [PATCH 136/932] Bump exercism/pr-commenter-action from 1.3.0 to 1.3.1
Bumps [exercism/pr-commenter-action](https://github.com/exercism/pr-commenter-action) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/exercism/pr-commenter-action/releases)
- [Changelog](https://github.com/exercism/pr-commenter-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/exercism/pr-commenter-action/compare/v1.3.0...v1.3.1)
---
updated-dependencies:
- dependency-name: exercism/pr-commenter-action
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
.github/workflows/pr-commenter.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pr-commenter.yml b/.github/workflows/pr-commenter.yml
index 8b895111279..12591d49004 100644
--- a/.github/workflows/pr-commenter.yml
+++ b/.github/workflows/pr-commenter.yml
@@ -6,7 +6,7 @@ jobs:
pr-comment:
runs-on: ubuntu-latest
steps:
- - uses: exercism/pr-commenter-action@v1.3.0
+ - uses: exercism/pr-commenter-action@v1.3.1
with:
github-token: "${{ github.token }}"
config-file: ".github/pr-commenter.yml"
\ No newline at end of file
From 02abfca1e2f49323f06e38693a3af3de1d69aa5d Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Fri, 25 Nov 2022 16:20:52 +0100
Subject: [PATCH 137/932] Create autoresponder for pausing community
contributions
We're going to take a step back and redesign the volunteering model for Exercism.
Please see [this forum post](https://forum.exercism.org/t/freeing-our-maintainers-exercism-wide-changes-to-track-repositories/1109) for context.
This PR adds an autoresponder that runs when an issue or PR is opened. If the person opening the issue is not a member of the Exercism organization, the autoresponder posts a comment and closes the issue. In the comment the author is directed to discuss the issue in the forum.
If the discussion in the forum results in the issue/PR being approved, a maintainer or staff member will reopen it.
Please feel free to merge this PR. It will be merged on December 1st, 2022. Please do not close it.
If you wish to discuss this, please do so in [the forum post](https://forum.exercism.org/t/freeing-our-maintainers-exercism-wide-changes-to-track-repositories/1109) rather than here.
---
.../pause-community-contributions.yml | 59 +++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 .github/workflows/pause-community-contributions.yml
diff --git a/.github/workflows/pause-community-contributions.yml b/.github/workflows/pause-community-contributions.yml
new file mode 100644
index 00000000000..85d18d1c5d7
--- /dev/null
+++ b/.github/workflows/pause-community-contributions.yml
@@ -0,0 +1,59 @@
+name: Pause Community Contributions
+
+on:
+ issues:
+ types:
+ - opened
+ pull_request_target:
+ types:
+ - opened
+ paths-ignore:
+ - "exercises/*/*/.approaches/**"
+ - "exercises/*/*/.articles/**"
+ workflow_dispatch:
+
+jobs:
+ pause:
+ name: Pause Community Contributions
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Detect if user is org member
+ uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
+ id: is-organization-member
+ with:
+ script: |
+ if (context.actor == 'dependabot' || context.actor == 'exercism-bot' || context.actor == 'github-actions[bot]') {
+ return true;
+ }
+
+ return github.rest.orgs.checkMembershipForUser({
+ org: context.repo.owner,
+ username: context.actor,
+ }).then(response => response.status == 204)
+ .catch(err => true);
+ - name: Comment
+ if: steps.is-organization-member.outputs.result == 'false'
+ uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
+ with:
+ script: |
+ const isIssue = !!context.payload.issue;
+ const subject = context.payload.issue || context.payload.pull_request;
+ const thing = (isIssue ? 'issue' : 'PR');
+ const aThing = (isIssue ? 'an issue' : 'a PR');
+ github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: `Hello. Thanks for opening ${aThing} on Exercism. We are currently in a phase of our journey where we have paused community contributions to allow us to take a breather and redesign our community model. You can learn more in [this blog post](https://exercism.org/blog/freeing-our-maintainers). **As such, all issues and PRs in this repository are being automatically closed.**\n\nThat doesn’t mean we’re not interested in your ideas, or that if you’re stuck on something we don’t want to help. The best place to discuss things is with our community on the Exercism Community Forum. You can use [this link](https://forum.exercism.org/new-topic?title=${encodeURI(subject.title)}&body=${encodeURI(subject.body)}&category=python) to copy this into a new topic there.\n\n---\n\n_Note: If this ${thing} has been pre-approved, please link back to this ${thing} on the forum thread and a maintainer or staff member will reopen it._\n`
+ })
+ - name: Close
+ if: steps.is-organization-member.outputs.result == 'false'
+ uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
+ with:
+ script: |
+ github.rest.issues.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ state: "closed",
+ })
From 676ec9742b5fde160b3c60d9cc77f6cea6bcfd6a Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 26 Nov 2022 02:20:10 -0800
Subject: [PATCH 138/932] First Pass on Feedback Edits
Instructions re-worked
Added missing Link
Started about.md re-write.
---
.../about.md | 11 +--
.../locomotive-engineer/.docs/instructions.md | 76 ++++++++++---------
2 files changed, 47 insertions(+), 40 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index 68c8e0662bd..385139d0149 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -346,10 +346,11 @@ Since `zip()` takes multiple iterables and returns a list of tuples with the val
[('y', 2, False), ('z', 3, True)]
```
-[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
-[sorting algorithms]: https://realpython.com/sorting-algorithms-python/
-[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
+[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
[deep unpacking]: https://mathspp.com/blog/pydonts/deep-unpacking#deep-unpacking
-[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
-[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
+[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
+[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
+[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
+[sorting algorithms]: https://realpython.com/sorting-algorithms-python/
+[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
\ No newline at end of file
diff --git a/exercises/concept/locomotive-engineer/.docs/instructions.md b/exercises/concept/locomotive-engineer/.docs/instructions.md
index 9b80b9ff32b..6da50371ffa 100644
--- a/exercises/concept/locomotive-engineer/.docs/instructions.md
+++ b/exercises/concept/locomotive-engineer/.docs/instructions.md
@@ -1,19 +1,21 @@
# Instructions
Your friend Linus is a Locomotive Engineer who drives cargo trains between cities.
-Your friend is great at handling trains, although they aren't amazing at handling the logistics computers and would like your programming help organizing the train and correcting mistakes in the data.
+Although they are amazing at handling trains, they are not amazing at handling logistics or computers.
+They would like to enlist your programming help organiing train details and correcting mistakes in route data.
```exercism/note
-This exercise could easily be solved using `list` slicing, indexing, and `dict` methods.
-However, we'd like you to practice using Unpacking and Multiple Assignment to solve each of the tasks below.
+This exercise could easily be solved using `slicing`, `indexing`, and various `dict` methods.
+However, we would like you to practice packing, unpacking, and multiple assignment in solving each of the tasks below.
```
## 1. Create a list of all wagons
-Your friend has been keeping track of each wagon identifier, but they're never sure how many wagons they are going to have to process at any given time. It would be much easier for the rest of the logistics program to have the data to be returned as a `list`.
+Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data be packaged into a unified `list`.
-Implement a function `get_list_of_wagons` that accepts an arbitrary amount of positive integers which are the IDs of each wagon.
-It should then return the given IDs as a `list`.
+Implement a function `get_list_of_wagons` that accepts an arbitrary number of wagon IDs.
+Each ID will be a positive integer.
+The function should then `return` the given IDs as a single `list`.
```python
>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
@@ -22,20 +24,20 @@ It should then return the given IDs as a `list`.
## 2. Fix the list of wagons
-At this point, you are starting to get a feel for your friend's data and how it's used in the logistics program.
-The train ID system works by assigning the locomotive an ID of `1` and then assigning the remainder of the wagons a randomly chosen ID greater than `1`.
+At this point, you are starting to get a feel for the data and how it's used in the logistics program.
+The ID system always assigns the locomotive an ID of `1`, with the remainder of the wagons in the train assigned a randomly chosen ID greater than `1`.
-But then your friend had to connect two new wagons to the train and forgot to update the system!
-Now the first two wagons in the `list` have to be moved to the back of the train, or everything will be out of order.
+Your friend had to connect two new wagons to the train and forgot to update the system!
+Now, the first two wagons in the train `list` have to be moved to the end, or everything will be out of order.
-Additionally, your friend just found a second `list` that appears to contain missing wagon IDs, and would like you to merge it together with the main wagon ID `list`.
-All they can remember is that once the new wagons are moved to the end, the values from the second list should be placed directly after the designated locomotive.
+To make matters more complicated, your friend just uncovered a second `list` that appears to contain missing wagon IDs.
+All they can remember is that once the new wagons are moved, the IDs from this second list should be placed directly after the designated locomotive.
-Your friend would be really grateful to you for fixing their mistakes and consolidating the data.
+Linus would be really grateful to you for fixing their mistakes and consolidating the data.
-Implement a function `fix_list_of_wagons` that takes two `lists` containing wagon IDs as the arguments.
-It should reposition the first two items of the first list to the end, and then insert the values from the second list behind the locomotive ID (`1`).
-The function should then `return` the `list` with the modifications.
+Implement a function `fix_list_of_wagons` that takes two `lists` containing wagon IDs.
+It should reposition the first two items of the first `list` to the end, and insert the values from the second `list` behind (_on the right hand side of_) the locomotive ID (`1`).
+The function should then `return` a `list` with the modifications.
```python
>>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15])
@@ -44,12 +46,14 @@ The function should then `return` the `list` with the modifications.
## 3. Add missing stops
-Now that all the wagon data is correct, your friend would like you to update the system's routing information.
-During the journey, the train will stop at a few stations to pick up and/or drop off cargo.
-Each journey will have a different amount of stops. To simplify setting up the routing program your friend would like you to add the missing stops to the routing `dict`.
+Now that all the wagon data is correct, Linus would like you to update the system's routing information.
+Along a transport route, a train might make stops at a few different stations to pick up and/or drop off cargo.
+Each journey could have a different amount of these intermediary delivery points.
+Your friend would like you to update the systems routing `dict` with any missing/additional delivery information.
-Implement a function `add_missing_stops` that accepts a routing `dict` followed by an arbitrary amount of additional keyword arguments. These arguments could be in the form of a `dict` which holds one or more stops in order, or a number of `stop_number=city` pairs.
-The function should then return the routing `dict`, updated with a key that holds a list of all the stops in order.
+Implement a function `add_missing_stops` that accepts a routing `dict` followed by a variable number of keyword arguments.
+These arguments could be in the form of a `dict` holding one or more stops, or amy number of `stop_number=city` keyword pairs.
+Your function should then return the routing `dict` updated with an additional `key` that holds a `list` of all the added stops in order.
```python
>>> add_missing_stops({"from": "Berlin", "to": "Hamburg"}, {"stop_1": "Hamburg", "stop_2": "Hannover", "stop_3": "Frankfurt"})
@@ -61,18 +65,18 @@ The function should then return the routing `dict`, updated with a key that hold
## 4. Extend routing information
-Your friend has been working on the routing program and has noticed that the routing information is missing some important details.
-Initial routing information has been constructed as a `dict`, and your friend would like you to update it with the additions provided.
-Every route requires slightly different information, so your friend would really prefer a generic solution.
+Linus has been working on the routing program and has noticed that certain routes are missing some important details.
+Initial route information has been constructed as a `dict` and your friend would like you to update that `dict` with whatever might be missing.
+Every route in the system requires slightly different details, so Linus would really prefer a generic/flexable solution.
-Implement a function `extend_route_information` that accepts two `dicts`.
-The first `dict` contains which cities the train route moves between.
+Implement a function called `extend_route_information` that accepts two `dicts`.
+The first `dict` contains the origin and destination cities the train route runs between.
-The second `dict` contains other routing details such as train speed or length.
+The second `dict` contains other routing details such as train speed, length, or temprature.
The function should return a consolidated `dict` with all routing information.
```exercism/note
-The second dict can contain different properties than the ones shown in the example.
+The second dict can contain different/more properties than the ones shown in the example.
```
```python
@@ -82,16 +86,18 @@ The second dict can contain different properties than the ones shown in the exam
## 5. Fix the wagon depot
-When your friend was surveying the wagon depot they noticed that the wagons were not getting stored in the correct order.
+When your Linus was surveying the wagon depot they noticed that the wagons were not getting stored in the correct order.
In addition to an ID, each wagon has a color that corresponds to the type of cargo it carries.
-Wagons are stored in the depot in grids, with each column in the grid grouped by wagon color.
+Wagons are stored in the depot in grids, where each column in the grid has wagons of the same color.
-In the control system, it appears that the lists of wagons to be stored in the depot have their _rows_ grouped by color. But for the storage grid to work correctly, each row has to have three different colors, so that the columns align properly.
-Your friend would like you to help them sort out the wagon depot lists, so that the wagons get stored correctly.
+However, the logistics system shows `lists` of wagons to be stored in the depot have their _rows_ grouped by color.
+But for the storage grid to work correctly, each _row_ should have three different colors so that the _columns_ align by color.
+Your friend would like you to sort out the wagon depot `lists`, so that the wagons get stored correctly.
-Implement a function `fix_wagon_depot` that accepts a nested `list`.
-The first `list` contains the first row of wagons, the second `list` contains the second row of wagons and the third `list` contains the third row of wagons. All rows are of equal length.
-Every wagon within a row is represented by a `tuple` with (``, ``).
+Implement a function called `fix_wagon_depot` that accepts a nested `list`.
+The first `list` contains the first row of wagons, the second `list` contains the second row of wagons and the third `list` contains the third row of wagons.
+All rows are of equal length.
+Every wagon within a row is represented by a `tuple` with `(, )`.
Your function should return a `list` with the three row `lists` reordered with the wagons swapped into their correct positions.
From 6e28e160e4b439ed8f8c6395d3a94ebb35a29166 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 26 Nov 2022 22:57:29 -0800
Subject: [PATCH 139/932] Added Test Error Messages
---
.../locomotive_engineer_test.py | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
index 537ac75b0e1..d90198ac478 100644
--- a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
@@ -15,7 +15,8 @@ def test_get_list_of_wagons(self):
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
- self.assertEqual(get_list_of_wagons(*input_data), output_data)
+ error_msg=f'Expected: {output_data} but got a different wagon list instead.'
+ self.assertEqual(get_list_of_wagons(*input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
def test_fix_list_of_wagons(self): # One extra case needed at first
@@ -29,7 +30,8 @@ def test_fix_list_of_wagons(self): # One extra case needed at first
]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
- self.assertEqual(fix_list_of_wagons(input_data[0], input_data[1]), output_data)
+ error_msg=f'Expected: {output_data} but got a different wagon list instead.'
+ self.assertEqual(fix_list_of_wagons(input_data[0], input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=3)
def test_add_missing_stops(self):
@@ -45,7 +47,8 @@ def test_add_missing_stops(self):
]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
- self.assertEqual(add_missing_stops(input_data[0], **input_data[1]), output_data)
+ error_msg=f'Expected: {output_data} but got a different set of stops instead.'
+ self.assertEqual(add_missing_stops(input_data[0], **input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=4)
def test_extend_route_information(self):
@@ -59,7 +62,8 @@ def test_extend_route_information(self):
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
- self.assertEqual(extend_route_information(input_data[0], input_data[1]), output_data)
+ error_msg=f'Expected: {output_data} but got a different route dictionary instead.'
+ self.assertEqual(extend_route_information(input_data[0], input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=5)
def test_fix_wagon_depot(self):
@@ -79,4 +83,5 @@ def test_fix_wagon_depot(self):
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
- self.assertEqual(fix_wagon_depot(input_data), output_data)
\ No newline at end of file
+ error_msg=f'Expected: {output_data} but got a different wagon depot list instead.'
+ self.assertEqual(fix_wagon_depot(input_data), output_data, msg=error_msg)
\ No newline at end of file
From 5428a33a1f7ddd9a5e3c6798fc22f08a4b221be4 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 27 Nov 2022 15:35:14 +0100
Subject: [PATCH 140/932] Added Matthijs as contributor and varius fixes
---
.../unpacking-and-multiple-assignment/.meta/config.json | 2 +-
concepts/unpacking-and-multiple-assignment/about.md | 6 +++---
exercises/concept/locomotive-engineer/.docs/instructions.md | 6 +++---
exercises/concept/locomotive-engineer/.meta/exemplar.py | 6 +++---
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/.meta/config.json b/concepts/unpacking-and-multiple-assignment/.meta/config.json
index f984160fe31..36aa817b971 100644
--- a/concepts/unpacking-and-multiple-assignment/.meta/config.json
+++ b/concepts/unpacking-and-multiple-assignment/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "Unpacking is the process of extracting individual elements of a collection, such as a list, tuple, or dictionary by iterating over them. Unpacked values can be assigned to variables within the same step. Multiple assignment is the ability to assign values to multiple variables in one line.",
"authors": ["meatball133","bethanyg"],
- "contributors": []
+ "contributors": ["MatthijsBlom"]
}
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index 385139d0149..d87d6143516 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -44,8 +44,8 @@ For example:
True
```
-Multiple assignment also allows for the swapping of variables in `lists`.
-This practice is pretty common in [linear sorting algorithms][sorting algorithms].
+Multiple assignment also allows for the swapping of elements in `lists`.
+This practice is pretty common in [sorting algorithms][sorting algorithms].
For example:
```python
@@ -353,4 +353,4 @@ Since `zip()` takes multiple iterables and returns a list of tuples with the val
[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
[sorting algorithms]: https://realpython.com/sorting-algorithms-python/
-[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
\ No newline at end of file
+[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
diff --git a/exercises/concept/locomotive-engineer/.docs/instructions.md b/exercises/concept/locomotive-engineer/.docs/instructions.md
index 6da50371ffa..39f9c8253c0 100644
--- a/exercises/concept/locomotive-engineer/.docs/instructions.md
+++ b/exercises/concept/locomotive-engineer/.docs/instructions.md
@@ -11,7 +11,7 @@ However, we would like you to practice packing, unpacking, and multiple assignme
## 1. Create a list of all wagons
-Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data be packaged into a unified `list`.
+Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data be packaged into a unified `list`.
Implement a function `get_list_of_wagons` that accepts an arbitrary number of wagon IDs.
Each ID will be a positive integer.
@@ -86,7 +86,7 @@ The second dict can contain different/more properties than the ones shown in the
## 5. Fix the wagon depot
-When your Linus was surveying the wagon depot they noticed that the wagons were not getting stored in the correct order.
+When Linus was surveying the wagon depot they noticed that the wagons were not getting stored in the correct order.
In addition to an ID, each wagon has a color that corresponds to the type of cargo it carries.
Wagons are stored in the depot in grids, where each column in the grid has wagons of the same color.
@@ -95,7 +95,7 @@ But for the storage grid to work correctly, each _row_ should have three differe
Your friend would like you to sort out the wagon depot `lists`, so that the wagons get stored correctly.
Implement a function called `fix_wagon_depot` that accepts a nested `list`.
-The first `list` contains the first row of wagons, the second `list` contains the second row of wagons and the third `list` contains the third row of wagons.
+The first `list` contains the first row of wagons, the second `list` contains the second row of wagons and the third `list` contains the third row of wagons.
All rows are of equal length.
Every wagon within a row is represented by a `tuple` with `(, )`.
diff --git a/exercises/concept/locomotive-engineer/.meta/exemplar.py b/exercises/concept/locomotive-engineer/.meta/exemplar.py
index 861cce2fde9..6fdc680ab06 100644
--- a/exercises/concept/locomotive-engineer/.meta/exemplar.py
+++ b/exercises/concept/locomotive-engineer/.meta/exemplar.py
@@ -12,15 +12,15 @@ def fix_list_of_wagons(each_wagons_id, missing_wagons):
:param missing_wagons: list - the list of missing wagons.
:return: list - list of wagons.
"""
- first, second, loctomotive, *rest = each_wagons_id
- return [loctomotive, *missing_wagons, *rest, first, second]
+ first, second, locomotive, *rest = each_wagons_id
+ return [locomotive, *missing_wagons, *rest, first, second]
def add_missing_stops(route, **kwargs):
"""Add missing stops to route dict.
:param route: dict - the dict of routing information.
- :param **kwards: arbitrary number of stops.
+ :param **kwargs: arbitrary number of stops.
:return: dict - updated route dictionary.
"""
return {**route, "stops": list(kwargs.values())}
From 605b6f5eb35c958855c87cbf7617d28d7ab6da66 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 27 Nov 2022 15:43:12 +0100
Subject: [PATCH 141/932] Update about.md
---
concepts/unpacking-and-multiple-assignment/about.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index d87d6143516..7bd34e7eda2 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -189,7 +189,7 @@ If you want to unpack the values then you can use the `values()` method:
If both `keys` and `values` are needed, use the `items()` method.
Using `items()` will generate tuples with `key-value` pairs.
-This is because [`dict.items()` generates a `tuple`][items] and within it there is a `tuple` for each `key-value` pair:.
+This is because [`dict.items()` generates an iterable with key-value `tuples`][items] and within it there is a `tuple` for each `key-value` pair:.
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
From 2b4aac7aaa2c94bd2d3dc4bcb4cf8e40a748ad20 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 27 Nov 2022 16:39:52 +0100
Subject: [PATCH 142/932] fix
---
concepts/unpacking-and-multiple-assignment/about.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index 7bd34e7eda2..6212e4d9444 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -189,7 +189,7 @@ If you want to unpack the values then you can use the `values()` method:
If both `keys` and `values` are needed, use the `items()` method.
Using `items()` will generate tuples with `key-value` pairs.
-This is because [`dict.items()` generates an iterable with key-value `tuples`][items] and within it there is a `tuple` for each `key-value` pair:.
+This is because [`dict.items()` generates an iterable with key-value `tuples`][items].
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
From 5fa0ed91e5887d52c93305104a511af57e851696 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 27 Nov 2022 20:47:00 +0100
Subject: [PATCH 143/932] Add missing functions & fixes
---
.../locomotive-engineer/.docs/instructions.md | 43 +++++++++----------
.../locomotive-engineer/.meta/design.md | 21 +++------
.../locomotive-engineer/.meta/exemplar.py | 16 +++++--
.../locomotive_engineer.py | 18 +++++++-
.../locomotive_engineer_test.py | 10 +++--
5 files changed, 64 insertions(+), 44 deletions(-)
diff --git a/exercises/concept/locomotive-engineer/.docs/instructions.md b/exercises/concept/locomotive-engineer/.docs/instructions.md
index 39f9c8253c0..c81cedcff70 100644
--- a/exercises/concept/locomotive-engineer/.docs/instructions.md
+++ b/exercises/concept/locomotive-engineer/.docs/instructions.md
@@ -2,18 +2,18 @@
Your friend Linus is a Locomotive Engineer who drives cargo trains between cities.
Although they are amazing at handling trains, they are not amazing at handling logistics or computers.
-They would like to enlist your programming help organiing train details and correcting mistakes in route data.
+They would like to enlist your programming help organizing train details and correcting mistakes in route data.
```exercism/note
-This exercise could easily be solved using `slicing`, `indexing`, and various `dict` methods.
+This exercise could easily be solved using slicing, indexing, and various `dict` methods.
However, we would like you to practice packing, unpacking, and multiple assignment in solving each of the tasks below.
```
## 1. Create a list of all wagons
-Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data be packaged into a unified `list`.
+Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data packaged into a unified `list`.
-Implement a function `get_list_of_wagons` that accepts an arbitrary number of wagon IDs.
+Implement a function `get_list_of_wagons()` that accepts an arbitrary number of wagon IDs.
Each ID will be a positive integer.
The function should then `return` the given IDs as a single `list`.
@@ -25,18 +25,18 @@ The function should then `return` the given IDs as a single `list`.
## 2. Fix the list of wagons
At this point, you are starting to get a feel for the data and how it's used in the logistics program.
-The ID system always assigns the locomotive an ID of `1`, with the remainder of the wagons in the train assigned a randomly chosen ID greater than `1`.
+The ID system always assigns the locomotive an ID of **1**, with the remainder of the wagons in the train assigned a randomly chosen ID greater than **1**.
Your friend had to connect two new wagons to the train and forgot to update the system!
Now, the first two wagons in the train `list` have to be moved to the end, or everything will be out of order.
To make matters more complicated, your friend just uncovered a second `list` that appears to contain missing wagon IDs.
-All they can remember is that once the new wagons are moved, the IDs from this second list should be placed directly after the designated locomotive.
+All they can remember is that once the new wagons are moved, the IDs from this second `list` should be placed directly after the designated locomotive.
Linus would be really grateful to you for fixing their mistakes and consolidating the data.
-Implement a function `fix_list_of_wagons` that takes two `lists` containing wagon IDs.
-It should reposition the first two items of the first `list` to the end, and insert the values from the second `list` behind (_on the right hand side of_) the locomotive ID (`1`).
+Implement a function `fix_list_of_wagons()` that takes two `lists` containing wagon IDs.
+It should reposition the first two items of the first `list` to the end, and insert the values from the second `list` behind (_on the right hand side of_) the locomotive ID (**1**).
The function should then `return` a `list` with the modifications.
```python
@@ -51,15 +51,15 @@ Along a transport route, a train might make stops at a few different stations to
Each journey could have a different amount of these intermediary delivery points.
Your friend would like you to update the systems routing `dict` with any missing/additional delivery information.
-Implement a function `add_missing_stops` that accepts a routing `dict` followed by a variable number of keyword arguments.
-These arguments could be in the form of a `dict` holding one or more stops, or amy number of `stop_number=city` keyword pairs.
+Implement a function `add_missing_stops()` that accepts a routing `dict` followed by a variable number of keyword arguments.
+These arguments could be in the form of a `dict` holding one or more stops, or any number of `stop_number=city` keyword pairs.
Your function should then return the routing `dict` updated with an additional `key` that holds a `list` of all the added stops in order.
```python
->>> add_missing_stops({"from": "Berlin", "to": "Hamburg"}, {"stop_1": "Hamburg", "stop_2": "Hannover", "stop_3": "Frankfurt"})
-{"from": "Berlin", "to": "Hamburg", "stops": ["Hamburg", "Hannover", "Frankfurt"]}
+>>> add_missing_stops({"from": "New York", "to": "Miami"},
+ stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta",
+ stop_4="Jacksonville", stop_5="Orlando")
->>> add_missing_stops({"from": "New York", "to": "Miami"}, stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta", stop_4="Jacksonville", stop_5="Orlando")
{"from": "New York", "to": "Miami", "stops": ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]}
```
@@ -67,16 +67,16 @@ Your function should then return the routing `dict` updated with an additional `
Linus has been working on the routing program and has noticed that certain routes are missing some important details.
Initial route information has been constructed as a `dict` and your friend would like you to update that `dict` with whatever might be missing.
-Every route in the system requires slightly different details, so Linus would really prefer a generic/flexable solution.
+Every route in the system requires slightly different details, so Linus would really prefer a generic solution.
-Implement a function called `extend_route_information` that accepts two `dicts`.
+Implement a function called `extend_route_information()` that accepts two `dicts`.
The first `dict` contains the origin and destination cities the train route runs between.
-The second `dict` contains other routing details such as train speed, length, or temprature.
+The second `dict` contains other routing details such as train speed, length, or temperature.
The function should return a consolidated `dict` with all routing information.
```exercism/note
-The second dict can contain different/more properties than the ones shown in the example.
+The second `dict` can contain different/more properties than the ones shown in the example.
```
```python
@@ -94,12 +94,11 @@ However, the logistics system shows `lists` of wagons to be stored in the depot
But for the storage grid to work correctly, each _row_ should have three different colors so that the _columns_ align by color.
Your friend would like you to sort out the wagon depot `lists`, so that the wagons get stored correctly.
-Implement a function called `fix_wagon_depot` that accepts a nested `list`.
-The first `list` contains the first row of wagons, the second `list` contains the second row of wagons and the third `list` contains the third row of wagons.
-All rows are of equal length.
-Every wagon within a row is represented by a `tuple` with `(, )`.
+Implement a function called `fix_wagon_depot()` that accepts a `list` of three items.
+Each `list` item is a sublist (or "row") that contains three `tuples`.
+Each `tuple` is a `(, )` pair.
-Your function should return a `list` with the three row `lists` reordered with the wagons swapped into their correct positions.
+Your function should return a `list` with the three "row" `lists` reordered to have the wagons swapped into their correct positions.
```python
>>> fix_wagon_depot([
diff --git a/exercises/concept/locomotive-engineer/.meta/design.md b/exercises/concept/locomotive-engineer/.meta/design.md
index af4109a9b0c..933b7cb8ff5 100644
--- a/exercises/concept/locomotive-engineer/.meta/design.md
+++ b/exercises/concept/locomotive-engineer/.meta/design.md
@@ -1,9 +1,8 @@
# Design
-## TODO: Add introduction for this concept.
## Goal
-This concept exercise is meant to teach an understanding/use of `unpacking` and the `*` (splat) and `**` (double splat) operators in Python.
+This concept exercise is meant to teach an understanding/use of `unpacking` and the `*` (splat) and `**` (double splat) operators in Python.
@@ -11,40 +10,34 @@ This concept exercise is meant to teach an understanding/use of `unpacking` and
- Understand/use `unpacking` through the use of `*` and `**` _prefix_ operators in various scenarios
- `*` and `**` as _prefixes_ ..... not to be confused with `*` (_multiply_) and `**` (_exponentiation_) as _infix_, or mathematical operators (**consider a link in the links doc or a mention in dig deeper.**)
- - ~~what happens in the process of "unpacking" - form, ordering, & iteration~~ (this will go in a **dig deeper** or the link docs.)
- use in arguments to `functions`
- - use in argument _capture_ for `functions` (_aka passing an arbitrary number of arguments -- *args * & **kwargs_)
- - ~~use in defining `keyword only arguments`~~ (_topic moved to arguments exercise_).
+ - use in argument _capture_ for `functions` (_aka passing an arbitrary number of arguments -- *args * & \*\*kwargs_)
- use in iterable (_mainly `tuple` and `list`_) unpacking & packing
- use in `dict` unpacking & packing
- Understand/use `unpacking` via `multiple assignment`
- using `multiple assignment ` in place of `indexing`
- - using `multiple assigment` + `*` in place of `slicing`
- - ~~using "list-like" syntax & "tuple-like" syntax~~
+ - using `multiple assignment` + `*` in place of `slicing`
- unpacking plus "leftovers" via `*`
-- Differences between straight `multiple assignment` and `*` & `**`
+- Differences between straight `multiple assignment` and `*` & `**`
- Deep unpacking
-
## Concepts
- `unpacking`
- `unpacking generalizations`
- `multiple assignment`
-
## Topics that are Out of scope
- `classes`
- `comprehensions`
- `comprehensions` in `lambdas`
- `map()`, `filter()` or `functools.reduce()` in a `comprehension`
-- `function-arguments` beyond explaining briefly how `*`, `**` work in function arguments.
+- `function-arguments` beyond explaining briefly how `*`, `**` work in function arguments.
- `functools` beyond `functools.reduce()`(_this will get its own exercise_)
- `generators`
- using an `assignment expression` or "walrus" operator (`:=`) alone or in a `lambda`
-
## Prerequisites
- `basics`
@@ -55,7 +48,7 @@ This concept exercise is meant to teach an understanding/use of `unpacking` and
- `numbers`
- `strings`
- `tuples`
-
+- `loops`
## Representer
@@ -66,4 +59,4 @@ This exercise does not require any specific logic to be added to the [represente
This exercise does not require any specific logic to be added to the [analyzer][analyzer].
[analyzer]: https://github.com/exercism/python-analyzer
-[representer]: https://github.com/exercism/python-representer
\ No newline at end of file
+[representer]: https://github.com/exercism/python-representer
diff --git a/exercises/concept/locomotive-engineer/.meta/exemplar.py b/exercises/concept/locomotive-engineer/.meta/exemplar.py
index 6fdc680ab06..bda295d2699 100644
--- a/exercises/concept/locomotive-engineer/.meta/exemplar.py
+++ b/exercises/concept/locomotive-engineer/.meta/exemplar.py
@@ -2,6 +2,12 @@
def get_list_of_wagons(*args):
+ """Return a list of wagons.
+
+ :param *args: arbitrary number of wagons.
+ :return: list - list of wagons.
+ """
+
return list(args)
@@ -12,7 +18,9 @@ def fix_list_of_wagons(each_wagons_id, missing_wagons):
:param missing_wagons: list - the list of missing wagons.
:return: list - list of wagons.
"""
+
first, second, locomotive, *rest = each_wagons_id
+
return [locomotive, *missing_wagons, *rest, first, second]
@@ -20,19 +28,21 @@ def add_missing_stops(route, **kwargs):
"""Add missing stops to route dict.
:param route: dict - the dict of routing information.
- :param **kwargs: arbitrary number of stops.
+ :param **kwargs: arbitrary number of stops.
:return: dict - updated route dictionary.
"""
+
return {**route, "stops": list(kwargs.values())}
def extend_route_information(route, more_route_information):
- """Extend the route information with the more_route_information.
+ """Extend route information with more_route_information.
:param route: dict - the route information.
- :param more_route_information: dict - extra route information.
+ :param more_route_information: dict - extra route information.
:return: dict - extended route information.
"""
+
return {**route, **more_route_information}
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer.py b/exercises/concept/locomotive-engineer/locomotive_engineer.py
index 6809958b3f4..4166a24b684 100644
--- a/exercises/concept/locomotive-engineer/locomotive_engineer.py
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer.py
@@ -1,6 +1,14 @@
"""Functions which helps the locomotive engineer to keep track of the train."""
+
# TODO: define the 'get_list_of_wagons' function
+def get_list_of_wagons():
+ """Return a list of wagons.
+
+ :param: arbitrary number of wagons.
+ :return: list - list of wagons.
+ """
+ pass
# TODO: define the 'fixListOfWagons()' function
@@ -15,11 +23,19 @@ def fix_list_of_wagons(each_wagons_id, missing_wagons):
# TODO: define the 'add_missing_stops()' function
+def add_missing_stops():
+ """Add missing stops to route dict.
+
+ :param route: dict - the dict of routing information.
+ :param: arbitrary number of stops.
+ :return: dict - updated route dictionary.
+ """
+ pass
# TODO: define the 'extend_route_information()' function
def extend_route_information(route, more_route_information):
- """Extend the route information with the more_route_information.
+ """Extend route information with more_route_information.
:param route: dict - the route information.
:param more_route_information: dict - extra route information.
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
index d90198ac478..7cc84251026 100644
--- a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
@@ -6,7 +6,7 @@
extend_route_information,
fix_wagon_depot)
-class InventoryTest(unittest.TestCase):
+class LocomotiveEngineerTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_get_list_of_wagons(self):
@@ -19,12 +19,14 @@ def test_get_list_of_wagons(self):
self.assertEqual(get_list_of_wagons(*input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
- def test_fix_list_of_wagons(self): # One extra case needed at first
- input_data = [([3, 27, 1, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19], [8, 10, 5, 9, 36, 7, 20]),
+ def test_fix_list_of_wagons(self):
+ input_data = [([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15]),
+ ([3, 27, 1, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19], [8, 10, 5, 9, 36, 7, 20]),
([4, 2, 1], [8, 6, 15]),
([3, 14, 1, 25, 7, 19, 10], [8, 6, 4, 5, 9, 21, 2, 13])
]
- output_data = [[1, 8, 10, 5, 9, 36, 7, 20, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19, 3, 27],
+ output_data = [[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5],
+ [1, 8, 10, 5, 9, 36, 7, 20, 14, 10, 4, 12, 6, 23, 17, 13, 22, 28, 19, 3, 27],
[1, 8, 6, 15, 4, 2],
[1, 8, 6, 4, 5, 9, 21, 2, 13, 25, 7, 19, 10, 3, 14]
]
From dca06a41db31cfefbf24089c2a02ebb75376cdd5 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 27 Nov 2022 23:32:38 +0100
Subject: [PATCH 144/932] Major changes
---
.../about.md | 116 ++++++++++--------
.../introduction.md | 1 -
.../links.json | 2 +-
config.json | 2 +-
.../locomotive-engineer/.docs/hints.md | 13 +-
.../locomotive_engineer_test.py | 3 +-
6 files changed, 72 insertions(+), 65 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index 6212e4d9444..b260ba42594 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -2,15 +2,17 @@
Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration.
Unpacked values can be assigned to variables within the same step.
-With unpacking, there are some special operators used: `*` and `**`.
-When unpacking a list or tuple, the `*` operator can be used to assign all the remaining elements to a variable.
-When unpacking a dictionary, the `**` operator can be used to assign all the remaining key-value pairs to a variable.
+With unpacking the `*` operator is used.
-When these operators are used without a collection they will _pack_ a number of values into a `list`, `tuple`, or dictionary.
-It is common to use this kind of behavior when creating functions that take an arbitrary number of arguments.
+When unpacking a list or tuple, `*` can be used to assign all the leftover elements to a variable.
+When the `*` operator is used without a collection, it _packs_ a number of values into a `list` or `tuple`.
+`**` can be used to combine multiple dictionaries into one dictionary.
-Multiple assignment is the ability to assign multiple variables in one line.
-This is done by separating the variables with a comma.
+It is common in Python to also exploit this unpacking/packing behavior when defining functions that take an arbitrary number of positional or keyword arguments.
+You will often see these "special" parameters defined as `def some_function(*args, **kwargs)`
+
+[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one statement.
+This allows for code to be more concise and readable, and is done by separating the variables with a comma.
```exercism/caution
`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
@@ -18,9 +20,7 @@ This is done by separating the variables with a comma.
## Multiple assignment
-[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one line.
-This allows for code to be more concise and readable.
-There has to be x number of variables on the left side of the `=` sign and x number of values on the right side of the `=` sign.
+In multiple assignment since you are assigning a number of variables in one statement, the number of variables on the left side of the assignment operator must match the number of values on the right side.
To separate the values, use a comma `,`:
```python
@@ -29,8 +29,15 @@ To separate the values, use a comma `,`:
1
```
-Multiple assignment is not limited to one data type but can instead be used with any data type.
-For example:
+If multiple assignment gets incorrect number of variables for the values given, you will get a `ValueError`:
+
+```python
+>>> x, y, z = 1, 2
+
+ValueError: too many values to unpack (expected 3, got 2)
+```
+
+Multiple assignment is not limited to one data type:
```python
>>> x, y, z = 1, "Hello", True
@@ -44,7 +51,7 @@ For example:
True
```
-Multiple assignment also allows for the swapping of elements in `lists`.
+Multiple assignment can be used to swap elements in `lists`.
This practice is pretty common in [sorting algorithms][sorting algorithms].
For example:
@@ -55,26 +62,16 @@ For example:
[2, 1]
```
-It is also possible to assign multiple variables to the same value:
-
-```python
->>> a = b = 1
->>> a
-1
->>> b
-1
-```
+Since `tuples` are immutable, you can't swap elements in a `tuple`.
## Unpacking
```exercism/note
-The examples below use lists but the same concepts apply to tuples.
+The examples below use `lists` but the same concepts apply to `tuples`.
```
-In Python, it is possible to [unpack a `list`/`tuple`/`dictionary`][unpacking] into distinct variables.
-Since values appear within lists in a specific order, it is therefore possible to _unpack_ a `list` into variables in the same order.
-
-Unpacking a list into variables:
+In Python, it is possible to [unpack the elements of `list`/`tuple`/`dictionary`][unpacking] into distinct variables.
+Since values appear within `lists`/`tuples` in a specific order, they are unpacked into variables in the same order:
```python
>>> fruits = ["apple", "banana", "cherry"]
@@ -83,7 +80,7 @@ Unpacking a list into variables:
"apple"
```
-If there are values that are not needed then you can use `_` to ignore those values:
+If there are values that are not needed then you can use `_` to flag them:
```python
>>> fruits = ["apple", "banana", "cherry"]
@@ -92,7 +89,9 @@ If there are values that are not needed then you can use `_` to ignore those val
"cherry"
```
-You can also do [deep unpacking][deep unpacking] on a `list`, which assigns values from a `list` within a `list` (_this is also known as nested list unpacking_):
+### Deep unpacking
+
+Unpacking and assigning values from a `list`/`tuple` inside of a `list` or `tuple` (_also known as nested lists/tuples_), works in the same way a shallow unpacking does, but often needs qualifiers to clarify the values context or position:
```python
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
@@ -104,7 +103,7 @@ You can also do [deep unpacking][deep unpacking] on a `list`, which assigns valu
"potato"
```
-Deep unpacking and normal unpacking can be mixed together:
+You can also deeply unpack just a portion of a nested `list`/`tuple`:
```python
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
@@ -127,9 +126,9 @@ ValueError: too many values to unpack (expected 1)
### Unpacking a list/tuple with `*`
-When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the remainder values.
-This can be used instead of slicing the `list`/`tuple`, which in some situations could be more readable.
-For example, we can extract the first element below and then pack the remaining values into a new `list` without the first element:
+When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the "leftover" values.
+This is clearer than slicing the `list`/`tuple` (_which in some situations is less readable_).
+For example, we can extract the first element and then assign the remaining values into a new `list` without the first element:
```python
>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
@@ -168,8 +167,8 @@ We can also use `*` in deep unpacking:
### Unpacking a dictionary
[Unpacking a dictionary][packing and unpacking] is a bit different than unpacking a `list`/`tuple`.
-Iteration over dictionaries defaults to the `keys`.
-So when unpacking a `dict`, you can only unpack the `keys` and not the `values`:
+Iteration over dictionaries defaults to the **keys**.
+So when unpacking a `dict`, you can only unpack the **keys** and not the **values**:
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
@@ -187,8 +186,8 @@ If you want to unpack the values then you can use the `values()` method:
6
```
-If both `keys` and `values` are needed, use the `items()` method.
-Using `items()` will generate tuples with `key-value` pairs.
+If both **keys** and **values** are needed, use the `items()` method.
+Using `items()` will generate tuples with **key-value** pairs.
This is because [`dict.items()` generates an iterable with key-value `tuples`][items].
```python
@@ -200,23 +199,23 @@ This is because [`dict.items()` generates an iterable with key-value `tuples`][i
## Packing
-As with unpacking, _packing_ uses the same `*` and `**` operators.
-[Packing][packing and unpacking]] is the ability to group multiple values into one variable.
-This is useful for when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable.
+[Packing][packing and unpacking] is the ability to group multiple values into one variable.
+This is useful when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable.
It also makes it possible to perform merges on 2 or more `lists`/`tuples`/`dicts`.
### Packing a list/tuple with `*`
-Packing a `list`/`tuple` is done by using the `*` operator
-This will pack all the variables into a list/tuple.
+Packing a `list`/`tuple` can be done using the `*` operator.
+This will pack all the values into a `list`/`tuple`.
```python
>>> fruits = ["apple", "banana", "cherry"]
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]
->>> combined_fruits_lists = [*fruits, *more_fruits]
-
+# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits_lists
+>>> combined_fruits_lists = *fruits, *more_fruits
+# If the unpacking is on the right side of "=" then it results in a tuple
>>> combined_fruits_lists
-["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")
```
### Packing a dictionary with `**`
@@ -227,8 +226,9 @@ This will pack all the variables into a dictionary.
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
+# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
-
+# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
```
@@ -237,21 +237,25 @@ This will pack all the variables into a dictionary.
### Packing with function parameters
-When you have a function that accepts an arbitrary or large number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] to pack or group those parameters together.
-`*args` is used for packing/signaling an arbitrary number of positional (non-keyworded) arguments.
-`**kwargs` is used for packing/signaling an arbitrary number of keyword arguments to a function.
+When you have a function that accepts an arbitrary number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] in the function definition.
+`*args` is used to pack an arbitrary number of positional (non-keyworded) arguments and
+`**kwargs` is used to pack an arbitrary number of keyword arguments.
Usage of `*args`:
```python
+# This function is defined to take any number of positional arguments
+
>>> def my_function(*args):
... print(args)
+# Arguments given to the function are packed into a tuple
+
>>> my_function(1, 2, 3)
(1, 2, 3)
->>> my_function("Hello", "World")
-("Hello", "World")
+>>> my_function("Hello")
+("Hello")
>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")
@@ -260,9 +264,13 @@ Usage of `*args`:
Usage of `**kwargs`:
```python
+# This function is defined to take any number of keyword arguments
+
>>> def my_function(**kwargs):
... print(kwargs)
+# Arguments given to the function are packed into a dictionary
+
>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}
```
@@ -273,7 +281,7 @@ Usage of `**kwargs`:
>>> def my_function(*args, **kwargs):
... print(sum(args))
... for key, value in kwargs.items():
-... print(f"{key} = {value}")
+... print(str(key) + " = " + str(value))
>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
@@ -321,7 +329,7 @@ TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'
### Unpacking into function calls
You can use `*` to unpack a `list`/`tuple` of arguments into a function call.
-This is very useful for functions that don't accept an `iterable` or `iterator`:
+This is very useful for functions that don't accept an `iterable`:
```python
>>> def my_function(a, b, c):
@@ -337,7 +345,7 @@ numbers = [1, 2, 3]
```
Using `*` unpacking with the `zip()` function is another common use case.
-Since `zip()` takes multiple iterables and returns a list of tuples with the values from each iterable grouped:
+Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the values from each `iterable` grouped:
```python
>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
diff --git a/concepts/unpacking-and-multiple-assignment/introduction.md b/concepts/unpacking-and-multiple-assignment/introduction.md
index 0bb09dee5cc..a2b190656e4 100644
--- a/concepts/unpacking-and-multiple-assignment/introduction.md
+++ b/concepts/unpacking-and-multiple-assignment/introduction.md
@@ -4,7 +4,6 @@ Unpacking refers to the act of extracting the elements of a collection, such as
Unpacked values can be assigned to variables within the same step.
With unpacking, there are some special operators used: `*` and `**`.
-
When unpacking a list or tuple, the `*` operator can be used to assign all the remaining elements to a variable.
When unpacking a dictionary, the `**` operator can be used to assign all the remaining key-value pairs to a variable.
When these operators are used without a collection, they _pack_ a number of values into a `list`, `tuple`, or `dict`.
diff --git a/concepts/unpacking-and-multiple-assignment/links.json b/concepts/unpacking-and-multiple-assignment/links.json
index 8caff2f9cd0..6ad50c216c7 100644
--- a/concepts/unpacking-and-multiple-assignment/links.json
+++ b/concepts/unpacking-and-multiple-assignment/links.json
@@ -1,7 +1,7 @@
[
{
"url": "https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/",
- "description": "Trey Hunner: Astrisks in Python - What they are and How to Use Them."
+ "description": "Trey Hunner: Asterisks in Python - What they are and How to Use Them."
},
{
"url": "https://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/",
diff --git a/config.json b/config.json
index 61cc1285f03..7350c2d7c86 100644
--- a/config.json
+++ b/config.json
@@ -142,7 +142,7 @@
"uuid": "e1b8b9c9-21c3-47b1-b645-5938b3110c78",
"concepts": ["unpacking-and-multiple-assignment"],
"prerequisites": ["loops", "lists", "tuples", "dicts"],
- "status": "wip"
+ "status": "beta"
},
{
"slug": "cater-waiter",
diff --git a/exercises/concept/locomotive-engineer/.docs/hints.md b/exercises/concept/locomotive-engineer/.docs/hints.md
index 929448f286d..6bfae5f3e3b 100644
--- a/exercises/concept/locomotive-engineer/.docs/hints.md
+++ b/exercises/concept/locomotive-engineer/.docs/hints.md
@@ -2,7 +2,7 @@
## General
-- To extract multiple arguments in the function parameters so can you pack them with the `*args` operator for list or tuples or `kwargs` for keyword-based arguments.
+- To extract multiple arguments in the function parameters so can you pack them with the `*args` operator for `list` or `tuples` or `**kwargs` for keyword-based arguments.
- To pack or unpack use the `*` or `**` operator.
## 1. Create a list of all wagons
@@ -11,25 +11,24 @@
## 2. Fix list of wagons
-- Using unpacking with the `*` operator, lets you extract the first two elements of a list while keeping the rest intact.
-- To add another list into an existing list, you can use the `*` operator to "spread" the list.
+- Using unpacking with the `*` operator, lets you extract the first two elements of a `list` while keeping the rest intact.
+- To add another `list` into an existing `list`, you can use the `*` operator to "spread" the `list`.
## 3. Add missing stops
- Using `**kwargs` as a function parameter will allow an arbitrary amount of keyword arguments to be passed.
-- Using `**(dict)` as an argument will unpack a dictionary into keyword arguments.
+- Using `**` as an argument will unpack a dictionary into keyword arguments.
- You can put keyword arguments in a `{}` or `dict()`.
- To get the values out of a dictionary, you can use the `.values()` method.
## 4. Extend routing information
-- Using `**(dict)` as an argument will unpack a dictionary into keyword arguments.
+- Using `**` as an argument will unpack a dictionary into keyword arguments.
- You can put keyword arguments in a `{}` or `dict()`.
## 5. Fix the wagon depot
-- `zip(*iterators)` can use used to transpose a nested list.
+- `zip(*iterators)` can use used to transpose a nested `list`.
- To extract data from zipped iterators, you can use a for loop.
- you can also unpack zipped iterators using `*`.
`[*content] = zip(iterator_1, iterator_2)` will unzip the `tuple` produced by `zip()` into a `list`.
-
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
index 7cc84251026..b3a46939fdb 100644
--- a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
@@ -6,6 +6,7 @@
extend_route_information,
fix_wagon_depot)
+
class LocomotiveEngineerTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
@@ -86,4 +87,4 @@ def test_fix_wagon_depot(self):
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 wagon depot list instead.'
- self.assertEqual(fix_wagon_depot(input_data), output_data, msg=error_msg)
\ No newline at end of file
+ self.assertEqual(fix_wagon_depot(input_data), output_data, msg=error_msg)
From 06f7c95fcfc6a93e501e40302447d4100fd4842a Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 27 Nov 2022 21:39:40 -0800
Subject: [PATCH 145/932] Final Rewrites for Concept and Exercise docs
Concept
introduction.md
about.md
Exercise
introduction.md
---
.../about.md | 58 +++---
.../introduction.md | 23 ++-
.../locomotive-engineer/.docs/introduction.md | 178 +++++++++++++-----
3 files changed, 175 insertions(+), 84 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index b260ba42594..f10d377075d 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -1,18 +1,22 @@
# Unpacking and Multiple Assignment
Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration.
-Unpacked values can be assigned to variables within the same step.
-With unpacking the `*` operator is used.
+Unpacked values can then be assigned to variables within the same statement.
+A very common example of this behavior is `for item in list`, where `item` takes on the value of each `list` element in turn throughout the iteration.
-When unpacking a list or tuple, `*` can be used to assign all the leftover elements to a variable.
-When the `*` operator is used without a collection, it _packs_ a number of values into a `list` or `tuple`.
-`**` can be used to combine multiple dictionaries into one dictionary.
+[Multiple assignment][multiple assignment] is the ability to assign multiple variables to unpacked values within one statement.
+This allows for code to be more concise and readable, and is done by separating the variables to be assigned with a comma such as `first, second, third = (1,2,3)` or `for index, item in enumerate(iterable)`.
-It is common in Python to also exploit this unpacking/packing behavior when defining functions that take an arbitrary number of positional or keyword arguments.
-You will often see these "special" parameters defined as `def some_function(*args, **kwargs)`
+The special operators `*` and `**` are often used in unpacking contexts.
+`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`.
+`**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`.
+
+When the `*` operator is used without a collection,it _packs_ a number of values into a `list`.
+This is often used in multiple assignment to group all "leftover" elements that do not have individual assignments into a single variable.
+
+It is common in Python to also exploit this unpacking/packing behavior when using or defining functions that take an arbitrary number of positional or keyword arguments.
+You will often see these "special" parameters defined as `def some_function(*args, **kwargs)` and the "special" arguments used as `some_function(*some_tuple, **some_dict)`.
-[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one statement.
-This allows for code to be more concise and readable, and is done by separating the variables with a comma.
```exercism/caution
`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
@@ -20,7 +24,7 @@ This allows for code to be more concise and readable, and is done by separating
## Multiple assignment
-In multiple assignment since you are assigning a number of variables in one statement, the number of variables on the left side of the assignment operator must match the number of values on the right side.
+In multiple assignment, the number of variables on the left side of the assignment operator (`=`) must match the number of values on the right side.
To separate the values, use a comma `,`:
```python
@@ -29,7 +33,7 @@ To separate the values, use a comma `,`:
1
```
-If multiple assignment gets incorrect number of variables for the values given, you will get a `ValueError`:
+If the multiple assignment gets an incorrect number of variables for the values given, a `ValueError` will be thrown:
```python
>>> x, y, z = 1, 2
@@ -64,6 +68,7 @@ For example:
Since `tuples` are immutable, you can't swap elements in a `tuple`.
+
## Unpacking
```exercism/note
@@ -199,7 +204,7 @@ This is because [`dict.items()` generates an iterable with key-value `tuples`][i
## Packing
-[Packing][packing and unpacking] is the ability to group multiple values into one variable.
+[Packing][packing and unpacking] is the ability to group multiple values into one `list` that is assigned to a variable.
This is useful when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable.
It also makes it possible to perform merges on 2 or more `lists`/`tuples`/`dicts`.
@@ -209,35 +214,44 @@ Packing a `list`/`tuple` can be done using the `*` operator.
This will pack all the values into a `list`/`tuple`.
```python
->>> fruits = ["apple", "banana", "cherry"]
+>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]
-# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits_lists
->>> combined_fruits_lists = *fruits, *more_fruits
-# If the unpacking is on the right side of "=" then it results in a tuple
->>> combined_fruits_lists
+
+# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
+>>> combined_fruits = *fruits, *more_fruits
+
+# If there is no * on to the left of the "=" the result is a tuple
+>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")
+
+# If the * operator is used on the left side of "=" the result is a list
+>>> *combined_fruits_too, = *fruits, *more_fruits
+>>> combined_fruits_too
+['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
```
### Packing a dictionary with `**`
Packing a dictionary is done by using the `**` operator.
-This will pack all the variables into a dictionary.
+This will pack all **key**-**value** pairs from one dictionary into another dictionary, or combine two dictionarys together.
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
-# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs
+
+# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
+
# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
```
-## Usage of `*` and `**` with a function
+## Usage of `*` and `**` with functions
### Packing with function parameters
-When you have a function that accepts an arbitrary number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] in the function definition.
+When you create a function that accepts an arbitrary number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] in the function definition.
`*args` is used to pack an arbitrary number of positional (non-keyworded) arguments and
`**kwargs` is used to pack an arbitrary number of keyword arguments.
@@ -355,10 +369,8 @@ Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the
```
[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
-[deep unpacking]: https://mathspp.com/blog/pydonts/deep-unpacking#deep-unpacking
[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
-[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
[sorting algorithms]: https://realpython.com/sorting-algorithms-python/
[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
diff --git a/concepts/unpacking-and-multiple-assignment/introduction.md b/concepts/unpacking-and-multiple-assignment/introduction.md
index a2b190656e4..d8b91bd9c0b 100644
--- a/concepts/unpacking-and-multiple-assignment/introduction.md
+++ b/concepts/unpacking-and-multiple-assignment/introduction.md
@@ -1,21 +1,26 @@
# Unpacking and Multiple Assignment
Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration.
-Unpacked values can be assigned to variables within the same step.
-With unpacking, there are some special operators used: `*` and `**`.
+Unpacked values can then be assigned to variables within the same statement.
+A very common example of this behavior is `for item in list`, where `item` takes on the value of each `list` element in turn throughout the iteration.
-When unpacking a list or tuple, the `*` operator can be used to assign all the remaining elements to a variable.
-When unpacking a dictionary, the `**` operator can be used to assign all the remaining key-value pairs to a variable.
-When these operators are used without a collection, they _pack_ a number of values into a `list`, `tuple`, or `dict`.
+[Multiple assignment][multiple assignment] is the ability to assign multiple variables to unpacked values within one statement.
+This allows for code to be more concise and readable, and is done by separating the variables to be assigned with a comma such as `first, second, third = (1,2,3)` or `for index, item in enumerate(iterable)`.
-It is common in Python to also exploit this unpacking/packing behavior when defining functions that take an arbitrary number of positional or keyword arguments.
-You will often see these "special" parameters defined as `def some_function(*args, **kwargs)`
+The special operators `*` and `**` are often used in unpacking contexts.
+`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`.
+`**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`.
+
+When the `*` operator is used without a collection,it _packs_ a number of values into a `list`.
+This is often used in multiple assignment to group all "leftover" elements that do not have individual assignments into a single variable.
+
+It is common in Python to also exploit this unpacking/packing behavior when using or defining functions that take an arbitrary number of positional or keyword arguments.
+You will often see these "special" parameters defined as `def some_function(*args, **kwargs)` and the "special" arguments used as `some_function(*some_tuple, **some_dict)`.
-[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one line.
-This allows for code to be more concise and readable, and is done by separating the variables with a comma.
```exercism/caution
`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
```
+
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
index 779a6e0504b..dcda62efe8a 100644
--- a/exercises/concept/locomotive-engineer/.docs/introduction.md
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -1,16 +1,9 @@
# Unpacking and Multiple Assignment
Unpacking refers to the act of extracting the elements of a collection, such as a `list`, `tuple`, or `dict`, using iteration.
-Unpacked values can be assigned to variables within the same step.
-With unpacking, there are some special operators used: `*` and `**`.
-When unpacking a list or tuple, the `*` operator can be used to assign all the remaining elements to a variable.
-When unpacking a dictionary, the `**` operator can be used to assign all the remaining key-value pairs to a variable.
+Unpacked values can then be assigned to variables within the same statement, which is commonly referred to as [Multiple assignment][multiple assignment].
-When these operators are used without a collection they will _pack_ a number of values into a `list`, `tuple`, or dictionary.
-It is common to use this kind of behavior when creating functions that take an arbitrary number of arguments.
-
-Multiple assignment is the ability to assign multiple variables in one line.
-This is done by separating the variables with a comma.
+The special operators `*` and `**` are often used in unpacking contexts and with multiple assignment.
```exercism/caution
`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
@@ -18,9 +11,7 @@ This is done by separating the variables with a comma.
## Multiple assignment
-[Multiple assignment][multiple assignment] is the ability to assign multiple variables in one line.
-This allows for code to be more concise and readable.
-There has to be x number of variables on the left side of the `=` sign and x number of values on the right side of the `=` sign.
+In multiple assignment, the number of variables on the left side of the assignment operator (`=`) must match the number of values on the right side.
To separate the values, use a comma `,`:
```python
@@ -29,31 +20,50 @@ To separate the values, use a comma `,`:
1
```
-Multiple assignment is not limited to one data type but can instead be used with any data type.
-For example:
+If the multiple assignment gets an incorrect number of variables for the values given, a `ValueError` will be thrown:
```python
->>> a, b, c = 1, "Hello", True
->>> a
+>>> x, y, z = 1, 2
+
+ValueError: too many values to unpack (expected 3, got 2)
+```
+
+Multiple assignment is not limited to one data type:
+
+```python
+>>> x, y, z = 1, "Hello", True
+>>> x
1
->>> b
+>>> y
'Hello'
->>> c
+>>> z
True
```
+Multiple assignment can be used to swap elements in `lists`.
+This practice is pretty common in [sorting algorithms][sorting algorithms].
+For example:
+
+```python
+>>> numbers = [1, 2]
+>>> numbers[0], numbers[1] = numbers[1], numbers[0]
+>>> numbers
+[2, 1]
+```
+
+Since `tuples` are immutable, you can't swap elements in a `tuple`.
+
+
## Unpacking
```exercism/note
-The examples below use lists but the same concepts apply to tuples.
+The examples below use `lists` but the same concepts apply to `tuples`.
```
-In Python, it is possible to [unpack a `list`/`tuple`/`dictionary`][unpacking] into distinct variables.
-Since values appear within lists in a specific order, it is therefore possible to _unpack_ a `list` into variables in the same order.
-
-Unpacking a list into variables:
+In Python, it is possible to [unpack the elements of `list`/`tuple`/`dictionary`][unpacking] into distinct variables.
+Since values appear within `lists`/`tuples` in a specific order, they are unpacked into variables in the same order:
```python
>>> fruits = ["apple", "banana", "cherry"]
@@ -62,7 +72,7 @@ Unpacking a list into variables:
"apple"
```
-If there are values that are not needed then you can use `_` to ignore those values:
+If there are values that are not needed then you can use `_` to flag them:
```python
>>> fruits = ["apple", "banana", "cherry"]
@@ -71,7 +81,9 @@ If there are values that are not needed then you can use `_` to ignore those val
"cherry"
```
-You can also do [deep unpacking][deep unpacking] on a `list`, which assigns values from a `list` within a `list` (_this is also known as nested list unpacking_):
+### Deep unpacking
+
+Unpacking and assigning values from a `list`/`tuple` inside of a `list` or `tuple` (_also known as nested lists/tuples_), works in the same way a shallow unpacking does, but often needs qualifiers to clarify the values context or position:
```python
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
@@ -83,7 +95,7 @@ You can also do [deep unpacking][deep unpacking] on a `list`, which assigns valu
"potato"
```
-Deep unpacking and normal unpacking can be mixed together:
+You can also deeply unpack just a portion of a nested `list`/`tuple`:
```python
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
@@ -106,9 +118,9 @@ ValueError: too many values to unpack (expected 1)
### Unpacking a list/tuple with `*`
-When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the remainder values.
-This can be used instead of slicing the `list`/`tuple`, which in some situations could be more readable.
-For example, we can extract the first element below and then pack the remaining values into a new `list` without the first element:
+When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the "leftover" values.
+This is clearer than slicing the `list`/`tuple` (_which in some situations is less readable_).
+For example, we can extract the first element and then assign the remaining values into a new `list` without the first element:
```python
>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
@@ -132,11 +144,24 @@ We can also extract the values at the beginning and end of the `list` while grou
["banana", "cherry", "orange", "kiwi"]
```
+We can also use `*` in deep unpacking:
+
+```python
+>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
+>>> [[a, *rest], b] = fruits_vegetables
+>>> a
+"apple"
+
+>>> rest
+["banana", "melon"]
+```
+
### Unpacking a dictionary
[Unpacking a dictionary][packing and unpacking] is a bit different than unpacking a `list`/`tuple`.
-Iteration over dictionaries defaults to the `keys`.
-So when unpacking a `dict`, you can only unpack the `keys` and not the `values`:
+Iteration over dictionaries defaults to the **keys**.
+So when unpacking a `dict`, you can only unpack the **keys** and not the **values**:
+
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
@@ -153,60 +178,85 @@ If you want to unpack the values then you can use the `values()` method:
6
```
+If both **keys** and **values** are needed, use the `items()` method.
+Using `items()` will generate tuples with **key-value** pairs.
+This is because [`dict.items()` generates an iterable with key-value `tuples`][items].
+
+```python
+>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
+>>> x, y, z = fruits_inventory.items()
+>>> x
+("apple", 6)
+```
+
## Packing
-As with unpacking, _packing_ uses the same `*` and `**` operators.
-[Packing][packing and unpacking]] is the ability to group multiple values into one variable.
-This is useful for when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable.
+[Packing][packing and unpacking] is the ability to group multiple values into one `list` that is assigned to a variable.
+This is useful when you want to _unpack_ values, make changes, and then _pack_ the results back into a variable.
It also makes it possible to perform merges on 2 or more `lists`/`tuples`/`dicts`.
### Packing a list/tuple with `*`
-Packing a `list`/`tuple` is done by using the `*` operator
-This will pack all the variables into a list/tuple.
+Packing a `list`/`tuple` can be done using the `*` operator.
+This will pack all the values into a `list`/`tuple`.
```python
->>> fruits = ["apple", "banana", "cherry"]
+>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]
->>> combined_fruits_lists = [*fruits, *more_fruits]
->>> combined_fruits_lists
-["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
+# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
+>>> combined_fruits = *fruits, *more_fruits
+
+# If there is no * on to the left of the "=" the result is a tuple
+>>> combined_fruits
+("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")
+
+# If the * operator is used on the left side of "=" the result is a list
+>>> *combined_fruits_too, = *fruits, *more_fruits
+>>> combined_fruits_too
+['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
```
### Packing a dictionary with `**`
Packing a dictionary is done by using the `**` operator.
-This will pack all the variables into a dictionary.
+This will pack all **key**-**value** pairs from one dictionary into another dictionary, or combine two dictionarys together.
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
+
+# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
+# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
```
-## Usage of `*` and `**` with a function
+## Usage of `*` and `**` with functions
### Packing with function parameters
-When you have a function that accepts an arbitrary or large number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] to pack or group those parameters together.
-`*args` is used for packing/signaling an arbitrary number of positional (non-keyworded) arguments.
-`**kwargs` is used for packing/signaling an arbitrary number of keyword arguments to a function.
+When you create a function that accepts an arbitrary number of arguments, you can use [`*args` or `**kwargs`][args and kwargs] in the function definition.
+`*args` is used to pack an arbitrary number of positional (non-keyworded) arguments and
+`**kwargs` is used to pack an arbitrary number of keyword arguments.
Usage of `*args`:
```python
+# This function is defined to take any number of positional arguments
+
>>> def my_function(*args):
... print(args)
+# Arguments given to the function are packed into a tuple
+
>>> my_function(1, 2, 3)
(1, 2, 3)
->>> my_function("Hello", "World")
-("Hello", "World")
+>>> my_function("Hello")
+("Hello")
>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")
@@ -215,13 +265,35 @@ Usage of `*args`:
Usage of `**kwargs`:
```python
+# This function is defined to take any number of keyword arguments
+
>>> def my_function(**kwargs):
... print(kwargs)
+# Arguments given to the function are packed into a dictionary
+
>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}
```
+`*args` and `**kwargs` can also be used in combination with one another:
+
+```python
+>>> def my_function(*args, **kwargs):
+... print(sum(args))
+... for key, value in kwargs.items():
+... print(str(key) + " = " + str(value))
+
+>>> my_function(1, 2, 3, a=1, b=2, c=3)
+6
+a = 1
+b = 2
+c = 3
+```
+
+You can also write parameters before `*args` to allow for specific positional arguments.
+Individual keyword arguments then have to appear before `**kwargs`.
+
```exercism/caution
[Arguments have to be structured][Positional and keyword arguments] like this:
@@ -258,7 +330,8 @@ TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'
### Unpacking into function calls
You can use `*` to unpack a `list`/`tuple` of arguments into a function call.
-This is very useful for functions that don't accept an `iterable` or `iterator`:
+This is very useful for functions that don't accept an `iterable`:
+
```python
>>> def my_function(a, b, c):
... print(c)
@@ -273,7 +346,7 @@ numbers = [1, 2, 3]
```
Using `*` unpacking with the `zip()` function is another common use case.
-Since `zip()` takes multiple iterables and returns a list of tuples with the values from each iterable grouped:
+Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the values from each `iterable` grouped:
```python
>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
@@ -282,8 +355,9 @@ Since `zip()` takes multiple iterables and returns a list of tuples with the val
[('y', 2, False), ('z', 3, True)]
```
+[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
+[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
-[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
-[deep unpacking]: https://mathspp.com/blog/pydonts/deep-unpacking#deep-unpacking
[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
-[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
+[sorting algorithms]: https://realpython.com/sorting-algorithms-python/
+[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
From fd9d85f8033cf1c064cb79eddaaf55e76b3d5ead Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 28 Nov 2022 08:13:31 +0100
Subject: [PATCH 146/932] Spell fixes
---
concepts/unpacking-and-multiple-assignment/about.md | 8 +++-----
.../unpacking-and-multiple-assignment/introduction.md | 4 +---
.../concept/locomotive-engineer/.docs/introduction.md | 3 +--
3 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index f10d377075d..4e1e90a2516 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -11,13 +11,12 @@ The special operators `*` and `**` are often used in unpacking contexts.
`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`.
`**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`.
-When the `*` operator is used without a collection,it _packs_ a number of values into a `list`.
+When the `*` operator is used without a collection, it _packs_ a number of values into a `list`.
This is often used in multiple assignment to group all "leftover" elements that do not have individual assignments into a single variable.
It is common in Python to also exploit this unpacking/packing behavior when using or defining functions that take an arbitrary number of positional or keyword arguments.
You will often see these "special" parameters defined as `def some_function(*args, **kwargs)` and the "special" arguments used as `some_function(*some_tuple, **some_dict)`.
-
```exercism/caution
`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
```
@@ -68,7 +67,6 @@ For example:
Since `tuples` are immutable, you can't swap elements in a `tuple`.
-
## Unpacking
```exercism/note
@@ -193,7 +191,7 @@ If you want to unpack the values then you can use the `values()` method:
If both **keys** and **values** are needed, use the `items()` method.
Using `items()` will generate tuples with **key-value** pairs.
-This is because [`dict.items()` generates an iterable with key-value `tuples`][items].
+This is because of [`dict.items()` generates an iterable with key-value `tuples`][items].
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
@@ -233,7 +231,7 @@ This will pack all the values into a `list`/`tuple`.
### Packing a dictionary with `**`
Packing a dictionary is done by using the `**` operator.
-This will pack all **key**-**value** pairs from one dictionary into another dictionary, or combine two dictionarys together.
+This will pack all **key**-**value** pairs from one dictionary into another dictionary, or combine two dictionaries together.
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
diff --git a/concepts/unpacking-and-multiple-assignment/introduction.md b/concepts/unpacking-and-multiple-assignment/introduction.md
index d8b91bd9c0b..a4675a771ed 100644
--- a/concepts/unpacking-and-multiple-assignment/introduction.md
+++ b/concepts/unpacking-and-multiple-assignment/introduction.md
@@ -11,16 +11,14 @@ The special operators `*` and `**` are often used in unpacking contexts.
`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`.
`**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`.
-When the `*` operator is used without a collection,it _packs_ a number of values into a `list`.
+When the `*` operator is used without a collection, it _packs_ a number of values into a `list`.
This is often used in multiple assignment to group all "leftover" elements that do not have individual assignments into a single variable.
It is common in Python to also exploit this unpacking/packing behavior when using or defining functions that take an arbitrary number of positional or keyword arguments.
You will often see these "special" parameters defined as `def some_function(*args, **kwargs)` and the "special" arguments used as `some_function(*some_tuple, **some_dict)`.
-
```exercism/caution
`*` and `**` should not be confused with `*` and `**`. While `*` and `**` are used for multiplication and exponentiation respectively, `*` and `**` are used as packing and unpacking operators.
```
-
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
index dcda62efe8a..5e7c3327a3b 100644
--- a/exercises/concept/locomotive-engineer/.docs/introduction.md
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -55,7 +55,6 @@ For example:
Since `tuples` are immutable, you can't swap elements in a `tuple`.
-
## Unpacking
```exercism/note
@@ -220,7 +219,7 @@ This will pack all the values into a `list`/`tuple`.
### Packing a dictionary with `**`
Packing a dictionary is done by using the `**` operator.
-This will pack all **key**-**value** pairs from one dictionary into another dictionary, or combine two dictionarys together.
+This will pack all **key**-**value** pairs from one dictionary into another dictionary, or combine two dictionaries together.
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
From 40542d20d0e72855d487044781af8f860ad7fafe Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 28 Nov 2022 08:17:31 +0100
Subject: [PATCH 147/932] Update introduction.md
---
exercises/concept/locomotive-engineer/.docs/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
index 5e7c3327a3b..103b5199a9a 100644
--- a/exercises/concept/locomotive-engineer/.docs/introduction.md
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -179,7 +179,7 @@ If you want to unpack the values then you can use the `values()` method:
If both **keys** and **values** are needed, use the `items()` method.
Using `items()` will generate tuples with **key-value** pairs.
-This is because [`dict.items()` generates an iterable with key-value `tuples`][items].
+This is because of [`dict.items()` generates an iterable with key-value `tuples`][items].
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
From 8350956bde2553da8c7850ec46e8399ab31878dc Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 28 Nov 2022 08:51:09 +0100
Subject: [PATCH 148/932] fixes (#3223)
Co-authored-by: BethanyG
---
concepts/unpacking-and-multiple-assignment/about.md | 1 +
exercises/concept/locomotive-engineer/.docs/introduction.md | 2 ++
2 files changed, 3 insertions(+)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index 4e1e90a2516..b61af5c845c 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -366,6 +366,7 @@ Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the
[('y', 2, False), ('z', 3, True)]
```
+[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
index 103b5199a9a..b89ad22929b 100644
--- a/exercises/concept/locomotive-engineer/.docs/introduction.md
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -354,6 +354,8 @@ Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the
[('y', 2, False), ('z', 3, True)]
```
+
+[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
From 6de3e45a5242edad3ee69e598c5212b09dc10585 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 28 Nov 2022 09:02:51 +0100
Subject: [PATCH 149/932] fixes
---
concepts/unpacking-and-multiple-assignment/about.md | 2 +-
exercises/concept/locomotive-engineer/.docs/introduction.md | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index b61af5c845c..bce43b047d9 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -306,7 +306,7 @@ You can also write parameters before `*args` to allow for specific positional ar
Individual keyword arguments then have to appear before `**kwargs`.
```exercism/caution
-[Arguments have to be structured][Positional and keyword arguments] like this:
+[Arguments have to be structured][positional and keyword arguments] like this:
`def my_function(, *args, , **kwargs)`
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
index b89ad22929b..aec5d75786d 100644
--- a/exercises/concept/locomotive-engineer/.docs/introduction.md
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -294,7 +294,7 @@ You can also write parameters before `*args` to allow for specific positional ar
Individual keyword arguments then have to appear before `**kwargs`.
```exercism/caution
-[Arguments have to be structured][Positional and keyword arguments] like this:
+[Arguments have to be structured][oositional and keyword arguments] like this:
`def my_function(, *args, , **kwargs)`
@@ -354,7 +354,6 @@ Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the
[('y', 2, False), ('z', 3, True)]
```
-
[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
From e236a38ead3d2720b37a9513e4818a40e5344ca5 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 28 Nov 2022 09:04:18 +0100
Subject: [PATCH 150/932] Update introduction.md
---
exercises/concept/locomotive-engineer/.docs/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
index aec5d75786d..655aad25188 100644
--- a/exercises/concept/locomotive-engineer/.docs/introduction.md
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -294,7 +294,7 @@ You can also write parameters before `*args` to allow for specific positional ar
Individual keyword arguments then have to appear before `**kwargs`.
```exercism/caution
-[Arguments have to be structured][oositional and keyword arguments] like this:
+[Arguments have to be structured][positional and keyword arguments] like this:
`def my_function(, *args, , **kwargs)`
From 83175bcd2802449de673cc84624f5b441f048fa8 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 28 Nov 2022 09:19:27 +0100
Subject: [PATCH 151/932] fixes
---
concepts/unpacking-and-multiple-assignment/about.md | 3 +--
exercises/concept/locomotive-engineer/.docs/introduction.md | 3 +--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index bce43b047d9..5623b2f034d 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -306,7 +306,7 @@ You can also write parameters before `*args` to allow for specific positional ar
Individual keyword arguments then have to appear before `**kwargs`.
```exercism/caution
-[Arguments have to be structured][positional and keyword arguments] like this:
+[Arguments have to be structured](https://www.python-engineer.com/courses/advancedpython/18-function-arguments/) like this:
`def my_function(, *args, , **kwargs)`
@@ -366,7 +366,6 @@ Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the
[('y', 2, False), ('z', 3, True)]
```
-[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
diff --git a/exercises/concept/locomotive-engineer/.docs/introduction.md b/exercises/concept/locomotive-engineer/.docs/introduction.md
index 655aad25188..e010c075768 100644
--- a/exercises/concept/locomotive-engineer/.docs/introduction.md
+++ b/exercises/concept/locomotive-engineer/.docs/introduction.md
@@ -294,7 +294,7 @@ You can also write parameters before `*args` to allow for specific positional ar
Individual keyword arguments then have to appear before `**kwargs`.
```exercism/caution
-[Arguments have to be structured][positional and keyword arguments] like this:
+[Arguments have to be structured](https://www.python-engineer.com/courses/advancedpython/18-function-arguments/) like this:
`def my_function(, *args, , **kwargs)`
@@ -354,7 +354,6 @@ Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the
[('y', 2, False), ('z', 3, True)]
```
-[positional and keyword arguments]: https://www.python-engineer.com/courses/advancedpython/18-function-arguments/
[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
From a43474fb13eae5921f07f054f4d7af0886cee426 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 04:18:23 -0600
Subject: [PATCH 152/932] Create config.json
---
.../practice/grains/.approaches/config.json | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/config.json
diff --git a/exercises/practice/grains/.approaches/config.json b/exercises/practice/grains/.approaches/config.json
new file mode 100644
index 00000000000..5b73df0e74a
--- /dev/null
+++ b/exercises/practice/grains/.approaches/config.json
@@ -0,0 +1,28 @@
+{
+ "introduction": {
+ "authors": ["bobahop"]
+ },
+ "approaches": [
+ {
+ "uuid": "b54a712d-e3f1-4d63-9425-7bbe2cb0bc43",
+ "slug": "exponentiation",
+ "title": "exponentiation",
+ "blurb": "Use exponentiation to raise 2 by a specified power.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "002a7924-faa4-42d8-b535-23da4dae034a",
+ "slug": "pow",
+ "title": "pow",
+ "blurb": "Use pow to raise 2 by a specified power.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "2f700645-aa46-46b3-8c43-2251420c3a9b",
+ "slug": "bit-shifting",
+ "title": "Bit-shifting",
+ "blurb": "Use bit-shifting to set the correct value.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From c352a0b752700132d2b5659488192da9f082d914 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 04:35:47 -0600
Subject: [PATCH 153/932] Create config.json
---
exercises/practice/grains/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/grains/.articles/config.json
diff --git a/exercises/practice/grains/.articles/config.json b/exercises/practice/grains/.articles/config.json
new file mode 100644
index 00000000000..95bf08b4040
--- /dev/null
+++ b/exercises/practice/grains/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "5fa8743e-3740-4e1e-b9d7-bda6856c3a08",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach to calculating Grains.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From b3fba8505986450ba67f410a5619d885bbfaf36b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 04:36:30 -0600
Subject: [PATCH 154/932] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 94 +++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 exercises/practice/grains/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/grains/.articles/performance/code/Benchmark.py b/exercises/practice/grains/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..8fef0fad053
--- /dev/null
+++ b/exercises/practice/grains/.articles/performance/code/Benchmark.py
@@ -0,0 +1,94 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""square(64)""",
+ """
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError("square must be between 1 and 64")
+ return 1 << (number - 1)
+
+def total():
+ return (1 << 64) - 1
+
+""", number=loops) / loops
+
+print(f"bit shifting square 64: {val}")
+
+
+val = timeit.timeit("""total()""",
+ """
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError("square must be between 1 and 64")
+ return 1 << (number - 1)
+
+def total():
+ return (1 << 64) - 1
+
+""", number=loops) / loops
+
+print(f"bit shifting total 64: {val}")
+
+val = timeit.timeit("""square(64)""",
+ """
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError("square must be between 1 and 64")
+
+ return 2**(number - 1)
+
+def total():
+ return 2**64 - 1
+
+""", number=loops) / loops
+
+print(f"exponentiation square 64: {val}")
+
+
+val = timeit.timeit("""total()""",
+ """
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError("square must be between 1 and 64")
+
+ return 2**(number - 1)
+
+def total():
+ return 2**64 - 1
+
+""", number=loops) / loops
+
+print(f"exponentiation total 64: {val}")
+
+val = timeit.timeit("""square(64)""",
+ """
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError("square must be between 1 and 64")
+
+ return pow(2, number-1)
+
+def total():
+ return pow(2, 64) - 1
+
+""", number=loops) / loops
+
+print(f"pow square 64: {val}")
+
+
+val = timeit.timeit("""total()""",
+ """
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError("square must be between 1 and 64")
+
+ return pow(2, number-1)
+
+def total():
+ return pow(2, 64) - 1
+
+""", number=loops) / loops
+
+print(f"pow total 64: {val}")
From 463fab709a28b1ec71169797874b5b32d08265f7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 04:37:18 -0600
Subject: [PATCH 155/932] Create snippet.md
---
.../practice/grains/.articles/performance/snippet.md | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/grains/.articles/performance/snippet.md
diff --git a/exercises/practice/grains/.articles/performance/snippet.md b/exercises/practice/grains/.articles/performance/snippet.md
new file mode 100644
index 00000000000..b12b828c20e
--- /dev/null
+++ b/exercises/practice/grains/.articles/performance/snippet.md
@@ -0,0 +1,8 @@
+```
+bit shifting square 64: 1.2760140001773835e-07
+bit shifting total 64: 6.096410000463947e-08
+exponentiation square 64: 4.122742000035942e-07
+exponentiation total 64: 6.094090000260621e-08
+pow square 64: 4.2468130000634117e-07
+pow total 64: 3.1965399999171494e-07
+```
From 7761d9ad195eb13a6add17995e6176de1c20ef62 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 04:55:37 -0600
Subject: [PATCH 156/932] Create content.md
---
.../grains/.articles/performance/content.md | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 exercises/practice/grains/.articles/performance/content.md
diff --git a/exercises/practice/grains/.articles/performance/content.md b/exercises/practice/grains/.articles/performance/content.md
new file mode 100644
index 00000000000..740afca58d9
--- /dev/null
+++ b/exercises/practice/grains/.articles/performance/content.md
@@ -0,0 +1,44 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently calculate the value for Grains in Python.
+
+The [approaches page][approaches] lists three idiomatic approaches to this exercise:
+
+1. [Using bit shifting][approach-bit-shifting]
+2. [Using exponentiation][approach-exponentiation]
+3. [Using `pow`][approach-pow]
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [timeit][timeit] library.
+
+```
+bit shifting square 64: 1.2760140001773835e-07
+bit shifting total 64: 6.096410000463947e-08
+exponentiation square 64: 4.122742000035942e-07
+exponentiation total 64: 6.094090000260621e-08
+pow square 64: 4.2468130000634117e-07
+pow total 64: 3.1965399999171494e-07
+```
+
+- Bit shifting was the fastest for `square`.
+- Bit shifting and exponentiation were about the same for `total`.
+- Exponentiation and `pow` were about the same for `square`.
+- `pow` was much significantly the slowest for `total`.
+
+Benchmarks were also done to substitute `if number not in range(1, 65):` for `if number < 1 or number > 64:`.
+
+```
+bit shifting square 64: 2.708769000018947e-07
+exponentiation square 64: 5.56936200009659e-07
+pow square 64: 5.738279999932274e-07
+```
+
+Using `if number not in range(1, 65):` was over `125` nanoseconds longer than using `if number < 1 or number > 64:` for all approaches.
+
+[approaches]: https://exercism.org/tracks/python/exercises/grains/approaches
+[approach-bit-shifting]: https://exercism.org/python/csharp/exercises/grains/approaches/bit-shifting
+[approach-pow]: https://exercism.org/tracks/python/exercises/grains/approaches/pow
+[approach-exponentiation]: https://exercism.org/tracks/python/exercises/grains/approaches/exponentiation
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/grains/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
From 590bf3654e30c2f529f1fc2ba4143390061f2847 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:11:51 -0600
Subject: [PATCH 157/932] Update content.md
---
exercises/practice/grains/.articles/performance/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/grains/.articles/performance/content.md b/exercises/practice/grains/.articles/performance/content.md
index 740afca58d9..b1150018314 100644
--- a/exercises/practice/grains/.articles/performance/content.md
+++ b/exercises/practice/grains/.articles/performance/content.md
@@ -24,7 +24,7 @@ pow total 64: 3.1965399999171494e-07
- Bit shifting was the fastest for `square`.
- Bit shifting and exponentiation were about the same for `total`.
- Exponentiation and `pow` were about the same for `square`.
-- `pow` was much significantly the slowest for `total`.
+- `pow` was significantly the slowest for `total`.
Benchmarks were also done to substitute `if number not in range(1, 65):` for `if number < 1 or number > 64:`.
From 37cb8523282c55dc335cea13d15cdaa25814b62f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:15:30 -0600
Subject: [PATCH 158/932] Create introduction.md
---
.../grains/.approaches/introduction.md | 91 +++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/introduction.md
diff --git a/exercises/practice/grains/.approaches/introduction.md b/exercises/practice/grains/.approaches/introduction.md
new file mode 100644
index 00000000000..13f56d33be8
--- /dev/null
+++ b/exercises/practice/grains/.approaches/introduction.md
@@ -0,0 +1,91 @@
+# Introduction
+
+There are various idiomatic approaches to solve Grains.
+You can use [bit shifting][bit-shifting] to calculate the number on grains on a square.
+Or you can use [exponentiation][exponentiation].
+Another approach is to use [`pow`][pow].
+
+## General guidance
+
+The key to solving Grains is to focus on each square having double the amount of grains as the square before it.
+This means that the amount of grains grows exponentially.
+The first square has one grain, which is `2` to the power of `0`.
+The second square has two grains, which is `2` to the power of `1`.
+The third square has four grains, which is `2` to the power of `2`.
+You can see that the exponent, or power, that `2` is raised by is always one less than the square number.
+
+| Square | Power | Value |
+| ------ | ----- | ----------------------- |
+| 1 | 0 | 2 to the power of 0 = 1 |
+| 2 | 1 | 2 to the power of 1 = 2 |
+| 3 | 2 | 2 to the power of 2 = 4 |
+| 4 | 3 | 2 to the power of 4 = 8 |
+
+
+## Approach: Bit-shifting
+
+```python
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+
+ return 1 << number - 1
+
+
+def total():
+ return (1 << 64) - 1
+
+```
+
+For more information, check the [bit-shifting approach][approach-bit-shifting].
+
+## Approach: Exponentiation
+
+```python
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+
+ return 2 ** (number - 1)
+
+
+def total():
+ return 2 ** 64 - 1
+
+```
+
+For more information, check the [exponentiation approach][approach-exponentiation].
+
+## Approach: `pow`
+
+```python
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+
+ return pow(2, number - 1)
+
+
+def total():
+ return pow(2, 64) - 1
+
+```
+
+For more information, check the [`pow` approach][approach-pow].
+
+## Which approach to use?
+
+- Bit shifting is the fastest for `square`.
+- Bit shifting and exponentiation are about the same for `total`.
+- Exponentiation and `pow` are about the same for `square`.
+- `pow` is significantly the slowest for `total`.
+
+For more information, check the [Performance article][article-performance].
+
+[bit-shifting]: https://realpython.com/python-bitwise-operators/
+[exponentiation]: https://www.codingem.com/python-exponent-maths/
+[pow]: https://docs.python.org/3/library/functions.html#pow
+[approach-bit-shifting]: https://exercism.org/tracks/python/exercises/grains/approaches/bit-shifting
+[approach-exponentiation]: https://exercism.org/tracks/python/exercises/grains/approaches/exponentiation
+[approach-pow]: https://exercism.org/tracks/python/exercises/grains/approaches/pow
+[article-performance]: https://exercism.org/tracks/python/exercises/grains/articles/performance
From 0033af4967d9ba3805995bf66de0ba85bcd81ffc Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:16:35 -0600
Subject: [PATCH 159/932] Create snippet.txt
---
.../practice/grains/.approaches/bit-shifting/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/bit-shifting/snippet.txt
diff --git a/exercises/practice/grains/.approaches/bit-shifting/snippet.txt b/exercises/practice/grains/.approaches/bit-shifting/snippet.txt
new file mode 100644
index 00000000000..28566f11735
--- /dev/null
+++ b/exercises/practice/grains/.approaches/bit-shifting/snippet.txt
@@ -0,0 +1,8 @@
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+ return 1 << number - 1
+
+
+def total():
+ return (1 << 64) - 1
From f8f67aa44d6341c9ceeaf4515a81f46c3b79cf5d Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:17:15 -0600
Subject: [PATCH 160/932] Create snippet.txt
---
.../grains/.approaches/exponentiation/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/exponentiation/snippet.txt
diff --git a/exercises/practice/grains/.approaches/exponentiation/snippet.txt b/exercises/practice/grains/.approaches/exponentiation/snippet.txt
new file mode 100644
index 00000000000..483206302d8
--- /dev/null
+++ b/exercises/practice/grains/.approaches/exponentiation/snippet.txt
@@ -0,0 +1,8 @@
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+ return 2 ** (number - 1)
+
+
+def total():
+ return 2 ** 64 - 1
From 6a63153300a317baa6911d1570350bd5bd3df60f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:17:51 -0600
Subject: [PATCH 161/932] Create snippet.txt
---
exercises/practice/grains/.approaches/pow/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/pow/snippet.txt
diff --git a/exercises/practice/grains/.approaches/pow/snippet.txt b/exercises/practice/grains/.approaches/pow/snippet.txt
new file mode 100644
index 00000000000..f8742af4060
--- /dev/null
+++ b/exercises/practice/grains/.approaches/pow/snippet.txt
@@ -0,0 +1,8 @@
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+ return pow(2, number - 1)
+
+
+def total():
+ return pow(2, 64) - 1
From bc2490b183a3583a805b2b6a137720a1f31e0318 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:30:56 -0600
Subject: [PATCH 162/932] Create content.md
---
.../.approaches/bit-shifting/content.md | 50 +++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/bit-shifting/content.md
diff --git a/exercises/practice/grains/.approaches/bit-shifting/content.md b/exercises/practice/grains/.approaches/bit-shifting/content.md
new file mode 100644
index 00000000000..409c4ffba07
--- /dev/null
+++ b/exercises/practice/grains/.approaches/bit-shifting/content.md
@@ -0,0 +1,50 @@
+# Bit-shifting
+
+```python
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+
+ return 1 << number - 1
+
+
+def total():
+ return (1 << 64) - 1
+
+```
+
+Instead of using math for calculation, you can set a bit in the correct position for the number of grains on a square.
+
+To understand how this works, consider just two squares that are represented in binary bits as `00`.
+
+You use the [left-shift operator][left-shift-operator] to set `1` at the position needed to make the correct decimal value.
+- To set the one grain on Square One you shift `1` for `0` positions to the left.
+So, if `number` is `1` for square One, you subtract `number` by `1` to get `0`, which will not move it any positions to the left.
+The result is binary `01`, which is decimal `1`.
+- To set the two grains on Square Two you shift `1` for `1` position to the left.
+So, if `number` is `2` for square Two, you subtract `number` by `1` to get `1`, which will move it `1` position to the left.
+The result is binary `10`, which is decimal `2`.
+
+| Square | Shift Left By | Binary Value | Decimal Value |
+| ------- | ------------- | ------------ | ------------- |
+| 1 | 0 | 0001 | 1 |
+| 2 | 1 | 0010 | 2 |
+| 3 | 2 | 0100 | 4 |
+| 4 | 3 | 1000 | 8 |
+
+For `total` we want all of the 64 bits set to `1` to get the sum of grains on all sixty-four squares.
+The easy way to do this is to set the 65th bit to `1` and then subtract `1`.
+To go back to our two-square example, if we can grow to three squares, then we can shift `1` two positions to the left for binary `100`,
+which is decimal `4`.
+By subtracting `1` we get `3`, which is the total amount of grains on the two squares.
+
+| Square | Binary Value | Decimal Value |
+| ------- | ------------ | ------------- |
+| 3 | 0100 | 4 |
+
+| Square | Sum Binary Value | Sum Decimal Value |
+| ------- | ---------------- | ----------------- |
+| 1 | 0001 | 1 |
+| 2 | 0011 | 3 |
+
+[left-shift-operator]: https://realpython.com/python-bitwise-operators/#left-shift
From 08460bbbec17166a7f61618e8bb56167fb80e462 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:35:43 -0600
Subject: [PATCH 163/932] Create content.md
---
.../.approaches/exponentiation/content.md | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/exponentiation/content.md
diff --git a/exercises/practice/grains/.approaches/exponentiation/content.md b/exercises/practice/grains/.approaches/exponentiation/content.md
new file mode 100644
index 00000000000..95e65ea2df2
--- /dev/null
+++ b/exercises/practice/grains/.approaches/exponentiation/content.md
@@ -0,0 +1,33 @@
+## Exponentiation
+
+```python
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+
+ return 2 ** (number - 1)
+
+
+def total():
+ return 2 ** 64 - 1
+
+```
+
+Python uses the exponential operator (`**`) to raise a number by a certain exponent.
+
+[Exponentiation][exponentiation] is nicely suited to the problem, since we start with one grain and keep doubling the number of grains on each successive square.
+`1` grain is `2 ** 0`, `2` grains is `2 ** 1`, `4` is `2 ** 2`, and so on.
+
+So, to get the right exponent, we subtract `1` from the square `number`.
+
+The easiest way to get `total` is to get the value for an imaginary 65th square,
+and then subtract `1` from it.
+To understand how that works, consider a board that has only two squares.
+If we could grow the board to three squares, then we could get the number of grains on the imaginary third square,
+which would be `4.`
+You could then subtract `4` by `1` to get `3`, which is the number of grains on the first square (`1`) and the second square (`2`).
+Remembering that the exponent must be one less than the square you want,
+you can call `2 ** 64` to get the number of grains on the imaginary 65th square.
+Subtracting that value by `1` gives the values on all `64` squares.
+
+[exponentiation]: https://www.codingem.com/python-exponent-maths/
From 3b7ac87c403dde35e9b5778c86edb069d66166a3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 16 Nov 2022 05:41:42 -0600
Subject: [PATCH 164/932] Create content.md
---
.../grains/.approaches/pow/content.md | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 exercises/practice/grains/.approaches/pow/content.md
diff --git a/exercises/practice/grains/.approaches/pow/content.md b/exercises/practice/grains/.approaches/pow/content.md
new file mode 100644
index 00000000000..5a3dc1ee99c
--- /dev/null
+++ b/exercises/practice/grains/.approaches/pow/content.md
@@ -0,0 +1,31 @@
+## `pow`
+
+```python
+def square(number):
+ if number < 1 or number > 64:
+ raise ValueError('square must be between 1 and 64')
+
+ return pow(2, number - 1)
+
+
+def total():
+ return pow(2, 64) - 1
+
+```
+
+[`pow`][pow] is nicely suited to the problem, since we start with one grain and keep doubling the number of grains on each successive square.
+`1` grain is `pow(2, 0)`, `2` grains is `pow(2, 1)`, `4` is `pow(2, 2)`, and so on.
+
+So, to get the right exponent, we subtract `1` from the square `number`.
+
+The easiest way to get `total` is to use `pow` to get the value for an imaginary 65th square,
+and then subtract `1` from it.
+To understand how that works, consider a board that has only two squares.
+If we could grow the board to three squares, then we could get the number of grains on the imaginary third square,
+which would be `4.`
+You could then subtract `4` by `1` to get `3`, which is the number of grains on the first square (`1`) and the second square (`2`).
+Remembering that the exponent must be one less than the square you want,
+you can call `pow(2, 64)` to get the number of grains on the imaginary 65th square.
+Subtracting that value by `1` gives the values on all `64` squares.
+
+[pow]: https://docs.python.org/3/library/functions.html#pow
From 9f7befe39cd81564a69f6b47980fd9d4654853fe Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Tue, 29 Nov 2022 08:48:45 +0100
Subject: [PATCH 165/932] Fail closed on pause contributions workflow
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In the original workflow, I had `catch(err) => false`.
But during code review the question was raised:
What happens if we get rate limited and all the requests from the API fail?
(E.g. if we do a script and automatically create PRs across all of Exercism, which is totally a thing).
We were like: ooh, that would suck, wouldn’t it? It would be better if we occasionally had to deal with manually closing a PR.
But here’s the kicker. The API response has no body. It’s either 204 or 404, where 404 is perceived as… an error.
So I changed it, and (importantly) forgot to test the script one final time.
So here is a version that will actually work. (I tested.)
---
.github/workflows/pause-community-contributions.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pause-community-contributions.yml b/.github/workflows/pause-community-contributions.yml
index 85d18d1c5d7..16b1ca555d9 100644
--- a/.github/workflows/pause-community-contributions.yml
+++ b/.github/workflows/pause-community-contributions.yml
@@ -30,7 +30,7 @@ jobs:
org: context.repo.owner,
username: context.actor,
}).then(response => response.status == 204)
- .catch(err => true);
+ .catch(err => false);
- name: Comment
if: steps.is-organization-member.outputs.result == 'false'
uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
From 5f2f91ffac09649090a3257d4c39f8968b80d563 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 05:35:11 -0600
Subject: [PATCH 166/932] Create config.json
---
.../practice/pangram/.approaches/config.json | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/config.json
diff --git a/exercises/practice/pangram/.approaches/config.json b/exercises/practice/pangram/.approaches/config.json
new file mode 100644
index 00000000000..550a3b5e11a
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/config.json
@@ -0,0 +1,35 @@
+{
+ "introduction": {
+ "authors": ["bobahop"]
+ },
+ "approaches": [
+ {
+ "uuid": "999c333a-2516-4d91-9a8f-7cbc39e56914",
+ "slug": "all",
+ "title": "all on lower case",
+ "blurb": "Use all on lowercased letters.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "57720fea-8fe6-44c6-80b6-b8f356aa3008",
+ "slug": "set-issubset",
+ "title": "set with issubset",
+ "blurb": "Use set with issubset.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "2743b5c4-fbd1-4a73-ae39-d4d275313494",
+ "slug": "set-len",
+ "title": "set with len",
+ "blurb": "Use set with len.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "0a6d1bbf-6d60-4489-b8d9-b8375894628b",
+ "slug": "bitfield",
+ "title": "Bit field",
+ "blurb": "Use a bit field to keep track of used letters.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From c7a57105b70b0f7f272c6933096c8429140afd5d Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 05:36:38 -0600
Subject: [PATCH 167/932] Create config.json
---
exercises/practice/pangram/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/pangram/.articles/config.json
diff --git a/exercises/practice/pangram/.articles/config.json b/exercises/practice/pangram/.articles/config.json
new file mode 100644
index 00000000000..b7de79a678c
--- /dev/null
+++ b/exercises/practice/pangram/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "f832ad8a-09dc-4929-9e46-d3e7c286a063",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach to determining a pangram.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 5d05960ff30affff08c8ab26f1e29cb323dbcc86 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 05:38:49 -0600
Subject: [PATCH 168/932] Create snippet.md
---
.../practice/pangram/.articles/performance/snippet.md | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/pangram/.articles/performance/snippet.md
diff --git a/exercises/practice/pangram/.articles/performance/snippet.md b/exercises/practice/pangram/.articles/performance/snippet.md
new file mode 100644
index 00000000000..0509fbee539
--- /dev/null
+++ b/exercises/practice/pangram/.articles/performance/snippet.md
@@ -0,0 +1,7 @@
+```
+all: 1.505466179997893e-05
+all: 1.6063886400021147e-05 // with sentence.casefold()
+set: 1.950172399985604e-06
+len: 3.7158977999933994e-06
+bit: 8.75982620002469e-06
+```
From 54c76d67b2022baf50bf2c0cc029103fda943f39 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 05:39:40 -0600
Subject: [PATCH 169/932] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 54 +++++++++++++++++++
1 file changed, 54 insertions(+)
create mode 100644 exercises/practice/pangram/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/pangram/.articles/performance/code/Benchmark.py b/exercises/practice/pangram/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..6ab5c8b1cf9
--- /dev/null
+++ b/exercises/practice/pangram/.articles/performance/code/Benchmark.py
@@ -0,0 +1,54 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""is_pangram("Victor jagt zwölf_(12) Boxkämpfer quer über den großen Sylter Deich.")""",
+ """
+from string import ascii_lowercase
+def is_pangram(sentence):
+ return all(letter in sentence.lower() for letter in ascii_lowercase)
+
+""", number=loops) / loops
+
+print(f"all: {val}")
+
+val = timeit.timeit("""is_pangram("Victor jagt zwölf_(12) Boxkämpfer quer über den großen Sylter Deich.")""",
+ """
+from string import ascii_lowercase
+
+ALPHABET = set(ascii_lowercase)
+
+def is_pangram(string):
+ return ALPHABET.issubset(string.lower())
+
+""", number=loops) / loops
+
+print(f"set: {val}")
+
+val = timeit.timeit("""is_pangram("Victor jagt zwölf_(12) Boxkämpfer quer über den großen Sylter Deich.")""",
+ """
+def is_pangram(sentence):
+ return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) == 26
+
+""", number=loops) / loops
+
+print(f"len: {val}")
+
+val = timeit.timeit("""is_pangram("Victor jagt zwölf_(12) Boxkämpfer quer über den großen Sylter Deich.")""",
+ """
+A_LCASE = 97;
+A_UCASE = 65;
+ALL_26_BITS_SET = 67108863;
+
+def is_pangram(sentence):
+ letter_flags = 0
+ for letter in sentence:
+ if letter >= 'a' and letter <= 'z':
+ letter_flags |= 1 << (ord(letter) - A_LCASE)
+ elif letter >= 'A' and letter <= 'Z':
+ letter_flags |= 1 << (ord(letter) - A_UCASE)
+ return letter_flags == ALL_26_BITS_SET
+
+""", number=loops) / loops
+
+print(f"bit: {val}")
From fa542ca742ef4050822d396470dc69f2e7c989a7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 05:53:11 -0600
Subject: [PATCH 170/932] Create content.md
---
.../pangram/.articles/performance/content.md | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 exercises/practice/pangram/.articles/performance/content.md
diff --git a/exercises/practice/pangram/.articles/performance/content.md b/exercises/practice/pangram/.articles/performance/content.md
new file mode 100644
index 00000000000..4f05a4157e2
--- /dev/null
+++ b/exercises/practice/pangram/.articles/performance/content.md
@@ -0,0 +1,37 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently determine if a string is a Pangram in Python.
+
+The [approaches page][approaches] lists three idiomatic approaches to this exercise:
+
+1. [Using `all` on lowercased letters][approach-all]
+2. [Using `set` with `issubset`][approach-set-issubset]
+3. [Using `set` with `len`][approach-set-len]
+
+For our performance investigation, we'll also include a fourth approach that [uses a bit field to keep track of used letters][approach-bitfield].
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+all: 1.505466179997893e-05
+all: 1.6063886400021147e-05 // with sentence.casefold()
+set: 1.950172399985604e-06
+len: 3.7158977999933994e-06
+bit: 8.75982620002469e-06
+```
+
+- The `set` `len` approach is not as fast as the `set` `issubset` approach.
+- The `all` approach is slower than either `set` approach.
+Using `casefold` was slower than using `lower`.
+- Although the bit field approach may be faster in other languages, it is significantly slower in Python.
+It is faster than the `all` approach, but much slower than either `set` approach.
+
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/pangram/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
+[approaches]: https://exercism.org/tracks/python/exercises/pangram/approaches
+[approach-all]: https://exercism.org/tracks/python/exercises/pangram/approaches/all
+[approach-set-issubset]: https://exercism.org/tracks/python/exercises/pangram/approaches/set-issubset
+[approach-set-len]: https://exercism.org/tracks/python/exercises/pangram/approaches/set-len
+[approach-bitfield]: https://exercism.org/tracks/python/exercises/pangram/approaches/bitfield
From 9eb76a10f328d0673b6541559558fb52b9bb7ce1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 05:58:12 -0600
Subject: [PATCH 171/932] Update Benchmark.py
---
.../practice/pangram/.articles/performance/code/Benchmark.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/pangram/.articles/performance/code/Benchmark.py b/exercises/practice/pangram/.articles/performance/code/Benchmark.py
index 6ab5c8b1cf9..729cc967dc9 100644
--- a/exercises/practice/pangram/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/pangram/.articles/performance/code/Benchmark.py
@@ -18,8 +18,8 @@ def is_pangram(sentence):
ALPHABET = set(ascii_lowercase)
-def is_pangram(string):
- return ALPHABET.issubset(string.lower())
+def is_pangram(sentence):
+ return ALPHABET.issubset(sentence.lower())
""", number=loops) / loops
From 8cfcb6552660be8775d5d1249e52d5c5f44f9dc2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:19:48 -0600
Subject: [PATCH 172/932] Create introduction.md
---
.../pangram/.approaches/introduction.md | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/introduction.md
diff --git a/exercises/practice/pangram/.approaches/introduction.md b/exercises/practice/pangram/.approaches/introduction.md
new file mode 100644
index 00000000000..9536fe63d59
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/introduction.md
@@ -0,0 +1,71 @@
+# Introduction
+
+There are various idomatic approaches to Pangram.
+You can use the `all` method on the `ascii_lowercase` letters with the lowercased letters of the `sentence`.
+You can see if the `set` of the alphabet `issubset` of a `set` of the lowercased `sentence`.
+Or you can see if the `set` `len` of the lowercased `sentence` filtered to just ASCII letters is `26`.
+
+## General guidance
+
+The key to solving Pangram is determining if all of the letters in the alphabet are in the `sentence` being tested.
+The occurrence of either the letter `a` or the letter `A` would count as the same letter.
+
+## Approach: `all` on lowercased letters
+
+```python
+from string import ascii_lowercase
+
+
+def is_pangram(sentence):
+ return all(letter in sentence.lower() for letter in ascii_lowercase)
+
+```
+
+For more information, check the [`all` approach][approach-all].
+
+## Approach: `set` with `issubset` on lowercased characters
+
+```python
+from string import ascii_lowercase
+
+ALPHABET = set(ascii_lowercase)
+
+
+def is_pangram(sentence):
+ return ALPHABET.issubset(sentence.lower())
+
+```
+
+For more information, check the [`set` with `issubset` approach][approach-set-issubset].
+
+## Approach: `set` with `len` on lowercased characters
+
+```python
+def is_pangram(sentence):
+ return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \
+ == 26
+
+```
+
+For more information, check the [`set` with `len` approach][approach-set-len].
+
+## Other approaches
+
+Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows:
+
+### Other approach: Bit field
+
+Another approach can use a bit field to keep track of used letters.
+For more information, check the [Bit field approach][approach-bitfield].
+
+## Which approach to use?
+
+The fastest is the `set` `issubset` approach.
+
+To compare performance of the approaches, check the [Performance article][article-performance].
+
+[approach-all]: https://exercism.org/tracks/python/exercises/pangram/approaches/all
+[approach-set-issubset]: https://exercism.org/tracks/python/exercises/pangram/approaches/set-issubset
+[approach-set-len]: https://exercism.org/tracks/python/exercises/pangram/approaches/set-len
+[approach-bitfield]: https://exercism.org/tracks/python/exercises/pangram/approaches/bitfield
+[article-performance]: https://exercism.org/tracks/python/exercises/pangram/articles/performance
From 9b69187dbfeba9dee0eeabb758449d72f7217261 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:24:03 -0600
Subject: [PATCH 173/932] Create snippet.txt
---
exercises/practice/pangram/.approaches/all/snippet.txt | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/all/snippet.txt
diff --git a/exercises/practice/pangram/.approaches/all/snippet.txt b/exercises/practice/pangram/.approaches/all/snippet.txt
new file mode 100644
index 00000000000..7e40ca76aa6
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/all/snippet.txt
@@ -0,0 +1,5 @@
+from string import ascii_lowercase
+
+
+def is_pangram(sentence):
+ return all(letter in sentence.lower() for letter in ascii_lowercase)
From 6a09dd9a83ab1f67b200c8f2647dae48342687c7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:24:49 -0600
Subject: [PATCH 174/932] Create snippet.txt
---
.../practice/pangram/.approaches/set-issubset/snippet.txt | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/set-issubset/snippet.txt
diff --git a/exercises/practice/pangram/.approaches/set-issubset/snippet.txt b/exercises/practice/pangram/.approaches/set-issubset/snippet.txt
new file mode 100644
index 00000000000..7e5e9272cc9
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/set-issubset/snippet.txt
@@ -0,0 +1,7 @@
+from string import ascii_lowercase
+
+ALPHABET = set(ascii_lowercase)
+
+
+def is_pangram(sentence):
+ return ALPHABET.issubset(sentence.lower())
From f48f213d207d44ac79a18887d0823f131d09ee85 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:25:24 -0600
Subject: [PATCH 175/932] Create snippet.txt
---
exercises/practice/pangram/.approaches/set-len/snippet.txt | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/set-len/snippet.txt
diff --git a/exercises/practice/pangram/.approaches/set-len/snippet.txt b/exercises/practice/pangram/.approaches/set-len/snippet.txt
new file mode 100644
index 00000000000..9a6a6d537bf
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/set-len/snippet.txt
@@ -0,0 +1,3 @@
+def is_pangram(sentence):
+ return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \
+ == 26
From 8fce5b9028e8c048f11ffa88649eab3edff74b2f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:26:47 -0600
Subject: [PATCH 176/932] Create snippet.txt
---
.../practice/pangram/.approaches/bitfield/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/bitfield/snippet.txt
diff --git a/exercises/practice/pangram/.approaches/bitfield/snippet.txt b/exercises/practice/pangram/.approaches/bitfield/snippet.txt
new file mode 100644
index 00000000000..991e4568d04
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/bitfield/snippet.txt
@@ -0,0 +1,8 @@
+def is_pangram(sentence):
+ letter_flags = 0
+ for letter in sentence:
+ if letter >= 'a' and letter <= 'z':
+ letter_flags |= 1 << ord(letter) - A_LCASE
+ elif letter >= 'A' and letter <= 'Z':
+ letter_flags |= 1 << ord(letter) - A_UCASE
+ return letter_flags == ALL_26_BITS_SET
From 9527ee0a54be0b61d017cd261a870ac0d6f7326f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:34:21 -0600
Subject: [PATCH 177/932] Create content.md
---
.../pangram/.approaches/all/content.md | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/all/content.md
diff --git a/exercises/practice/pangram/.approaches/all/content.md b/exercises/practice/pangram/.approaches/all/content.md
new file mode 100644
index 00000000000..7aace376a6e
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/all/content.md
@@ -0,0 +1,20 @@
+# `all` on lowercased letters
+
+```python
+from string import ascii_lowercase
+
+
+def is_pangram(sentence):
+ return all(letter in sentence.lower() for letter in ascii_lowercase)
+
+```
+
+- This begins by importing all of the [ascii_lowercase][ascii-lowercase] letters.
+- It lowercases the input by using the [lower][lower] method.
+- It then checks if all letters in the lowercase alphabet are contained in the lowercased `sentence`,
+using the [`all`][all] function.
+- If all of the letters in the alphabet are contained in the `sentence`, then the function will return `True`.
+
+[ascii-lowercase]: https://docs.python.org/3/library/string.html#string.ascii_lowercase
+[lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower
+[all]: https://docs.python.org/3/library/functions.html#all
From b371a18abeb6d6b80b59c3363643273225b25362 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:42:05 -0600
Subject: [PATCH 178/932] Create content.md
---
.../.approaches/set-issubset/content.md | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/set-issubset/content.md
diff --git a/exercises/practice/pangram/.approaches/set-issubset/content.md b/exercises/practice/pangram/.approaches/set-issubset/content.md
new file mode 100644
index 00000000000..34d012b246e
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/set-issubset/content.md
@@ -0,0 +1,24 @@
+# `HashSet` with `is_subset`
+
+```python
+from string import ascii_lowercase
+
+ALPHABET = set(ascii_lowercase)
+
+
+def is_pangram(sentence):
+ return ALPHABET.issubset(sentence.lower())
+
+```
+
+In this approach a [set][set] is made from the [ascii_lowercase][ascii-lowercase] letters,
+and another `set` is made from the [lower][lower]cased letters in the `sentence`.
+
+The function returns if the alphabet `set` [issubset][issubset] of the `sentence` `set`.
+If all of the letters in the alphabet are a subset of the letters in the `sentence`,
+then the function will return `True`.
+
+[set]: https://docs.python.org/3/library/stdtypes.html?#set
+[ascii-lowercase]: https://docs.python.org/3/library/string.html#string.ascii_lowercase
+[lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower
+[issubset]: https://docs.python.org/3/library/stdtypes.html?highlight=issubset#frozenset.issubset
From c661291db2a80bc1393cacfdfa95c23dafa7c326 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:59:03 -0600
Subject: [PATCH 179/932] Create content.md
---
.../pangram/.approaches/set-len/content.md | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/set-len/content.md
diff --git a/exercises/practice/pangram/.approaches/set-len/content.md b/exercises/practice/pangram/.approaches/set-len/content.md
new file mode 100644
index 00000000000..645a47674ae
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/set-len/content.md
@@ -0,0 +1,21 @@
+# `set` with `len`
+
+```python
+def is_pangram(sentence):
+ return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \
+ == 26
+
+```
+
+- This approach first makes a [set][set] from the [`lower`][lower]cased characters of the `sentence`.
+- The characters in the `set`are then iterated in a [list comprehension][list-comprehension].
+- The characters are filtered by an `if` [`isalpha()`][isalpha] statement, so that only alphabetic characters make it into the list.
+- The function returns if the [`len`][len] of the [`list`][list] is `26`.
+If the number of unique letters in the `set` is equal to the `26` letters in the alphabet, then the function will return `True`.
+
+[lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower
+[set]: https://docs.python.org/3/library/stdtypes.html?#set
+[list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
+[isalpha]: https://docs.python.org/3/library/stdtypes.html?highlight=isalpha#str.isalpha
+[len]: https://docs.python.org/3/library/functions.html?#len
+[list]: https://docs.python.org/3/library/stdtypes.html?#list
From be737116c0f7a7dd95f1eb73e65387cf3819830d Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 06:59:51 -0600
Subject: [PATCH 180/932] Update content.md
---
exercises/practice/pangram/.approaches/set-issubset/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/pangram/.approaches/set-issubset/content.md b/exercises/practice/pangram/.approaches/set-issubset/content.md
index 34d012b246e..1c43c637dd9 100644
--- a/exercises/practice/pangram/.approaches/set-issubset/content.md
+++ b/exercises/practice/pangram/.approaches/set-issubset/content.md
@@ -12,7 +12,7 @@ def is_pangram(sentence):
```
In this approach a [set][set] is made from the [ascii_lowercase][ascii-lowercase] letters,
-and another `set` is made from the [lower][lower]cased letters in the `sentence`.
+and another `set` is made from the [`lower`][lower]cased letters in the `sentence`.
The function returns if the alphabet `set` [issubset][issubset] of the `sentence` `set`.
If all of the letters in the alphabet are a subset of the letters in the `sentence`,
From 8d2697260fd26a15dbe9b69e6b65a1b72961d7b2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 07:13:03 -0600
Subject: [PATCH 181/932] Create content.md
---
.../pangram/.approaches/bitfield/content.md | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 exercises/practice/pangram/.approaches/bitfield/content.md
diff --git a/exercises/practice/pangram/.approaches/bitfield/content.md b/exercises/practice/pangram/.approaches/bitfield/content.md
new file mode 100644
index 00000000000..20e460fea0d
--- /dev/null
+++ b/exercises/practice/pangram/.approaches/bitfield/content.md
@@ -0,0 +1,62 @@
+# Bit field
+
+```python
+A_LCASE = 97
+A_UCASE = 65
+ALL_26_BITS_SET = 67108863
+
+
+def is_pangram(sentence):
+ letter_flags = 0
+ for letter in sentence:
+ if letter >= 'a' and letter <= 'z':
+ letter_flags |= 1 << ord(letter) - A_LCASE
+ elif letter >= 'A' and letter <= 'Z':
+ letter_flags |= 1 << ord(letter) - A_UCASE
+ return letter_flags == ALL_26_BITS_SET
+
+```
+
+This solution uses the [ASCII][ascii] value of the letter to set the corresponding bit position.
+First, some [constant][const] values are set.
+
+```exercism/note
+Python doesn't _enforce_ having real constant values,
+but using all uppercase letters is the naming convention for a Python constant.
+It indicates that the value is not intended to be changed.
+```
+
+These values will be used for readability in the body of the `is_pangram` function.
+The ASCII value for `a` is `97`.
+The ASCII value for `A` is `65`.
+The value for all of the rightmost `26` bits being set is `67108863`.
+
+- The [`for` loop][for-loop] loops through the characters of the `sentence`.
+- Each letter is tested for being `a` through `z` or `A` through `Z`.
+- If the lowercased letter is subtracted by `a`, then `a` will result in `0`, because `97` minus `97` equals `0`.
+`z` would result in `25`, because `122` minus `97` equals `25`.
+So `a` would have `1` [shifted left][shift-left] 0 places (so not shifted at all) and `z` would have `1` shifted left 25 places.
+- If the uppercased letter is subtracted by `A`, then `A` will result in `0`, because `65` minus `65` equals `0`.
+`Z` would result in `25`, because `90` minus `65` equals `25`.
+So `A` would have `1` [shifted left][shift-left] 0 places (so not shifted at all) and `Z` would have `1` shifted left 25 places.
+
+In that way, both a lower-cased `z` and an upper-cased `Z` can share the same position in the bit field.
+
+So, for a thirty-two bit integer, if the values for `a` and `Z` were both set, the bits would look like
+
+```
+ zyxwvutsrqponmlkjihgfedcba
+00000010000000000000000000000001
+```
+
+We can use the [bitwise OR operator][or] to set the bit.
+After the loop completes, the function returns `True` if the `letter_flags` value is the same value as when all of the rightmost `26` bits are set,
+which is `67108863`.
+
+Although this approach is usually very fast in some other languages, it is comparatively slow in Python.
+
+[ascii]: https://www.asciitable.com/
+[const]: https://realpython.com/python-constants/
+[for-loop]: https://wiki.python.org/moin/ForLoop
+[shift-left]: https://realpython.com/python-bitwise-operators/#left-shift
+[or]: https://realpython.com/python-bitwise-operators/#bitwise-or
From f43f8cae06f23b7f0aa9f22fcf86929ce18858e8 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 08:12:03 -0600
Subject: [PATCH 182/932] Update content.md
---
.../practice/pangram/.approaches/all/content.md | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/pangram/.approaches/all/content.md b/exercises/practice/pangram/.approaches/all/content.md
index 7aace376a6e..ce09af36d5a 100644
--- a/exercises/practice/pangram/.approaches/all/content.md
+++ b/exercises/practice/pangram/.approaches/all/content.md
@@ -1,4 +1,4 @@
-# `all` on lowercased letters
+# `all()` on lowercased letters
```python
from string import ascii_lowercase
@@ -10,11 +10,19 @@ def is_pangram(sentence):
```
- This begins by importing all of the [ascii_lowercase][ascii-lowercase] letters.
-- It lowercases the input by using the [lower][lower] method.
+- It lowercases the input by using the [lower()][lower] method.
- It then checks if all letters in the lowercase alphabet are contained in the lowercased `sentence`,
-using the [`all`][all] function.
+using the [`all()`][all] function.
- If all of the letters in the alphabet are contained in the `sentence`, then the function will return `True`.
+```exercism/note
+Instead of `lower()`, the [`casefold`](https://docs.python.org/3/library/stdtypes.html#str.casefold)
+method could be used to lowercase the letters.
+`casefold()` differs from `lower()` in lowercasing certain Unicode characters.
+At the time of writing, those differences are not of concern to this exercise.
+Also, `casefold()` benched slower than `lower()`.
+```
+
[ascii-lowercase]: https://docs.python.org/3/library/string.html#string.ascii_lowercase
[lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower
[all]: https://docs.python.org/3/library/functions.html#all
From 39e93e082182c1f53f4acff3ea58d28ebb3bdae3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 08:12:56 -0600
Subject: [PATCH 183/932] Update content.md
---
exercises/practice/pangram/.approaches/set-issubset/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/pangram/.approaches/set-issubset/content.md b/exercises/practice/pangram/.approaches/set-issubset/content.md
index 1c43c637dd9..c87dd604ef0 100644
--- a/exercises/practice/pangram/.approaches/set-issubset/content.md
+++ b/exercises/practice/pangram/.approaches/set-issubset/content.md
@@ -14,7 +14,7 @@ def is_pangram(sentence):
In this approach a [set][set] is made from the [ascii_lowercase][ascii-lowercase] letters,
and another `set` is made from the [`lower`][lower]cased letters in the `sentence`.
-The function returns if the alphabet `set` [issubset][issubset] of the `sentence` `set`.
+The function returns if the alphabet `set` [issubset()][issubset] of the `sentence` `set`.
If all of the letters in the alphabet are a subset of the letters in the `sentence`,
then the function will return `True`.
From 2f8f1ec4952868a4ece001a62b5070a6cb8be370 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 08:14:55 -0600
Subject: [PATCH 184/932] Update introduction.md
---
.../practice/pangram/.approaches/introduction.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/pangram/.approaches/introduction.md b/exercises/practice/pangram/.approaches/introduction.md
index 9536fe63d59..fec919313ee 100644
--- a/exercises/practice/pangram/.approaches/introduction.md
+++ b/exercises/practice/pangram/.approaches/introduction.md
@@ -10,7 +10,7 @@ Or you can see if the `set` `len` of the lowercased `sentence` filtered to just
The key to solving Pangram is determining if all of the letters in the alphabet are in the `sentence` being tested.
The occurrence of either the letter `a` or the letter `A` would count as the same letter.
-## Approach: `all` on lowercased letters
+## Approach: `all()` on lowercased letters
```python
from string import ascii_lowercase
@@ -21,9 +21,9 @@ def is_pangram(sentence):
```
-For more information, check the [`all` approach][approach-all].
+For more information, check the [`all()` approach][approach-all].
-## Approach: `set` with `issubset` on lowercased characters
+## Approach: `set` with `issubset()` on lowercased characters
```python
from string import ascii_lowercase
@@ -36,9 +36,9 @@ def is_pangram(sentence):
```
-For more information, check the [`set` with `issubset` approach][approach-set-issubset].
+For more information, check the [`set` with `issubset()` approach][approach-set-issubset].
-## Approach: `set` with `len` on lowercased characters
+## Approach: `set` with `len()` on lowercased characters
```python
def is_pangram(sentence):
@@ -47,7 +47,7 @@ def is_pangram(sentence):
```
-For more information, check the [`set` with `len` approach][approach-set-len].
+For more information, check the [`set` with `len()` approach][approach-set-len].
## Other approaches
@@ -56,11 +56,11 @@ Besides the aforementioned, idiomatic approaches, you could also approach the ex
### Other approach: Bit field
Another approach can use a bit field to keep track of used letters.
-For more information, check the [Bit field approach][approach-bitfield].
+For more information, check the [bit field approach][approach-bitfield].
## Which approach to use?
-The fastest is the `set` `issubset` approach.
+The fastest is the `set` `issubset()` approach.
To compare performance of the approaches, check the [Performance article][article-performance].
From 6e307129c2322dbba556b7b256a82c9c643692f4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 17 Nov 2022 08:16:35 -0600
Subject: [PATCH 185/932] Update content.md
---
exercises/practice/pangram/.approaches/set-len/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/pangram/.approaches/set-len/content.md b/exercises/practice/pangram/.approaches/set-len/content.md
index 645a47674ae..e3f99fc756b 100644
--- a/exercises/practice/pangram/.approaches/set-len/content.md
+++ b/exercises/practice/pangram/.approaches/set-len/content.md
@@ -1,4 +1,4 @@
-# `set` with `len`
+# `set` with `len()`
```python
def is_pangram(sentence):
@@ -10,7 +10,7 @@ def is_pangram(sentence):
- This approach first makes a [set][set] from the [`lower`][lower]cased characters of the `sentence`.
- The characters in the `set`are then iterated in a [list comprehension][list-comprehension].
- The characters are filtered by an `if` [`isalpha()`][isalpha] statement, so that only alphabetic characters make it into the list.
-- The function returns if the [`len`][len] of the [`list`][list] is `26`.
+- The function returns if the [`len()`][len] of the [`list`][list] is `26`.
If the number of unique letters in the `set` is equal to the `26` letters in the alphabet, then the function will return `True`.
[lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower
From e84dd5abeac87616143e096a29a432ed5de238fd Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 06:55:09 -0600
Subject: [PATCH 186/932] Update introduction.md
---
exercises/practice/pangram/.approaches/introduction.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/pangram/.approaches/introduction.md b/exercises/practice/pangram/.approaches/introduction.md
index fec919313ee..cf5538c0158 100644
--- a/exercises/practice/pangram/.approaches/introduction.md
+++ b/exercises/practice/pangram/.approaches/introduction.md
@@ -1,9 +1,9 @@
# Introduction
There are various idomatic approaches to Pangram.
-You can use the `all` method on the `ascii_lowercase` letters with the lowercased letters of the `sentence`.
-You can see if the `set` of the alphabet `issubset` of a `set` of the lowercased `sentence`.
-Or you can see if the `set` `len` of the lowercased `sentence` filtered to just ASCII letters is `26`.
+You can use the `all()` method on the `ascii_lowercase` letters with the lowercased letters of the `sentence`.
+You can see if the `set` of the alphabet `issubset()` of a `set` of the lowercased `sentence`.
+Or you can see if the `set` `len()` of the lowercased `sentence` filtered to just ASCII letters is `26`.
## General guidance
From d98370b6772d0f5e3f45d18d3a70e26fc5725dda Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 06:57:08 -0600
Subject: [PATCH 187/932] Update content.md
---
exercises/practice/pangram/.approaches/set-len/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/pangram/.approaches/set-len/content.md b/exercises/practice/pangram/.approaches/set-len/content.md
index e3f99fc756b..b647a01d495 100644
--- a/exercises/practice/pangram/.approaches/set-len/content.md
+++ b/exercises/practice/pangram/.approaches/set-len/content.md
@@ -10,7 +10,7 @@ def is_pangram(sentence):
- This approach first makes a [set][set] from the [`lower`][lower]cased characters of the `sentence`.
- The characters in the `set`are then iterated in a [list comprehension][list-comprehension].
- The characters are filtered by an `if` [`isalpha()`][isalpha] statement, so that only alphabetic characters make it into the list.
-- The function returns if the [`len()`][len] of the [`list`][list] is `26`.
+- The function returns whether the [`len()`][len] of the [`list`][list] is `26`.
If the number of unique letters in the `set` is equal to the `26` letters in the alphabet, then the function will return `True`.
[lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower
From 10aef0230f2e7cab0fe557a0bfeae0339b7d7da4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 06:58:35 -0600
Subject: [PATCH 188/932] Update content.md
---
.../pangram/.articles/performance/content.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/pangram/.articles/performance/content.md b/exercises/practice/pangram/.articles/performance/content.md
index 4f05a4157e2..c5546e948ba 100644
--- a/exercises/practice/pangram/.articles/performance/content.md
+++ b/exercises/practice/pangram/.articles/performance/content.md
@@ -4,9 +4,9 @@ In this approach, we'll find out how to most efficiently determine if a string i
The [approaches page][approaches] lists three idiomatic approaches to this exercise:
-1. [Using `all` on lowercased letters][approach-all]
-2. [Using `set` with `issubset`][approach-set-issubset]
-3. [Using `set` with `len`][approach-set-len]
+1. [Using `all()` on lowercased letters][approach-all]
+2. [Using `set` with `issubset()`][approach-set-issubset]
+3. [Using `set` with `len()`][approach-set-len]
For our performance investigation, we'll also include a fourth approach that [uses a bit field to keep track of used letters][approach-bitfield].
@@ -22,11 +22,11 @@ len: 3.7158977999933994e-06
bit: 8.75982620002469e-06
```
-- The `set` `len` approach is not as fast as the `set` `issubset` approach.
-- The `all` approach is slower than either `set` approach.
+- The `set` `len()` approach is not as fast as the `set` `issubset()` approach.
+- The `all()` approach is slower than either `set` approach.
Using `casefold` was slower than using `lower`.
- Although the bit field approach may be faster in other languages, it is significantly slower in Python.
-It is faster than the `all` approach, but much slower than either `set` approach.
+It is faster than the `all()` approach, but much slower than either `set` approach.
[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/pangram/.articles/performance/code/Benchmark.py
[timeit]: https://docs.python.org/3/library/timeit.html
From b32fb92ad483bb3fcbf2708088fae25a4e97e471 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 29 Nov 2022 17:47:59 -0800
Subject: [PATCH 189/932] Update
exercises/practice/pangram/.approaches/set-issubset/content.md
---
exercises/practice/pangram/.approaches/set-issubset/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/pangram/.approaches/set-issubset/content.md b/exercises/practice/pangram/.approaches/set-issubset/content.md
index c87dd604ef0..80e31444f00 100644
--- a/exercises/practice/pangram/.approaches/set-issubset/content.md
+++ b/exercises/practice/pangram/.approaches/set-issubset/content.md
@@ -1,4 +1,4 @@
-# `HashSet` with `is_subset`
+# `set` with `is_subset`
```python
from string import ascii_lowercase
From 17d75187f8c0afa3bd4bac9125ed124683a261de Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 29 Nov 2022 17:12:58 -0800
Subject: [PATCH 190/932] Updated Contributing and readme for contribution
pause.
---
CONTRIBUTING.md | 264 ++++++++++++++++++++++--------------------------
README.md | 77 +++++++-------
2 files changed, 157 insertions(+), 184 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 816a44f6b95..691ca9f92fc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,68 +3,66 @@
Contributing
-
-
+ [](https://forum.exercism.org)
+ [](https://exercism.org)
+ [](https://exercism.org/blog/freeing-our-maintainers)
+ [](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
-Hi. 👋🏽 👋 **We are happy you are here.** 🎉🌟
-
-Thank you so much for your interest in contributing!
+
-**`exercsim/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.
+Hi. 👋🏽 👋 **We are happy you are here.** 🎉 🌟
- 🌟 Track exercises support Python `3.8`.
- 🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.9`.
+
+
+
+
-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.
+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.
+
+
-
+**`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.
-🌟🌟 If you have not already done so, please take a moment to read our [Code of Conduct][exercism-code-of-conduct]. 🌟🌟
-It might also be helpful to look at [Being a Good Community Member][being-a-good-community-member] & [The words that we use][the-words-that-we-use], and [Pull Requests][prs].
+🌟 Track exercises support Python `3.7` - `3.10.6`.
+Exceptions to this support are noted where they occur.
+ 🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.10.6`.
-Some defined roles in our community: [Contributors][exercism-contributors] **|** [Mentors][exercism-mentors] **|** [Maintainers][exercism-track-maintainers] **|** [Admins][exercism-admins]
-
-
-
-
-✨ 🦄 _**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] (_ ✨ versions available in [contributing][website-contributing-section] on [exercism(dot)org][exercism-website]._)
+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.
## 🐛 **Did you find a bug?**
-It is not uncommon to discover typos, confusing directions, or incorrect implementations of certain tests or code examples. Or you might have a great suggestion for a hint to aid students ( 💙 ), see optimizations for exemplar or test code, find missing test cases to add, or want to correct factual and/or logical errors. Or maybe you have a great idea for an exercise or feature (❗ ).
+It is not uncommon to discover typos, confusing directions, or incorrect implementations of certain tests or code examples. Or you might have a great suggestion for a hint to aid students ( 💙 ), see optimizations for exemplar or test code, find missing test cases to add, or want to correct factual and/or logical errors. Or maybe you have a great idea 💡 for an exercise or feature ( 💙 ).
_Our track is always a work in progress!_ 🌟🌟
-Please 📛 [ Open an issue ][open-an-issue]📛 , and let us know what you have found/suggest.
+While contributions are paused, we ask that you [`open a thread in our community forum`](https://forum.exercism.org) to let us know what you have found/suggest.
-## 🚧 **Did you write a patch that fixes a bug?**
-_Before you get started, please review [Pull Requests][prs]._
-
+## 🚧 **Did you write a patch that fixes a bug?**
- 💛 💙 **We Warmly Welcome Pull Requests that are:**
+**`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.
- 1️⃣ Small, contained fixes for typos/grammar/punctuation/code syntax on [one] exercise,
- 2️⃣ Medium changes that have been agreed/discussed via a filed issue,
- 3️⃣ Contributions from our [help wanted][help-wanted] issue list,
- 4️⃣ Larger (_and previously agreed-upon_) contributions from recent & regular (_within the last 6 months_) contributors.
+Once the pause ends, we will **happily** consider your PR.
+Until that time, all PRs from the larger community will be **automatically closed** with a note.
-When in doubt, 📛 [ Open an issue ][open-an-issue]📛 . We will happily discuss your proposed change.
-🐍 _But we should talk before you take a whole lot of time or energy implementing anything._
+We're leaving the general contributing docs below for our long-term collaborators and maintainers.
-
-In General
+
+In General
-- Please make sure to have a quick read-through of our Exercism [Pull Requests][prs] document before jumping in. 😅
- Maintainers are happy to review your work and help troubleshoot with you. 💛 💙
- Requests are reviewed as soon as is practical/possible.
- (❗ ) Reviewers may be in a different timezone ⌚ , or tied up 🧶 with other tasks.
@@ -78,62 +76,47 @@ When in doubt, 📛 [ Open an issue ][open-an-issue]📛 . We wil
- creating a Pull Request making significant or breaking changes.
- for changes across multiple exercises, even if they are typos or small.
- anything that is going to require doing a lot of work (_on your part or the maintainers part_).
-- Follow coding standards found in [PEP8][PEP8] (["For Humans" version here][pep8-for-humans]).
-- All files should have a proper [EOL][EOL]. This means one carriage return at the end of the final line of text files.
+- Follow coding standards found in [PEP8][pep8] (["For Humans" version here][pep8-for-humans]).
+- All files should have a proper [EOL][eol]. This means one carriage return at the end of the final line of text files.
- Otherwise, watch out ⚠️ for trailing spaces, extra blank lines, extra spaces, and spaces in blank lines.
-- Continuous Integration is going to run **a lot** of checks. Try to understand & fix any failures.
+- Continuous Integration is going to run **a lot** of checks. Pay attention to failures & try to understand and fix them.
+
⚠️ Pre-Commit Checklist ⚠️
-
-
-
- - [ ] Update & rebase your branch with any (recent) upstream changes.
- - [ ] Spell and grammar check all prose changes.
- - [ ] Run [Prettier](https://prettier.io/) on all markdown and JSON files.
- - (_Optionally_) run [yapf](https://github.com/google/yapf) ([_yapf config_](https://github.com/exercism/python/blob/main/.style.yapf)) to help format your code.
- - [ ] Run [flake8](http://flake8.pycqa.org/) with [_flake8 config_](https://github.com/exercism/python/blob/main/.flake8) to check general code style standards.
- - [ ] Run [pylint](https://pylint.pycqa.org/en/v2.11.1/user_guide/index.html) with [_pylint config_](https://github.com/exercism/python/blob/main/pylintrc) to check extended code style standards.
- - [ ] Use pytest or the [python-track-test-runner](https://github.com/exercism/python-test-runner) to test any changed `example.py`/`exemplar.py`files
- against their associated test files.
- - [ ] Similarly, use [pytest](https://docs.pytest.org/en/6.2.x/contents.html) or
- the [python-track-test-runner](https://github.com/exercism/python-test-runner) to test any changed _**test**_ files.
- - Check that tests **fail** properly, as well as succeed.
- (_**e.g.**, make some tests fail on purpose to "test the tests" & failure messages_).
- - [ ] Double-check all files for proper EOL.
- - [ ] [Regenerate](https://github.com/exercism/python/blob/main/CONTRIBUTING.md#generating-practice-exercise-documents) exercise documents when you modified or created a `hints.md` file for a practice exercise.
- - [ ] [Regenerate the test file](https://github.com/exercism/python/blob/main/CONTRIBUTING.md#auto-generated-test-files-and-test-templates) if you modified or created a `JinJa2` template file for a practice exercise.
- - Run the generated test file result against its `example.py`.
- - [ ] Run [`configlet-lint`](https://github.com/exercism/configlet#configlet-lint) if the track [config.json](https://github.com/exercism/docs/blob/main/building/tracks/config-json.md), or any other exercise `config.json` has been modified.
-
- |
-
-
+1. Run [`configlet-lint`][configlet-lint] if the track [config.json](config-json) has been modified.
+2. Run [Prettier][prettier] on all markdown files.
+3. (_Optionally_) run [yapf][yapf] ([_config file_][.style.yapf]) to help format your code, and give you a head start on making the linters happy.
+4. Run [flake8][flake8] ([_config file_][.flake8]) & [pylint][pylint] ([_config file_][pylintrc]) to ensure all Python code files conform to general code style standards.
+5. Run `test/check-exercises.py [EXERCISE]` to check if your test changes function correctly.
+6. Run the `example.py` or `exemplar.py` file against the exercise test file to ensure that it passes without error.
+7. If you modified or created a `hints.md` file for a practice exercise, [regenerate](#generating-practice-exercise-documents) it.
+
+
-
-Prose Writing Style and Standards
+
+
+Prose Writing Style & Standards
-Non-code content (_exercise introductions & instructions, hints, concept write-ups, documentation etc._) should be written in [American English][american-english].
-We strive to watch [the words we use][the-words-that-we-use].
+Non-code content (_exercise introductions & instructions, hints, concept write-ups, documentation etc._) should be written in [American English][american-english]. We strive to watch [the words we use][the-words-that-we-use].
-When a word or phrase usage is contested/ambiguous, we default to what is best understood by our international community of learners, even if it "sounds a little weird" to a "native" American English speaker.
+When a word or phrase usage is contested | ambiguous, we default to what is best understood by our international community of learners, even if it "sounds a little weird" to a "native" American English speaker.
Our documents use [Markdown][markdown-language], with certain [alterations][exercism-markdown-widgets] & [additions][exercism-internal-linking]. Here is our full [Markdown Specification][exercism-markdown-specification]. 📐 We format/lint our Markdown with [Prettier][prettier]. ✨
-
-Coding Standards
+Coding Standards
-1. We follow [PEP8][PEP8] (["For Humans" version here][pep8-for-humans]).
+1. We follow [PEP8][pep8] (["For Humans" version here][pep8-for-humans]).
In particular, we (mostly) follow the [Google flavor][google-coding-style] of PEP8.
2. We use [flake8][flake8] to help us format Python code nicely.
Our `flake8` config file is [.flake8][.flake8] in the top level of this repo.
@@ -153,7 +136,7 @@ Our documents use [Markdown][markdown-language], with certain [alterations][exer
- **120 character per line limit** (_as opposed to the default limit of 79_)
- Variable, function, and method names should be `lower_case_with_underscores` (_aka "snake case"_)
- Classes should be named in `TitleCase` (_aka "camel case"_)
-- **No single letter variable names** outside of a `lambda`. This includes loop variables and comprehensions.
+- **No single letter variable names** outside of a `lambda`. This includes loop variables and comprehensions.
- Refrain from putting `list`, `tuple`, `set`, or `dict` members on their own lines.
Fit as many data members as can be easily read on one line, before wrapping to a second.
- If a data structure spreads to more than one line and a break (_for clarity_) is needed, prefer breaking after the opening bracket.
@@ -162,20 +145,20 @@ Our documents use [Markdown][markdown-language], with certain [alterations][exer
- Use **`"""`** for docstrings.
- Prefer [implicit line joining][implicit-line-joining] for long strings.
- Prefer enclosing imports in **`()`**, and putting each on their own line when importing multiple methods.
-- Two lines between `Classes`, one line between `functions`. Other vertical whitespace as needed to help readability.
+- Two lines between `Classes`, one line between `functions`. Other vertical whitespace as needed to help readability.
- Always use an **`EOL`** to end a file.
- Test File Style (concept exercises)
+ Test File Style (concept exercises)
- [Unittest.TestCase][unittest] syntax, with [PyTest][pytest] as a test runner.
- We are transitioning to using more PyTest features/syntax, but are leaving `Unittest` syntax in place where possible.
- Always check with a maintainer before introducing a PyTest feature into your tests.
- Test **Classes** should be titled `Test`. **e.g.** `class CardGamesTest(unittest.TestCase):`
-- Test method names should begin with `test_`. Try to make test case names descriptive but not too long.
+- Test method names should begin with `test_`. Try to make test case names descriptive but not too long.
- Favor [_parameterizing_][distinguishing-test-iterations] tests that only vary input data. Use [unittest.TestCase.subTest][subtest] for parameterization.
- An [example from Guido's Gorgeous Lasagna][guidos-gorgeous-lasagna-testfile].
- A second [example from Card Games][card-games-testfile].
@@ -184,20 +167,19 @@ Our documents use [Markdown][markdown-language], with certain [alterations][exer
- Use [`enumerate()`][enumerate] where possible when indexes are needed. See [Card Games][card-games-testfile] for example usage.
- Favor using names like `inputs`, `data`, `input_data`, `test_data`, or `test_case_data` for test inputs.
- Favor using names like `results`, `expected`, `result_data`, `expected_data`, or `expected_results` for test outcomes.
-- Favor putting the assert failure message outside of `self.assert()`. Name it `failure_msg`. See [Card Games][card-games-testfile] for example usage.
-- Favor `f-strings` for dynamic failure messages. Please make your error messages as relevant and human-readable as possible.
+- Favor putting the assert failure message outside of `self.assert()`. Name it `failure_msg`. See [Card Games][card-games-testfile] for example usage.
+- Favor `f-strings` for dynamic failure messages. Please make your error messages as relevant and human-readable as possible.
- We relate test cases to **task number** via a custom [PyTest Marker][pytestmark].
- These take the form of `@pytest.mark.task(taskno=)`. See [Guido's Gorgeous Lasagna][guidos-gorgeous-lasagna-testfile] for an example.
-- We prefer **test data files** when test inputs/outputs are verbose.
+- We prefer **test data files** when test inputs/ouputs are verbose.
- These should be named with `_data` or `_test_data` at the end of the filename, and saved alongside the test case file.
- See the [Cater-Waiter][cater-waiter] exercise directory for an example of this setup.
- **Test data files** need to be added under an `editor` key within [`config.json "files"`][exercise-config-json].
- Check with a maintainer if you have questions or issues, or need help with an exercise `config.json`.
-- For new test files going forward, omit `if __name__ == "__main__":
- unittest.main()`.
+- For new test files going forward, omit `if __name__ == "__main__": unittest.main()`.
- Lint with both `flake8` and `pylint`.
- Both linters are known to toss false-positives for some testing patterns.
- - Where necessary, deploy the [`#noqa`][flake8-noqa] or [`#pylint disable=`][pylint-disable-check] comments to suppress false-positive warnings. - See **line 16** of [Guido's Gorgeous Lasagna][guidos-gorgeous-lasagna-testfile] test file for an example of an override.
+ - Where necessary, deploy the [`#noqa`][flake8-noqa] or [`#pylint disable=`][pylint-disable-check] comments to suppress false-positive warnings. - See **line 16** of [Guido's Gorgeous Lasagna][guidos-gorgeous-lasagna-testfile] test file for an example of an override.
@@ -205,30 +187,28 @@ Our documents use [Markdown][markdown-language], with certain [alterations][exer
If you have any questions or issues, don't hesitate to ask the maintainers -- they're always happy to help 💛 💙
Some of our code is old and does not (yet) conform to all these standards.
-_**We know it, and trust us, we are working on fixing it.**_ But if you see 👀 something, 👄 say something. It will motivate us to fix it! 🌈
+_We know it, and trust us, we are working on fixing it._ But if you see 👀 something, 👄 say something. It'll motivate us to fix it! 🌈
-Python Versions
+Language Versions
-This track officially supports Python = `3.8`
-The track `test runner`, `analyzer`, and `representer` run in docker on `python:3.9-slim`.
+This track officially supports Python `3.7 - 3.10.6` for students completing exercises.
+The track `test runner`, `analyzer`, and `representer` run in docker on `python:3.10.6-slim`.
-- All exercises should be written for compatibility with Python = `3.8` or `3.9`.
-- Version backward _incompatibility_ (*e.g* an exercise using a `3.8` or `3.9` **only** feature) should be clearly noted in any exercise hits, links, introductions or other notes.
+Although the majority of test cases are written using `unittest.TestCase`,
-- Here is an example of how the Python documentation handles [version-tagged 🏷 ][version-tagged-language-features] feature introduction.
+- All exercises should be written for compatibility with Python `3.7` - `3.10.6`.
+- Version backward _incompatibility_ (_e.g_ an exercise using features introduced in `3.8`, `3.9`, or `3.10`) should be clearly noted in any exercise hints, links, introductions or other notes.
-- _Most_ exercises will work with Python `3.6+`, and _many_ are compatible with Python `2.7+`.
- - Please do not change existing exercises to add new language features without consulting with a maintainer first.
- - We 💛 💙 modern Python, but we _also_ want to avoid student confusion when it comes to which Python versions support brand-new features.
+- Here is an example of how the Python documentation handles [version-tagged 🏷 ][version-tagged-language-features] feature introduction.
-- All test suites and example solutions must work in all Python versions that we currently support. When in doubt about a feature, please check with maintainers.
+- _Most_ exercises will work with Python `3.6+`, and _many_ are compatible with Python `2.7+`. Please do not change existing exercises to add new language features without consulting with a maintainer first. We 💛 💙 modern Python, but we _also_ want to avoid student confusion when it comes to which Python versions support brand-new features.
-
+* All test suites and example solutions must work in all Python versions that we currently support. When in doubt about a feature, please check with maintainers.
@@ -237,22 +217,19 @@ The track `test runner`, `analyzer`, and `representer` run in docker on `python:
-- Each exercise must be self-contained. Please do not use or reference files that reside outside the given exercise directory. "Outside" files will not be included if a student fetches the exercise via the Command line Interface.
+- Each exercise must be self-contained. Please do not use or reference files that reside outside the given exercise directory. "Outside" files will not be included if a student fetches the exercise via the CLI.
+
+- Each exercise/problem should include a complete test suite, an example/exemplar solution, and a stub file ready for student implementation.
-- Each exercise/problem should include
- - a complete test suite,
- - an example/exemplar solution,
- - a stub file ready for student implementation.
+- For specifications, refer to [Concept Exercise Anatomy][concept-exercise-anatomy], or [Practice Exercise Anatomy][practice-exercise-anatomy] depending on which type of exercise you are contributing to.
-- For specifications, refer to the links below, depending on which type of exercise you are contributing to.
- - [Concept Exercise Anatomy][concept-exercise-anatomy]
- - [Practice Exercise Anatomy][practice-exercise-anatomy]
+- **Practice exercise**, descriptions and instructions come from a centralized, cross-track [problem specifications][problem-specifications] repository.
-- **Practice exercise**, descriptions and instructions come from a centralized, cross-track [problem specifications][problem-specifications] repository.
- Any updates or changes need to be proposed/approved in `problem-specifications` first.
- - If Python-specific changes become necessary, they need to be appended to the canonical instructions by creating a `instructions.append.md` file in this (`exercism/Python`) repository.
+ - If Python-specific changes become necessary, they need to be appended to the canonical instructions by creating a `instructions.append.md` file in this (`exercism/Python`) repository.
- Practice Exercise **Test Suits** for many practice exercises are similarly [auto-generated](#auto-generated-files) from data in [problem specifications][problem-specifications].
+
- Any changes to them need to be proposed/discussed in the `problem-specifications` repository and approved by **3 track maintainers**, since changes could potentially affect many (_or all_) exercism language tracks.
- If Python-specific test changes become necessary, they can be appended to the exercise `tests.toml` file.
- 📛 [ **Please file an issue**][open-an-issue] 📛 and check with maintainers before adding any Python-specific tests.
@@ -265,18 +242,18 @@ The track `test runner`, `analyzer`, and `representer` run in docker on `python:
- - [ ] `.docs/hints.md`
- - [ ] `.docs/instructions.md`
- - [ ] `.docs/introduction.md`
- - [ ] `.meta/config.json`
- - [ ] `.meta/design.md`
- - [ ] `.meta/exemplar.py` (_exemplar solution_)
- - [ ] `_test.py` (_test file_)
- - [ ] `.py` (_stub file_)
- - [ ] `concepts/../introduction.md`
- - [ ] `concepts/../about.md`
- - [ ] `concepts/../links.json`
- - [ ] `concepts/../.meta/config.json`
+ - [ ] `.docs/hints.md`
+ - [ ] `.docs/instructions.md`
+ - [ ] `.docs/introduction.md`
+ - [ ] `.meta/config.json`
+ - [ ] `.meta/design.md`
+ - [ ] `.meta/exemplar.py` (_exemplar solution_)
+ - [ ] `_test.py` (_test file_)
+ - [ ] `.py` (_stub file_)
+ - [ ] `concepts/../introduction.md`
+ - [ ] `concepts/../about.md`
+ - [ ] `concepts/../links.json`
+ - [ ] `concepts/../.meta/config.json`
@@ -286,50 +263,46 @@ The track `test runner`, `analyzer`, and `representer` run in docker on `python:
- - [ ] `.docs/instructions.md`(**required**)
- - [ ] `.docs/introduction.md`(_optional_)
- - [ ] `.docs/introduction.append.md`(_optional_)
- - [ ] `.docs/instructions.append.md` (_optional_)
- - [ ] `.docs/hints.md`(_optional_)
- - [ ] `.meta/config.json` (**required**)
- - [ ] `.meta/example.py` (**required**)
- - [ ] `.meta/design.md` (_optional_)
- - [ ] `.meta/template.j2` (_template for generating tests from canonical data_)
- - [ ] `.meta/tests.toml` (_tests configuration from canonical data_)
- - [ ] `_test.py` (_**auto-generated from canonical data**_)
- - [ ] `.py` (**required**)
+ - [ ] `.docs/instructions.md`(**required**)
+ - [ ] `.docs/introduction.md`(_optional_)
+ - [ ] `.docs/introduction.append.md`(_optional_)
+ - [ ] `.docs/instructions.append.md` (_optional_)
+ - [ ] `.docs/hints.md`(_optional_)
+ - [ ] `.meta/config.json` (**required**)
+ - [ ] `.meta/example.py` (**required**)
+ - [ ] `.meta/design.md` (_optional_)
+ - [ ] `.meta/template.j2` (_template for generating tests from cannonical data_)
+ - [ ] `.meta/tests.toml` (_tests configuration from cannonical data_)
+ - [ ] `_test.py` (_**auto-generated from cannonical data**_)
+ - [ ] `.py` (**required**)
-
-
-
-
External Libraries and Dependencies
-Our tooling (_runners, representers, and analyzers_) runs in isolated containers within the exercism website. Because of this isolation, exercises cannot rely on third-party or external libraries. Any library needed for an exercise or exercise tests must be incorporated as part of a tooling build, and noted for students who are using the CLI to solve problems locally.
+Our tooling (_runners, analyzers and representers_) runs in isolated containers within the exercism website. Because of this, **exercises cannot rely on third-party or external libraries.** Any library needed for an exercise or exercise tests must be incorporated as part of the tooling build, and noted for students who are using the CLI to solve problems locally.
-If your exercise depends on a third-party library (_aka not part of standard Python_), please consult with maintainers about it. We may or may not be able to accommodate the package.
+If your exercise depends on a third-party library (_aka not part of standard Python_), please consult with maintainers about it. We may or may not be able to accommodate the package.
-
+
Auto-Generated Test Files and Test Templates
-[**Practice exercises**][practice-exercise-files] inherit their definitions from the [problem-specifications][problem-specifications] repository in the form of _description files_. Exercise introductions, instructions and (_in the case of **many**, but not **all**_) test files are then machine-generated for each language track.
+[**Practice exercises**][practice-exercise-files] inherit their definitions from the [problem-specifications][problem-specifications] repository in the form of _description files_. Exercise introductions, instructions and (_in the case of **many**, but not **all**_) test files are then machine-generated for each language track.
-Changes to practice exercise _specifications_ should be raised/PR'd in [problem-specifications][problem-specifications] and approved by **3 track maintainers**. After an exercise change has gone through that process , related documents and tests for the Python track will need to be [re-generated](#generating-practice-exercise-documents) via [configlet][configlet]. Configlet is also used as part of the track CI, essential track and exercise linting, and other verification tasks.
+Changes to practice exercise _specifications_ should be raised/PR'd in [problem-specifications][problem-specifications] and approved by **3 track maintainers**. After an exercise change has gone through that process, related documents and tests for the Python track will need to be [re-generated](#generating-practice-exercise-documents) via [configlet][configlet]. Configlet is also used as part of the track CI, essential track and exercise linting, and other verification tasks.
-If a practice exercise has an auto-generated `_test.py` file, there will be a `.meta/template.j2` and a `.meta/tests.toml` file in the exercise directory. If an exercise implements Python track-specific tests, there may be a `.meta/additional_tests.json` to define them. These `additional_tests.json` files will automatically be included in test generation.
+If a practice exercise has an auto-generated `_test.py` file, there will be a `.meta/template.j2` and a `.meta/tests.toml` file in the exercise directory. If an exercise implements Python track-specific tests, there may be a `.meta/additional_tests.json` to define them. These `additional_tests.json` files will automatically be included in test generation.
_Exercise Structure with Auto-Generated Test Files_
-```Graphql
+```Bash
[/
├── .docs
│ └── instructions.md
@@ -341,9 +314,10 @@ _Exercise Structure with Auto-Generated Test Files_
├── .py #stub file
└──
-Practice exercise `_test.py` files are generated/regenerated via the [Python Track Test Generator][python-track-test-generator].
+Practice exercise `_test.py` files are generated/regenerated via the [Python Track Test Generator][python-track-test-generator].
Please reach out to a maintainer if you need any help with the process.
@@ -361,6 +335,7 @@ If an unimplemented exercise does not have a `canonical-data.json` file, the tes
**Example solution files serve two purposes only:**
+
1. Verification of the tests
2. Example implementation for mentor/student reference
@@ -376,7 +351,7 @@ Implementing Track-specific Practice Exercises is similar to implementing a `can
-
Generating Practice Exercise Documents
+
Generating Practice Exercise Documents
You will need
@@ -388,6 +363,7 @@ Implementing Track-specific Practice Exercises is similar to implementing a `can
```bash
configlet generate --spec-path path/to/problem/specifications --only example-exercise
```
+
For all Practice Exercises

```bash
@@ -396,12 +372,8 @@ configlet generate --spec-path path/to/problem/specifications
-
-
[.flake8]: https://github.com/exercism/python/blob/main/.flake8
[.style.yapf]: https://github.com/exercism/python/blob/main/.style.yapf
-[EOL]: https://en.wikipedia.org/wiki/Newline
-[PEP8]: https://www.python.org/dev/peps/pep-0008/
[american-english]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
[being-a-good-community-member]: https://github.com/exercism/docs/tree/main/community/good-member
[card-games-testfile]: https://github.com/exercism/python/blob/main/exercises/concept/card-games/lists_test.py
@@ -409,10 +381,12 @@ configlet generate --spec-path path/to/problem/specifications
[concept-exercise-anatomy]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md
[concept-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md
[config-json]: https://github.com/exercism/javascript/blob/main/config.json
+[configlet-general]: https://github.com/exercism/configlet
[configlet-lint]: https://github.com/exercism/configlet#configlet-lint
[configlet]: https://github.com/exercism/docs/blob/main/building/configlet/generating-documents.md
[distinguishing-test-iterations]: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
[enumerate]: https://docs.python.org/3/library/functions.html#enumerate
+[eol]: https://en.wikipedia.org/wiki/Newline
[exercise-config-json]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md#full-example
[exercise-presentation]: https://github.com/exercism/docs/blob/main/building/tracks/presentation.md
[exercism-admins]: https://github.com/exercism/docs/blob/main/community/administrators.md
@@ -437,12 +411,12 @@ configlet generate --spec-path path/to/problem/specifications
[markdown-language]: https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf
[open-an-issue]: https://github.com/exercism/python/issues/new/choose
[pep8-for-humans]: https://pep8.org/
+[pep8]: https://www.python.org/dev/peps/pep-0008/
[practice-exercise-anatomy]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md
[practice-exercise-files]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md#exercise-files
[practice-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md
[prettier]: https://prettier.io/
[problem-specifications]: https://github.com/exercism/problem-specifications
-[prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md
[pylint-disable-check]: https://pylint.pycqa.org/en/latest/user_guide/message-control.html#block-disables
[pylint]: https://pylint.pycqa.org/en/v2.11.1/user_guide/index.html
[pylintrc]: https://github.com/exercism/python/blob/main/pylintrc
diff --git a/README.md b/README.md
index 879557ac612..64930816362 100644
--- a/README.md
+++ b/README.md
@@ -3,66 +3,68 @@
Exercism Python Track
-
-
-
-[](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
+ [](https://forum.exercism.org)
+ [](https://exercism.org)
+ [](https://exercism.org/blog/freeing-our-maintainers)
+ [](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
Hi. 👋🏽 👋 **We are happy you are here.** 🎉 🌟
+
+
**`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.
+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.8`.
- 🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.9`.
+🌟 Track exercises support Python `3.7` - `3.10.6`.
+Exceptions to this support are noted where they occur.
+ 🌟 Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.10.6`.
-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.
-
+
+
+
+
+
+
+
-
+🌟🌟 Please take a moment to read our [Code of Conduct][exercism-code-of-conduct] 🌟🌟
+It might also be helpful to look at [Being a Good Community Member][being-a-good-community-member] & [The words that we use][the-words-that-we-use].
-🌟🌟 Please take a moment to read our [Code of Conduct][exercism-code-of-conduct]. 🌟🌟
-It might also be helpful to look at [Being a Good Community Member][being-a-good-community-member], [The words that we use][the-words-that-we-use], and [Pull Requests][prs].
+ Some defined roles in our community: [Contributors][exercism-contributors] **|** [Mentors][exercism-mentors] **|** [Maintainers][exercism-track-maintainers] **|** [Admins][exercism-admins]
-Some defined roles in our community: [Contributors][exercism-contributors] **|** [Mentors][exercism-mentors] **|** [Maintainers][exercism-track-maintainers] **|** [Admins][exercism-admins]
+
-
+
-We 💛 💙 Pull Requests. **But our maintainers generally can't accept _unsolicited_ PRs.**
-Check our [help wanted][open-issues] list or [open an issue ][open-an-issue] for discussion first.
-We ✨💙 💛 💙 ✨ [PRs][prs] that follow our **[Contributing Guidelines][contributing-guidelines]**.
+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.
+
+
+
+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].
+_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_)
-
-
-
-If you are here to help out with [open issues][open-issues], you have our gratitude 🙌 🙌🏽.
-Anything with [`help wanted`] and without a [`Claimed`] tag is up for grabs.
-Comment on the issue and we will reserve it for you. 🌈 ✨
-
-
-
-
-Here to suggest a new feature or new exercise?? **Hooray!** 🎉
-Please keep in mind [Chesterton's Fence][chestertons-fence].
-_Thoughtful suggestions will likely result faster & more enthusiastic responses from maintainers._
-
-
+
## Python Software and Documentation
-**Copyright © 2001-2021 Python Software Foundation. All rights reserved.**
+**Copyright © 2001-2022 Python Software Foundation. All rights reserved.**
Python software and documentation are licensed under the [PSF License Agreement][psf-license].
@@ -70,17 +72,16 @@ Starting with `Python 3.8.6`, examples, recipes, and other code in the Python do
Some software incorporated into Python is under different licenses. The licenses are listed with code falling under that license. See [Licenses and Acknowledgements for Incorporated Software](https://docs.python.org/3/license.html#otherlicenses) for an incomplete list of these licenses.
+
## Exercism Python Track License
-This repository uses the [MIT License](/LICENSE).
-
+This repository uses the [MIT License](/LICENSE).
[being-a-good-community-member]: https://github.com/exercism/docs/tree/main/community/good-member
[chestertons-fence]: https://github.com/exercism/docs/blob/main/community/good-member/chestertons-fence.md
[concept-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md
[config-json]: https://github.com/exercism/python/blob/main/config.json
-[contributing-guidelines]: https://github.com/exercism/python/blob/main/CONTRIBUTING.md
[exercise-presentation]: https://github.com/exercism/docs/blob/main/building/tracks/presentation.md
[exercism-admins]: https://github.com/exercism/docs/blob/main/community/administrators.md
[exercism-code-of-conduct]: https://exercism.org/docs/using/legal/code-of-conduct
@@ -93,10 +94,8 @@ 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
-[open-an-issue]: https://github.com/exercism/python/issues/new/choose
-[open-issues]: https://github.com/exercism/python/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
-[prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md
[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
[the-words-that-we-use]: https://github.com/exercism/docs/blob/main/community/good-member/words.md
From 6ff41f24ad097be9f3dfd4e7277201f02dd5585b Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Wed, 30 Nov 2022 12:48:08 +0100
Subject: [PATCH 191/932] Update autoresponder for pausing community
contributions
We realized belatedly that we should create a shared, re-usable workflow.
This simplifies the workflow logic using a variable for the forum category
that the new topic gets created in, if they click the link to copy their post
from here to the forum.
---
.../pause-community-contributions.yml | 55 ++++---------------
1 file changed, 10 insertions(+), 45 deletions(-)
diff --git a/.github/workflows/pause-community-contributions.yml b/.github/workflows/pause-community-contributions.yml
index 16b1ca555d9..e91c86cf68e 100644
--- a/.github/workflows/pause-community-contributions.yml
+++ b/.github/workflows/pause-community-contributions.yml
@@ -8,52 +8,17 @@ on:
types:
- opened
paths-ignore:
- - "exercises/*/*/.approaches/**"
- - "exercises/*/*/.articles/**"
+ - 'exercises/*/*/.approaches/**'
+ - 'exercises/*/*/.articles/**'
workflow_dispatch:
+permissions:
+ issues: write
+ pull-requests: write
+
jobs:
pause:
- name: Pause Community Contributions
- runs-on: ubuntu-22.04
- steps:
- - name: Detect if user is org member
- uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
- id: is-organization-member
- with:
- script: |
- if (context.actor == 'dependabot' || context.actor == 'exercism-bot' || context.actor == 'github-actions[bot]') {
- return true;
- }
-
- return github.rest.orgs.checkMembershipForUser({
- org: context.repo.owner,
- username: context.actor,
- }).then(response => response.status == 204)
- .catch(err => false);
- - name: Comment
- if: steps.is-organization-member.outputs.result == 'false'
- uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
- with:
- script: |
- const isIssue = !!context.payload.issue;
- const subject = context.payload.issue || context.payload.pull_request;
- const thing = (isIssue ? 'issue' : 'PR');
- const aThing = (isIssue ? 'an issue' : 'a PR');
- github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: `Hello. Thanks for opening ${aThing} on Exercism. We are currently in a phase of our journey where we have paused community contributions to allow us to take a breather and redesign our community model. You can learn more in [this blog post](https://exercism.org/blog/freeing-our-maintainers). **As such, all issues and PRs in this repository are being automatically closed.**\n\nThat doesn’t mean we’re not interested in your ideas, or that if you’re stuck on something we don’t want to help. The best place to discuss things is with our community on the Exercism Community Forum. You can use [this link](https://forum.exercism.org/new-topic?title=${encodeURI(subject.title)}&body=${encodeURI(subject.body)}&category=python) to copy this into a new topic there.\n\n---\n\n_Note: If this ${thing} has been pre-approved, please link back to this ${thing} on the forum thread and a maintainer or staff member will reopen it._\n`
- })
- - name: Close
- if: steps.is-organization-member.outputs.result == 'false'
- uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
- with:
- script: |
- github.rest.issues.update({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: context.issue.number,
- state: "closed",
- })
+ if: github.repository_owner == 'exercism' # Stops this job from running on forks
+ uses: exercism/github-actions/.github/workflows/community-contributions.yml@main
+ with:
+ forum_category: python
From b46d0dfef65ccfd6988fc05115aa6f351d32c3b9 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Thu, 1 Dec 2022 23:33:08 +0100
Subject: [PATCH 192/932] Marked new_tests as excluded
---
.../armstrong-numbers/.meta/tests.toml | 37 +++++++++++++------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/exercises/practice/armstrong-numbers/.meta/tests.toml b/exercises/practice/armstrong-numbers/.meta/tests.toml
index fdada6d1ef8..b956bdfd475 100644
--- a/exercises/practice/armstrong-numbers/.meta/tests.toml
+++ b/exercises/practice/armstrong-numbers/.meta/tests.toml
@@ -1,30 +1,45 @@
-# 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.
[c1ed103c-258d-45b2-be73-d8c6d9580c7b]
description = "Zero is an Armstrong number"
[579e8f03-9659-4b85-a1a2-d64350f6b17a]
-description = "Single digit numbers are Armstrong numbers"
+description = "Single-digit numbers are Armstrong numbers"
[2d6db9dc-5bf8-4976-a90b-b2c2b9feba60]
-description = "There are no 2 digit Armstrong numbers"
+description = "There are no two-digit Armstrong numbers"
[509c087f-e327-4113-a7d2-26a4e9d18283]
-description = "Three digit number that is an Armstrong number"
+description = "Three-digit number that is an Armstrong number"
[7154547d-c2ce-468d-b214-4cb953b870cf]
-description = "Three digit number that is not an Armstrong number"
+description = "Three-digit number that is not an Armstrong number"
[6bac5b7b-42e9-4ecb-a8b0-4832229aa103]
-description = "Four digit number that is an Armstrong number"
+description = "Four-digit number that is an Armstrong number"
[eed4b331-af80-45b5-a80b-19c9ea444b2e]
-description = "Four digit number that is not an Armstrong number"
+description = "Four-digit number that is not an Armstrong number"
[f971ced7-8d68-4758-aea1-d4194900b864]
-description = "Seven digit number that is an Armstrong number"
+description = "Seven-digit number that is an Armstrong number"
[7ee45d52-5d35-4fbd-b6f1-5c8cd8a67f18]
-description = "Seven digit number that is not an Armstrong number"
+description = "Seven-digit number that is not an Armstrong number"
+
+[5ee2fdf8-334e-4a46-bb8d-e5c19c02c148]
+description = "Armstrong number containing seven zeroes"
+include = false
+
+[12ffbf10-307a-434e-b4ad-c925680e1dd4]
+description = "The largest and last Armstrong number"
+include = false
From 805c1c07359f317daec973d857eb67add9096f1a Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 05:54:06 -0600
Subject: [PATCH 193/932] Create config.json
---
.../practice/isogram/.approaches/config.json | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/config.json
diff --git a/exercises/practice/isogram/.approaches/config.json b/exercises/practice/isogram/.approaches/config.json
new file mode 100644
index 00000000000..daf98835d0a
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/config.json
@@ -0,0 +1,42 @@
+{
+ "introduction": {
+ "authors": ["bobahop"]
+ },
+ "approaches": [
+ {
+ "uuid": "5f06953f-6002-437d-a9a4-6aa5c7ebf247",
+ "slug": "scrub-comprehension",
+ "title": "Scrub with a list comprehension",
+ "blurb": "Use a list comprehension and set to return the answer.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "cc399b36-e9d5-4a97-857d-6ef9dea50115",
+ "slug": "scrub-replace",
+ "title": "Scrub with a series of replace calls",
+ "blurb": "Use a series of replace calls and set to return the answer.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "0f0c738e-885a-4431-a592-ee12d25ae9f4",
+ "slug": "scrub-regex",
+ "title": "Scrub with a regex",
+ "blurb": "Use a re.sub and set to return the answer.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "9d733e59-dbc4-45f7-9c60-1b9c99020687",
+ "slug": "findall-regex",
+ "title": "Use re.findall to get valid letters",
+ "blurb": "Use a re.findall and set to return the answer.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "b87e1bcf-0c40-416e-b6cc-684a57c9455c",
+ "slug": "bitfield",
+ "title": "Bit field using a for loop",
+ "blurb": "Use a bit field with a for loop to keep track of used letters.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 6e75340094aa883e232fbc682c84e9dfab19ab63 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 05:57:14 -0600
Subject: [PATCH 194/932] Create config.json
---
exercises/practice/isogram/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/isogram/.articles/config.json
diff --git a/exercises/practice/isogram/.articles/config.json b/exercises/practice/isogram/.articles/config.json
new file mode 100644
index 00000000000..7ffb9bab186
--- /dev/null
+++ b/exercises/practice/isogram/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "c933d353-8857-48f9-bac3-1ac752676390",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach to determining an isogram.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From c63b15ea7f94086c607f30db1d557bb22604cd8e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 05:58:13 -0600
Subject: [PATCH 195/932] Create snippet.md
---
.../practice/isogram/.articles/performance/snippet.md | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/isogram/.articles/performance/snippet.md
diff --git a/exercises/practice/isogram/.articles/performance/snippet.md b/exercises/practice/isogram/.articles/performance/snippet.md
new file mode 100644
index 00000000000..3a0439bfc47
--- /dev/null
+++ b/exercises/practice/isogram/.articles/performance/snippet.md
@@ -0,0 +1,7 @@
+```
+scrubbed comprehension: 3.118929599993862e-06
+scrubbed replace: 9.586393000063253e-07
+scrubbed regex: 1.8171838999987813e-06
+findall regex: 4.059006099996623e-06
+bitfield: 5.4183307999919636e-06
+```
From 7b72b881e53fd78ec1ec65fb2d95d05292cd6a0b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 05:59:19 -0600
Subject: [PATCH 196/932] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 76 +++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 exercises/practice/isogram/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/isogram/.articles/performance/code/Benchmark.py b/exercises/practice/isogram/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..47790fdc4af
--- /dev/null
+++ b/exercises/practice/isogram/.articles/performance/code/Benchmark.py
@@ -0,0 +1,76 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""is_isogram("Emily Jung-Schwartzkopf")""",
+ """
+def is_isogram(phrase):
+ scrubbed = [ltr.lower() for ltr in phrase if ltr.isalpha()]
+ return len(set(scrubbed)) == len(scrubbed)
+
+""", number=loops) / loops
+
+print(f"scrubbed comprehension: {val}")
+
+val = timeit.timeit("""is_isogram("Emily Jung-Schwartzkopf")""",
+ """
+def is_isogram(phrase):
+ scrubbed = phrase.replace('-', '').replace(' ', '').lower()
+ return len(scrubbed) == len(set(scrubbed))
+
+""", number=loops) / loops
+
+print(f"scrubbed replace: {val}")
+
+val = timeit.timeit("""is_isogram("Emily Jung-Schwartzkopf")""",
+ """
+import re
+
+def is_isogram(phrase):
+ scrubbed = re.compile('[^a-zA-Z]').sub('', phrase).lower()
+ return len(set(scrubbed)) == len(scrubbed)
+
+""", number=loops) / loops
+
+print(f"scrubbed regex: {val}")
+
+val = timeit.timeit("""is_isogram("Emily Jung-Schwartzkopf")""",
+ """
+import re
+
+def is_isogram(phrase):
+ scrubbed = "".join(re.findall("[a-zA-Z]", phrase)).lower()
+ return len(set(scrubbed)) == len(scrubbed)
+
+""", number=loops) / loops
+
+print(f"findall regex: {val}")
+
+val = timeit.timeit("""is_isogram("Emily Jung-Schwartzkopf")""",
+ """
+A_LCASE = 97
+Z_LCASE = 122
+A_UCASE = 65
+Z_UCASE = 90
+
+
+def is_isogram(phrase):
+ letter_flags = 0
+
+ for ltr in phrase:
+ letter = ord(ltr)
+ if letter >= A_LCASE and letter <= Z_LCASE:
+ if letter_flags & (1 << (letter - A_LCASE)) != 0:
+ return False
+ else:
+ letter_flags |= 1 << (letter - A_LCASE)
+ elif letter >= A_UCASE and letter <= Z_UCASE:
+ if letter_flags & (1 << (letter - A_UCASE)) != 0:
+ return False
+ else:
+ letter_flags |= 1 << (letter - A_UCASE)
+ return True
+
+""", number=loops) / loops
+
+print(f"bitfield: {val}")
From dc9e74bfad2261af0c625c764945cac7d8c429c9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 06:24:46 -0600
Subject: [PATCH 197/932] Create content.md
---
.../isogram/.articles/performance/content.md | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 exercises/practice/isogram/.articles/performance/content.md
diff --git a/exercises/practice/isogram/.articles/performance/content.md b/exercises/practice/isogram/.articles/performance/content.md
new file mode 100644
index 00000000000..8ee1f1b9af9
--- /dev/null
+++ b/exercises/practice/isogram/.articles/performance/content.md
@@ -0,0 +1,42 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently determine if a string is an Isogram in Python.
+
+The [approaches page][approaches] lists four idiomatic approaches to this exercise:
+
+1. [Using a list comprehension and `set`][approach-scrub-comprehension]
+2. [Using `replace` and `set`][approach-scrub-replace]
+3. [Using a `re.sub` and `set`][approach-scrub-regex]
+4. [Using a `re.findall` and `set`][approach-findall-regex]
+
+For our performance investigation, we'll also include a fifth approach that [uses a bit field to keep track of used letters][approach-bitfield].
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+scrubbed comprehension: 3.118929599993862e-06
+scrubbed replace: 9.586393000063253e-07
+scrubbed regex: 1.8171838999987813e-06
+findall regex: 4.059006099996623e-06
+bitfield: 5.4183307999919636e-06
+```
+
+The four fastest approaches use `set`.
+
+- Calling a series of `replace` methods to scrub the input was the fastest approach at about 959 nanoseconds.
+- Next fastest was using `re.sub` to scrub the input, at about 1817 nanoseconds.
+- Third fastest was using a list comprehension to scrub the input, at about 3119 nanoseconds.
+- Using `re.findall` to scrub the input was fourth fastest, at about 4059 nanoseconds.
+- Although the bit field approach may be faster in other languages, it is significantly slower in Python.
+It was slower than all of the `set` approaches, at about 5418 nanoseconds.
+
+[approaches]: https://exercism.org/tracks/python/exercises/isogram/approaches
+[approach-scrub-comprehension]: https://exercism.org/tracks/python/exercises/isogram/approaches/scrub-comprehension
+[approach-scrub-replace]: https://exercism.org/tracks/python/exercises/isogram/approaches/scrub-replace
+[approach-scrub-regex]: https://exercism.org/tracks/python/exercises/isogram/approaches/scrub-regex
+[approach-findall-regex]: https://exercism.org/tracks/python/exercises/isogram/approaches/findall-regex
+[approach-bitfield]: https://exercism.org/tracks/python/exercises/isogram/approaches/bitfield
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/isogram/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
From e35fd58f10463a2eb5a2ed039756467a0bf1e40e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 18 Nov 2022 08:35:28 -0600
Subject: [PATCH 198/932] Create introduction.md
---
.../isogram/.approaches/introduction.md | 90 +++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/introduction.md
diff --git a/exercises/practice/isogram/.approaches/introduction.md b/exercises/practice/isogram/.approaches/introduction.md
new file mode 100644
index 00000000000..6616e5a1949
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/introduction.md
@@ -0,0 +1,90 @@
+# Introduction
+
+There are many idiomatic ways to solve Isogram.
+Among them are:
+- You can scrub the input with a list comprehension and then compare the `len()` of the scrubbed letters with the `len()` of a `set` of the scrubbed letters.
+- You can scrub the input with a couple of calls to `replace()` and then compare the `len()` of the scrubbed letters with the `len()` of a `set` of the scrubbed letters.
+- You can scrub the input with a `re.sub()` and then compare the `len()` of the scrubbed letters with the `len()` of a `set` of the scrubbed letters.
+- You can filter the input with a `re.findall()` and then compare the `len()` of the scrubbed letters with the `len()` of a `set` of the scrubbed letters.
+
+## General guidance
+
+The key to solving Isogram is to determine if any of the letters in the input are repeated.
+A repeated letter means the input is not an isogram.
+The letters are "scrubbed" or filtered so that non-alphabetic characters are not considered when determining an isogram.
+The occurrence of the letter `a` and the letter `A` count as a repeated letter, so `Alpha` would not be an isogram.
+
+The following four approaches compare the length of the scrubbed letters with the length of a `set`of the scrubbed letters.
+
+## Approach: scrub with a list comprehension
+
+```python
+def is_isogram(phrase):
+ scrubbed = [ltr.lower() for ltr in phrase if ltr.isalpha()]
+ return len(set(scrubbed)) == len(scrubbed)
+
+```
+
+For more information, check the [scrub with list comprehension approach][approach-scrub-comprehension]
+
+## Approach: scrub with `replace()`
+
+```python
+def is_isogram(phrase):
+ scrubbed = phrase.replace('-', '').replace(' ', '').lower()
+ return len(scrubbed) == len(set(scrubbed))
+
+```
+
+For more information, check the [scrub with `replace()` approach][approach-scrub-replace]
+
+## Approach: scrub with `re.sub()`
+
+```python
+import re
+
+
+def is_isogram(phrase):
+ scrubbed = re.compile('[^a-zA-Z]').sub('', phrase).lower()
+ return len(set(scrubbed)) == len(scrubbed)
+
+```
+
+For more information, check the [scrub with `re.sub()` approach][approach-scrub-regex]
+
+## Approach: filter with `re.findall()`
+
+```python
+import re
+
+
+def is_isogram(phrase):
+ scrubbed = "".join(re.findall("[a-zA-Z]", phrase)).lower()
+ return len(set(scrubbed)) == len(scrubbed)
+
+```
+
+For more information, check the [filter with `re.findall()` approach][approach-scrub-regex]
+
+## Other approaches
+
+Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows:
+
+### Other approach: Bit field
+
+Another approach can use a bit field to keep track of used letters.
+For more information, check the [bit field approach][approach-bitfield].
+
+## Which approach to use?
+
+All four `set` approaches are idiomatic.
+The `replace` approach is the fastest.
+
+To compare performance of the approaches, check the [Performance article][article-performance].
+
+[approach-scrub-comprehension]: https://exercism.org/tracks/python/exercises/isogram/approaches/scrub-comprehension
+[approach-scrub-replace]: https://exercism.org/tracks/python/exercises/isogram/approaches/scrub-replace
+[approach-scrub-regex]: https://exercism.org/tracks/python/exercises/isogram/approaches/scrub-regex
+[approach-findall-regex]: https://exercism.org/tracks/python/exercises/isogram/approaches/findall-regex
+[approach-bitfield]: https://exercism.org/tracks/python/exercises/isogram/approaches/bitfield
+[article-performance]: https://exercism.org/tracks/python/exercises/isogram/articles/performance
From ba21ad766b83c313c944bcd00d2b8b00e857d2f2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 13:27:52 -0600
Subject: [PATCH 199/932] Create snippet.txt
---
.../isogram/.approaches/scrub-comprehension/snippet.txt | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/scrub-comprehension/snippet.txt
diff --git a/exercises/practice/isogram/.approaches/scrub-comprehension/snippet.txt b/exercises/practice/isogram/.approaches/scrub-comprehension/snippet.txt
new file mode 100644
index 00000000000..9bf47270fa7
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/scrub-comprehension/snippet.txt
@@ -0,0 +1,3 @@
+def is_isogram(phrase):
+ scrubbed = [ltr.lower() for ltr in phrase if ltr.isalpha()]
+ return len(set(scrubbed)) == len(scrubbed)
From bae0024fca06745e6bb763e0ee93b51155092c3b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:09:54 -0600
Subject: [PATCH 200/932] Create content.md
---
.../scrub-comprehension/content.md | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/scrub-comprehension/content.md
diff --git a/exercises/practice/isogram/.approaches/scrub-comprehension/content.md b/exercises/practice/isogram/.approaches/scrub-comprehension/content.md
new file mode 100644
index 00000000000..04dc5bf0e2d
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/scrub-comprehension/content.md
@@ -0,0 +1,36 @@
+# Scrub with a list comprehension
+
+```python
+def is_isogram(phrase):
+ scrubbed = [ltr.lower() for ltr in phrase if ltr.isalpha()]
+ return len(set(scrubbed)) == len(scrubbed)
+
+```
+
+For this approach, a [list comprehension][list-comprehension] is used to iterate the letters in the input phrase [str][str]ing.
+
+- In the code example, `ltr` is the name given to each letter iterated in the [`for`][for] loop.
+- The result of each iteration is `ltr.lower()`, which is the [`lower`][lower]cased letter being iterated.
+All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
+since `A` and `a` are considered to be the same letter.
+- The iterable part of the list comprehension is the input phrase.
+- The letters are filtered by the use of the [optional conditional logic][conditional-logic]: `if ltr.isalpha()`.
+[`isalpha()`][isalpha] returns `True` if the letter being iterated is alphabetic.
+
+When the list comprehension is done, the `scrubbed` variable will be a list holding only lowercased alphabetic characters.
+- A [`set`][set] is constructed from the `scrubbed` list and its [`len`][len] is compared with the `len` of the the `scrubbed` list.
+Since a `set` holds only unique values, the phrase will be an isogram if its number of unique letters is the same as its total number of letters.
+The function returns whether the number of unique letters equals the total number of letters.
+- For `Alpha` it would return `False`, because `a` is considered to repeat `A`, so the number of unique letters in `Alpha` is `4`,
+and the total number of letters in `Alpha` is `5`.
+- For `Bravo` it would return `True`, since the number of unique letters in `Bravo` is `5`, and the total number of letters in `Bravo` is `5`.
+
+
+[list-comprehension]: https://realpython.com/list-comprehension-python/#using-list-comprehensions
+[str]: https://docs.python.org/3/library/stdtypes.html#textseq
+[lower]: https://docs.python.org/3/library/stdtypes.html?highlight=lower#str.lower
+[for]: https://realpython.com/python-for-loop/#the-python-for-loop
+[conditional-logic]: https://realpython.com/list-comprehension-python/#using-conditional-logic
+[isalpha]: https://docs.python.org/3/library/stdtypes.html?highlight=lower#str.isalpha
+[set]: https://docs.python.org/3/library/stdtypes.html?highlight=set#set
+[len]: https://docs.python.org/3/library/functions.html?highlight=len#len
From b9eef38434ea166e1dc5a22229f625db86bf055e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:11:46 -0600
Subject: [PATCH 201/932] Create snippet.txt
---
.../practice/isogram/.approaches/scrub-replace/snippet.txt | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/scrub-replace/snippet.txt
diff --git a/exercises/practice/isogram/.approaches/scrub-replace/snippet.txt b/exercises/practice/isogram/.approaches/scrub-replace/snippet.txt
new file mode 100644
index 00000000000..586fb984429
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/scrub-replace/snippet.txt
@@ -0,0 +1,3 @@
+def is_isogram(phrase):
+ scrubbed = phrase.replace('-', '').replace(' ', '').lower()
+ return len(scrubbed) == len(set(scrubbed))
From 28880732a4a83ecb49737264bff9ec922621acec Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:12:27 -0600
Subject: [PATCH 202/932] Create snippet.txt
---
.../practice/isogram/.approaches/scrub-regex/snippet.txt | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/scrub-regex/snippet.txt
diff --git a/exercises/practice/isogram/.approaches/scrub-regex/snippet.txt b/exercises/practice/isogram/.approaches/scrub-regex/snippet.txt
new file mode 100644
index 00000000000..e95ebddd6ea
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/scrub-regex/snippet.txt
@@ -0,0 +1,6 @@
+import re
+
+
+def is_isogram(phrase):
+ scrubbed = re.compile('[^a-zA-Z]').sub('', phrase).lower()
+ return len(set(scrubbed)) == len(scrubbed)
From 757a8b492950ccf195338408ee51ff2d51fbd4a0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:13:12 -0600
Subject: [PATCH 203/932] Create snippet.txt
---
.../practice/isogram/.approaches/findall-regex/snippet.txt | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/findall-regex/snippet.txt
diff --git a/exercises/practice/isogram/.approaches/findall-regex/snippet.txt b/exercises/practice/isogram/.approaches/findall-regex/snippet.txt
new file mode 100644
index 00000000000..313670c840a
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/findall-regex/snippet.txt
@@ -0,0 +1,6 @@
+import re
+
+
+def is_isogram(phrase):
+ scrubbed = "".join(re.findall("[a-zA-Z]", phrase)).lower()
+ return len(set(scrubbed)) == len(scrubbed)
From 6c06d9ef563538dcde0ed620bdcb60c0cb711dd1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:15:54 -0600
Subject: [PATCH 204/932] Create content.md
---
.../isogram/.approaches/bitfield/content.md | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/bitfield/content.md
diff --git a/exercises/practice/isogram/.approaches/bitfield/content.md b/exercises/practice/isogram/.approaches/bitfield/content.md
new file mode 100644
index 00000000000..3cf367a19d4
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/bitfield/content.md
@@ -0,0 +1,27 @@
+# Bitfield to keep track of used letters
+
+```python
+A_LCASE = 97
+Z_LCASE = 122
+A_UCASE = 65
+Z_UCASE = 90
+
+
+def is_isogram(phrase):
+ letter_flags = 0
+
+ for ltr in phrase:
+ letter = ord(ltr)
+ if letter >= A_LCASE and letter <= Z_LCASE:
+ if letter_flags & (1 << (letter - A_LCASE)) != 0:
+ return False
+ else:
+ letter_flags |= 1 << (letter - A_LCASE)
+ elif letter >= A_UCASE and letter <= Z_UCASE:
+ if letter_flags & (1 << (letter - A_UCASE)) != 0:
+ return False
+ else:
+ letter_flags |= 1 << (letter - A_UCASE)
+ return True
+
+```
From f32e5ec911008158566ce2c3906dc55b52cafd92 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:17:09 -0600
Subject: [PATCH 205/932] Create snippet.txt
---
.../practice/isogram/.approaches/bitfield/snippet.txt | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/bitfield/snippet.txt
diff --git a/exercises/practice/isogram/.approaches/bitfield/snippet.txt b/exercises/practice/isogram/.approaches/bitfield/snippet.txt
new file mode 100644
index 00000000000..802615392ac
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/bitfield/snippet.txt
@@ -0,0 +1,7 @@
+for ltr in phrase:
+ letter = ord(ltr)
+ if letter >= A_LCASE and letter <= Z_LCASE:
+ if letter_flags & (1 << (letter - A_LCASE)) != 0:
+ return False
+ else:
+ letter_flags |= 1 << (letter - A_LCASE)
From 81c379b117b8eb0dc2946a9115b3d90d0f7c481b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:18:06 -0600
Subject: [PATCH 206/932] Create content.md
---
.../practice/isogram/.approaches/scrub-replace/content.md | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/scrub-replace/content.md
diff --git a/exercises/practice/isogram/.approaches/scrub-replace/content.md b/exercises/practice/isogram/.approaches/scrub-replace/content.md
new file mode 100644
index 00000000000..72606784fda
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/scrub-replace/content.md
@@ -0,0 +1,8 @@
+# Scrub with `replace()`
+
+```python
+def is_isogram(phrase):
+ scrubbed = phrase.replace('-', '').replace(' ', '').lower()
+ return len(scrubbed) == len(set(scrubbed))
+
+```
From cd47b94258952fd0aaf33f9117651fa09914fb48 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:18:43 -0600
Subject: [PATCH 207/932] Create content.md
---
.../isogram/.approaches/scrub-regex/content.md | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/scrub-regex/content.md
diff --git a/exercises/practice/isogram/.approaches/scrub-regex/content.md b/exercises/practice/isogram/.approaches/scrub-regex/content.md
new file mode 100644
index 00000000000..5da783f0276
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/scrub-regex/content.md
@@ -0,0 +1,11 @@
+# Scrub with `re.sub()`
+
+```python
+import re
+
+
+def is_isogram(phrase):
+ scrubbed = re.compile('[^a-zA-Z]').sub('', phrase).lower()
+ return len(set(scrubbed)) == len(scrubbed)
+
+```
From 9b66ad1004f76aadb0f2413aa0e06e34faf58fc3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:19:18 -0600
Subject: [PATCH 208/932] Create content.md
---
.../isogram/.approaches/findall-regex/content.md | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/isogram/.approaches/findall-regex/content.md
diff --git a/exercises/practice/isogram/.approaches/findall-regex/content.md b/exercises/practice/isogram/.approaches/findall-regex/content.md
new file mode 100644
index 00000000000..4032df2c670
--- /dev/null
+++ b/exercises/practice/isogram/.approaches/findall-regex/content.md
@@ -0,0 +1,11 @@
+# Filter with `re.findall()`
+
+```python
+import re
+
+
+def is_isogram(phrase):
+ scrubbed = "".join(re.findall("[a-zA-Z]", phrase)).lower()
+ return len(set(scrubbed)) == len(scrubbed)
+
+```
From 7c3f339790ebfde7dca9f2dac06585c3f4654b31 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 26 Nov 2022 06:59:43 -0600
Subject: [PATCH 209/932] Update content.md
---
.../.approaches/scrub-replace/content.md | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/exercises/practice/isogram/.approaches/scrub-replace/content.md b/exercises/practice/isogram/.approaches/scrub-replace/content.md
index 72606784fda..91ea8ea3d84 100644
--- a/exercises/practice/isogram/.approaches/scrub-replace/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-replace/content.md
@@ -6,3 +6,26 @@ def is_isogram(phrase):
return len(scrubbed) == len(set(scrubbed))
```
+
+For this approach, [`replace()`][replace] is called a couple times to scrub the input phrase [str][str]ing.
+Thw two `replace()` calls are [chained][method-chaining], so the result of the first `replace()` is the input for the next `replace()`.
+The result of the last `replace()` is the input for `lower()`.
+All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
+since `A` and `a` are considered to be the same letter.
+When the replacing and lowercasing is done, the `scrubbed` variable will be a string having no hyphens or spaces,
+and with all alphabetic letters lowercased.
+- A [`set`][set] is constructed from the `scrubbed` string and its [`len`][len] is compared with the `len` of the the `scrubbed` string.
+Since a `set` holds only unique values, the phrase will be an isogram if its number of unique letters is the same as its total number of letters.
+The function returns whether the number of unique letters equals the total number of letters.
+- For `Alpha` it would return `False`, because `a` is considered to repeat `A`, so the number of unique letters in `Alpha` is `4`,
+and the total number of letters in `Alpha` is `5`.
+- For `Bravo` it would return `True`, since the number of unique letters in `Bravo` is `5`, and the total number of letters in `Bravo` is `5`.
+
+
+[replace]: https://docs.python.org/3/library/stdtypes.html?highlight=replace#str.replace
+[str]: https://docs.python.org/3/library/stdtypes.html#textseq
+[method-chaining]: https://www.tutorialspoint.com/Explain-Python-class-method-chaining
+[lower]: https://docs.python.org/3/library/stdtypes.html?highlight=lower#str.lower
+[set]: https://docs.python.org/3/library/stdtypes.html?highlight=set#set
+[len]: https://docs.python.org/3/library/functions.html?highlight=len#len
+
From 108f0251b7fa62b3c7b7fc67574438e8fb54266b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 26 Nov 2022 07:23:07 -0600
Subject: [PATCH 210/932] Update content.md
---
.../.approaches/scrub-regex/content.md | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/exercises/practice/isogram/.approaches/scrub-regex/content.md b/exercises/practice/isogram/.approaches/scrub-regex/content.md
index 5da783f0276..0676bd12145 100644
--- a/exercises/practice/isogram/.approaches/scrub-regex/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-regex/content.md
@@ -9,3 +9,36 @@ def is_isogram(phrase):
return len(set(scrubbed)) == len(scrubbed)
```
+
+For this approach, [regular expression][regex], also known as a [regex][regex-how-to], is used to scrub the input phrase [str][str]ing.
+- In the pattern of `[^a-zA-Z]` the brackets are used to define a character set that looks for characters which are not `a` through `z` and `A` through `Z`.
+```exercism/note
+If the first character of a character set is `^`, all the characters that are _not_ in the rest of the set will be matched.
+```
+This essentially matches any characters which are not in the English alphabet.
+ The pattern is passed to the [`compile()`][compile] method to construct a [regular expression object][regex-object].
+- The [`sub()`][sub] method is then called on the regex object.
+The `sub()` method replaces all non-alphabetic characters in the input phrase with an empty string.
+- The result of `sub()` is then [chained][method-chaining] as the input for [`lower()`][lower].
+All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
+since `A` and `a` are considered to be the same letter.
+When the replacing and lowercasing is done, the `scrubbed` variable will be a string having all alphabetic letters lowercased.
+- A [`set`][set] is constructed from the `scrubbed` string and its [`len`][len] is compared with the `len` of the the `scrubbed` string.
+Since a `set` holds only unique values, the phrase will be an isogram if its number of unique letters is the same as its total number of letters.
+The function returns whether the number of unique letters equals the total number of letters.
+- For `Alpha` it would return `False`, because `a` is considered to repeat `A`, so the number of unique letters in `Alpha` is `4`,
+and the total number of letters in `Alpha` is `5`.
+- For `Bravo` it would return `True`, since the number of unique letters in `Bravo` is `5`, and the total number of letters in `Bravo` is `5`.
+
+
+[regex]: https://docs.python.org/3/library/re.html
+[regex-how-to]: https://docs.python.org/3/howto/regex.html
+[str]: https://docs.python.org/3/library/stdtypes.html#textseq
+[compile]: https://docs.python.org/3/library/re.html?#re.compile
+[regex-object]: https://docs.python.org/3/library/re.html?#re-objects
+[sub]: https://docs.python.org/3/library/re.html?#re.sub
+[method-chaining]: https://www.tutorialspoint.com/Explain-Python-class-method-chaining
+[lower]: https://docs.python.org/3/library/stdtypes.html?highlight=lower#str.lower
+[set]: https://docs.python.org/3/library/stdtypes.html?highlight=set#set
+[len]: https://docs.python.org/3/library/functions.html?highlight=len#len
+
From baeaa3fef5122ea3333440ebc41c9c52b2a264c3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 26 Nov 2022 07:23:46 -0600
Subject: [PATCH 211/932] Update content.md
---
exercises/practice/isogram/.approaches/scrub-replace/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/isogram/.approaches/scrub-replace/content.md b/exercises/practice/isogram/.approaches/scrub-replace/content.md
index 91ea8ea3d84..8c6989717c2 100644
--- a/exercises/practice/isogram/.approaches/scrub-replace/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-replace/content.md
@@ -9,7 +9,7 @@ def is_isogram(phrase):
For this approach, [`replace()`][replace] is called a couple times to scrub the input phrase [str][str]ing.
Thw two `replace()` calls are [chained][method-chaining], so the result of the first `replace()` is the input for the next `replace()`.
-The result of the last `replace()` is the input for `lower()`.
+The result of the last `replace()` is the input for [`lower()`][lower].
All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
since `A` and `a` are considered to be the same letter.
When the replacing and lowercasing is done, the `scrubbed` variable will be a string having no hyphens or spaces,
From ac6bd1a9ffa0fc2487b9142ff87df886e3a538de Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 26 Nov 2022 07:36:58 -0600
Subject: [PATCH 212/932] Update content.md
---
.../.approaches/findall-regex/content.md | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/exercises/practice/isogram/.approaches/findall-regex/content.md b/exercises/practice/isogram/.approaches/findall-regex/content.md
index 4032df2c670..bc4a6f619af 100644
--- a/exercises/practice/isogram/.approaches/findall-regex/content.md
+++ b/exercises/practice/isogram/.approaches/findall-regex/content.md
@@ -9,3 +9,32 @@ def is_isogram(phrase):
return len(set(scrubbed)) == len(scrubbed)
```
+
+For this approach, [regular expression pattern][regex], also known as a [regex][regex-how-to], is used to filter the input phrase [str][str]ing.
+- In the pattern of `[a-zA-Z]` the brackets are used to define a character set that looks for characters which are `a` through `z` or `A` through `Z`.
+This essentially matches any characters which are in the English alphabet.
+The pattern is passed to the [`findall()`][findall] method to return a list of matched characters.
+- The result of `findall()` is passed as an argument to the [`join()`][join] method, which is called on an empty string.
+This makes a string out of the list of matched characters.
+- The output of `join()` is then [chained][method-chaining] as the input for [`lower()`][lower].
+All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
+since `A` and `a` are considered to be the same letter.
+When the filtering and lowercasing is done, the `scrubbed` variable will be a string having all alphabetic letters lowercased.
+- A [`set`][set] is constructed from the `scrubbed` string and its [`len`][len] is compared with the `len` of the the `scrubbed` string.
+Since a `set` holds only unique values, the phrase will be an isogram if its number of unique letters is the same as its total number of letters.
+The function returns whether the number of unique letters equals the total number of letters.
+- For `Alpha` it would return `False`, because `a` is considered to repeat `A`, so the number of unique letters in `Alpha` is `4`,
+and the total number of letters in `Alpha` is `5`.
+- For `Bravo` it would return `True`, since the number of unique letters in `Bravo` is `5`, and the total number of letters in `Bravo` is `5`.
+
+
+[regex]: https://docs.python.org/3/library/re.html
+[regex-how-to]: https://docs.python.org/3/howto/regex.html
+[str]: https://docs.python.org/3/library/stdtypes.html#textseq
+[findall]: https://docs.python.org/3/library/re.html?#re.findall
+[join]: https://docs.python.org/3/library/stdtypes.html?#str.join
+[method-chaining]: https://www.tutorialspoint.com/Explain-Python-class-method-chaining
+[lower]: https://docs.python.org/3/library/stdtypes.html?highlight=lower#str.lower
+[set]: https://docs.python.org/3/library/stdtypes.html?highlight=set#set
+[len]: https://docs.python.org/3/library/functions.html?highlight=len#len
+
From 1c39403ab12e163912e719a95696b40fa20fc599 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 26 Nov 2022 07:37:31 -0600
Subject: [PATCH 213/932] Update content.md
---
exercises/practice/isogram/.approaches/scrub-regex/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/isogram/.approaches/scrub-regex/content.md b/exercises/practice/isogram/.approaches/scrub-regex/content.md
index 0676bd12145..0d2316c05b8 100644
--- a/exercises/practice/isogram/.approaches/scrub-regex/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-regex/content.md
@@ -19,7 +19,7 @@ This essentially matches any characters which are not in the English alphabet.
The pattern is passed to the [`compile()`][compile] method to construct a [regular expression object][regex-object].
- The [`sub()`][sub] method is then called on the regex object.
The `sub()` method replaces all non-alphabetic characters in the input phrase with an empty string.
-- The result of `sub()` is then [chained][method-chaining] as the input for [`lower()`][lower].
+- The output of `sub()` is then [chained][method-chaining] as the input for [`lower()`][lower].
All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
since `A` and `a` are considered to be the same letter.
When the replacing and lowercasing is done, the `scrubbed` variable will be a string having all alphabetic letters lowercased.
From b70fa6f831e572c2ea4b72387b4ffac841bd49b2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 26 Nov 2022 07:38:07 -0600
Subject: [PATCH 214/932] Update content.md
---
.../practice/isogram/.approaches/scrub-replace/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/isogram/.approaches/scrub-replace/content.md b/exercises/practice/isogram/.approaches/scrub-replace/content.md
index 8c6989717c2..dd65e9ab8c9 100644
--- a/exercises/practice/isogram/.approaches/scrub-replace/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-replace/content.md
@@ -8,8 +8,8 @@ def is_isogram(phrase):
```
For this approach, [`replace()`][replace] is called a couple times to scrub the input phrase [str][str]ing.
-Thw two `replace()` calls are [chained][method-chaining], so the result of the first `replace()` is the input for the next `replace()`.
-The result of the last `replace()` is the input for [`lower()`][lower].
+Thw two `replace()` calls are [chained][method-chaining], so the output of the first `replace()` is the input for the next `replace()`.
+The output of the last `replace()` is the input for [`lower()`][lower].
All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
since `A` and `a` are considered to be the same letter.
When the replacing and lowercasing is done, the `scrubbed` variable will be a string having no hyphens or spaces,
From 18e3195f02cf0edfeceff8933d6eacc8ea8b5263 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 26 Nov 2022 09:10:35 -0600
Subject: [PATCH 215/932] Update content.md
---
.../isogram/.approaches/bitfield/content.md | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/exercises/practice/isogram/.approaches/bitfield/content.md b/exercises/practice/isogram/.approaches/bitfield/content.md
index 3cf367a19d4..d522330ec1e 100644
--- a/exercises/practice/isogram/.approaches/bitfield/content.md
+++ b/exercises/practice/isogram/.approaches/bitfield/content.md
@@ -25,3 +25,48 @@ def is_isogram(phrase):
return True
```
+
+This solution uses the [ASCII][ascii] value of the letter to set the corresponding bit position.
+
+Some [constants][const] are defined for readability in the function.
+Python doesn't _enforce_ having real constant values, but using all uppercase letters is the naming convention for a Python constant.
+It indicates that the value is not intended to be changed.
+
+- The ASCII value for `a` is `97`.
+- The ASCII value for `z` is `122`.
+- The ASCII value for `A` is `65`.
+- The ASCII value for `Z` is `90`.
+
+
+- A [`for`][for] loop is used to iterate the characters in the input phrase.
+- The [`ord()`][ord] function is used to get the ASCII value of the letter.
+- The `if` statements look for a character being `a` through `z` or `A` through `Z`.
+
+- If the lowercase letter is subtracted by `97`, then `a` will result in `0`, because `97` minus `97` equals `0`.
+ `z` would result in `25`, because `122` minus `97` equals `25`.
+ So `a` would have `1` [shifted left][shift-left] 0 places (so not shifted at all) and `z` would have `1` shifted left 25 places.
+- If the uppercase letter is subtracted by `A`, then `A` will result in `0`, because `65` minus `65` equals `0`.
+ `Z` would result in `25`, because `90` minus `65` equals `25`.
+ So `A` would have `1` [shifted left][shift-left] 0 places (so not shifted at all) and `Z` would have `1` shifted left 25 places.
+
+In that way, both a lower-cased `z` and an upper-cased `Z` can share the same position in the bit field.
+
+So, for a thirty-two bit integer, if the values for `a` and `Z` were both set, the bits would look like
+
+```
+ zyxwvutsrqponmlkjihgfedcba
+00000010000000000000000000000001
+```
+
+We can use the [bitwise AND operator][and] to check if a bit has already been set.
+If it has been set, we know the letter is duplicated and we can immediately return `False`.
+If it has not been set, we can use the [bitwise OR operator][or] to set the bit.
+If the loop completes without finding a duplicate letter (and returning `False`), the function returns `True`.
+
+[ascii]: https://www.asciitable.com/
+[const]: https://realpython.com/python-constants/
+[for]: https://realpython.com/python-for-loop/#the-python-for-loop
+[ord]: https://docs.python.org/3/library/functions.html?#ord
+[shift-left]: https://realpython.com/python-bitwise-operators/#left-shift
+[or]: https://realpython.com/python-bitwise-operators/#bitwise-or
+[and]: https://realpython.com/python-bitwise-operators/#bitwise-and
From 1ed21677cc31e59bef7608d6d4c61905a4b32d45 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 04:21:34 -0600
Subject: [PATCH 216/932] Update content.md
---
.../practice/isogram/.approaches/findall-regex/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/isogram/.approaches/findall-regex/content.md b/exercises/practice/isogram/.approaches/findall-regex/content.md
index bc4a6f619af..72f6bb41640 100644
--- a/exercises/practice/isogram/.approaches/findall-regex/content.md
+++ b/exercises/practice/isogram/.approaches/findall-regex/content.md
@@ -19,8 +19,8 @@ This makes a string out of the list of matched characters.
- The output of `join()` is then [chained][method-chaining] as the input for [`lower()`][lower].
All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
since `A` and `a` are considered to be the same letter.
-When the filtering and lowercasing is done, the `scrubbed` variable will be a string having all alphabetic letters lowercased.
-- A [`set`][set] is constructed from the `scrubbed` string and its [`len`][len] is compared with the `len` of the the `scrubbed` string.
+When the filtering and lowercasing is done, the scrubbed variable will be a string having all alphabetic letters lowercased.
+- A [`set`][set] is constructed from the scrubbed string and its [`len`][len] is compared with the `len` of the the scrubbed string.
Since a `set` holds only unique values, the phrase will be an isogram if its number of unique letters is the same as its total number of letters.
The function returns whether the number of unique letters equals the total number of letters.
- For `Alpha` it would return `False`, because `a` is considered to repeat `A`, so the number of unique letters in `Alpha` is `4`,
From 57d7b3a895b0d9a575e42c7e101a76836bb86be6 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 04:22:49 -0600
Subject: [PATCH 217/932] Update content.md
---
.../isogram/.approaches/scrub-comprehension/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/isogram/.approaches/scrub-comprehension/content.md b/exercises/practice/isogram/.approaches/scrub-comprehension/content.md
index 04dc5bf0e2d..66e5bc7d03a 100644
--- a/exercises/practice/isogram/.approaches/scrub-comprehension/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-comprehension/content.md
@@ -17,8 +17,8 @@ since `A` and `a` are considered to be the same letter.
- The letters are filtered by the use of the [optional conditional logic][conditional-logic]: `if ltr.isalpha()`.
[`isalpha()`][isalpha] returns `True` if the letter being iterated is alphabetic.
-When the list comprehension is done, the `scrubbed` variable will be a list holding only lowercased alphabetic characters.
-- A [`set`][set] is constructed from the `scrubbed` list and its [`len`][len] is compared with the `len` of the the `scrubbed` list.
+When the list comprehension is done, the scrubbed variable will be a list holding only lowercased alphabetic characters.
+- A [`set`][set] is constructed from the scrubbed list and its [`len`][len] is compared with the `len` of the the scrubbed list.
Since a `set` holds only unique values, the phrase will be an isogram if its number of unique letters is the same as its total number of letters.
The function returns whether the number of unique letters equals the total number of letters.
- For `Alpha` it would return `False`, because `a` is considered to repeat `A`, so the number of unique letters in `Alpha` is `4`,
From 4dec58271fc30439c84d1727cd2f87baad637399 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 04:25:01 -0600
Subject: [PATCH 218/932] Update content.md
---
exercises/practice/isogram/.approaches/scrub-regex/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/isogram/.approaches/scrub-regex/content.md b/exercises/practice/isogram/.approaches/scrub-regex/content.md
index 0d2316c05b8..7c9c1b0dfe8 100644
--- a/exercises/practice/isogram/.approaches/scrub-regex/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-regex/content.md
@@ -11,9 +11,9 @@ def is_isogram(phrase):
```
For this approach, [regular expression][regex], also known as a [regex][regex-how-to], is used to scrub the input phrase [str][str]ing.
-- In the pattern of `[^a-zA-Z]` the brackets are used to define a character set that looks for characters which are not `a` through `z` and `A` through `Z`.
+- In the pattern of `[^a-zA-Z]` the brackets are used to define a character set that looks for characters which are _not_ `a` through `z` and `A` through `Z`.
```exercism/note
-If the first character of a character set is `^`, all the characters that are _not_ in the rest of the set will be matched.
+If the first character of a character set is `^`, all the characters that are _not_ in the rest of the character set will be matched.
```
This essentially matches any characters which are not in the English alphabet.
The pattern is passed to the [`compile()`][compile] method to construct a [regular expression object][regex-object].
From e0f35d0685163f5e43c1884157ed0fff13c10a1a Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 04:26:17 -0600
Subject: [PATCH 219/932] Update content.md
---
.../practice/isogram/.approaches/scrub-replace/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/isogram/.approaches/scrub-replace/content.md b/exercises/practice/isogram/.approaches/scrub-replace/content.md
index dd65e9ab8c9..041727a8ee9 100644
--- a/exercises/practice/isogram/.approaches/scrub-replace/content.md
+++ b/exercises/practice/isogram/.approaches/scrub-replace/content.md
@@ -12,9 +12,9 @@ Thw two `replace()` calls are [chained][method-chaining], so the output of the f
The output of the last `replace()` is the input for [`lower()`][lower].
All of the letters are lowercased so that letters of different cases will become the same letter for comparison purposes,
since `A` and `a` are considered to be the same letter.
-When the replacing and lowercasing is done, the `scrubbed` variable will be a string having no hyphens or spaces,
+When the replacing and lowercasing is done, the scrubbed variable will be a string having no hyphens or spaces,
and with all alphabetic letters lowercased.
-- A [`set`][set] is constructed from the `scrubbed` string and its [`len`][len] is compared with the `len` of the the `scrubbed` string.
+- A [`set`][set] is constructed from the scrubbed string and its [`len`][len] is compared with the `len` of the the scrubbed string.
Since a `set` holds only unique values, the phrase will be an isogram if its number of unique letters is the same as its total number of letters.
The function returns whether the number of unique letters equals the total number of letters.
- For `Alpha` it would return `False`, because `a` is considered to repeat `A`, so the number of unique letters in `Alpha` is `4`,
From 7f9d017b6c7559aff609c1a85ce7932eeeb393f1 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 17:33:00 +0100
Subject: [PATCH 220/932] Update roman_numeals
---
exercises/practice/roman-numerals/.meta/tests.toml | 6 ++++++
exercises/practice/roman-numerals/roman_numerals_test.py | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/exercises/practice/roman-numerals/.meta/tests.toml b/exercises/practice/roman-numerals/.meta/tests.toml
index 1f669c213e2..ca142e9f915 100644
--- a/exercises/practice/roman-numerals/.meta/tests.toml
+++ b/exercises/practice/roman-numerals/.meta/tests.toml
@@ -80,3 +80,9 @@ description = "666 is DCLXVI"
[efbe1d6a-9f98-4eb5-82bc-72753e3ac328]
description = "1666 is MDCLXVI"
+
+[3bc4b41c-c2e6-49d9-9142-420691504336]
+description = "3001 is MMMI"
+
+[4e18e96b-5fbb-43df-a91b-9cb511fe0856]
+description = "3999 is MMMCMXCIX"
diff --git a/exercises/practice/roman-numerals/roman_numerals_test.py b/exercises/practice/roman-numerals/roman_numerals_test.py
index 49b74d987e0..f85422cc5e6 100644
--- a/exercises/practice/roman-numerals/roman_numerals_test.py
+++ b/exercises/practice/roman-numerals/roman_numerals_test.py
@@ -77,3 +77,9 @@ def test_666_is_dclxvi(self):
def test_1666_is_mdclxvi(self):
self.assertEqual(roman(1666), "MDCLXVI")
+
+ def test_3001_is_mmmi(self):
+ self.assertEqual(roman(3001), "MMMI")
+
+ def test_3999_is_mmmcmxcix(self):
+ self.assertEqual(roman(3999), "MMMCMXCIX")
From fe875bf3dadbc9420d441e999df7fc688166066d Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 17:53:08 +0100
Subject: [PATCH 221/932] Updated series
---
exercises/practice/series/.meta/tests.toml | 16 +++++++++++++---
exercises/practice/series/series_test.py | 8 ++++++++
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/series/.meta/tests.toml b/exercises/practice/series/.meta/tests.toml
index 52ff7ed54c8..9696f51fca2 100644
--- a/exercises/practice/series/.meta/tests.toml
+++ b/exercises/practice/series/.meta/tests.toml
@@ -1,6 +1,13 @@
-# 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.
[7ae7a46a-d992-4c2a-9c15-a112d125ebad]
description = "slices of one from one"
@@ -23,6 +30,9 @@ description = "slices of a long series"
[6d235d85-46cf-4fae-9955-14b6efef27cd]
description = "slice length is too large"
+[d7957455-346d-4e47-8e4b-87ed1564c6d7]
+description = "slice length is way too large"
+
[d34004ad-8765-4c09-8ba1-ada8ce776806]
description = "slice length cannot be zero"
diff --git a/exercises/practice/series/series_test.py b/exercises/practice/series/series_test.py
index c4a6371255f..7f6de7a4669 100644
--- a/exercises/practice/series/series_test.py
+++ b/exercises/practice/series/series_test.py
@@ -37,6 +37,14 @@ def test_slice_length_is_too_large(self):
err.exception.args[0], "slice length cannot be greater than series length"
)
+ def test_slice_length_is_way_too_large(self):
+ with self.assertRaises(ValueError) as err:
+ slices("12345", 42)
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(
+ err.exception.args[0], "slice length cannot be greater than series length"
+ )
+
def test_slice_length_cannot_be_zero(self):
with self.assertRaises(ValueError) as err:
slices("12345", 0)
From 57e71a4d71e80c5a9c8c8cc01cbedb489738d6bb Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 18:36:26 +0100
Subject: [PATCH 222/932] updated luhn
---
exercises/practice/luhn/.meta/tests.toml | 3 +++
exercises/practice/luhn/luhn_test.py | 3 +++
2 files changed, 6 insertions(+)
diff --git a/exercises/practice/luhn/.meta/tests.toml b/exercises/practice/luhn/.meta/tests.toml
index eea08d79760..c0be0c4d9d0 100644
--- a/exercises/practice/luhn/.meta/tests.toml
+++ b/exercises/practice/luhn/.meta/tests.toml
@@ -33,6 +33,9 @@ description = "invalid credit card"
[20e67fad-2121-43ed-99a8-14b5b856adb9]
description = "invalid long number with an even remainder"
+[7e7c9fc1-d994-457c-811e-d390d52fba5e]
+description = "invalid long number with a remainder divisible by 5"
+
[ad2a0c5f-84ed-4e5b-95da-6011d6f4f0aa]
description = "valid number with an even number of digits"
diff --git a/exercises/practice/luhn/luhn_test.py b/exercises/practice/luhn/luhn_test.py
index a60d8525f2a..bbc1f8eb1f8 100644
--- a/exercises/practice/luhn/luhn_test.py
+++ b/exercises/practice/luhn/luhn_test.py
@@ -32,6 +32,9 @@ def test_invalid_credit_card(self):
def test_invalid_long_number_with_an_even_remainder(self):
self.assertIs(Luhn("1 2345 6789 1234 5678 9012").valid(), False)
+ def test_invalid_long_number_with_a_remainder_divisible_by_5(self):
+ self.assertIs(Luhn("1 2345 6789 1234 5678 9013").valid(), False)
+
def test_valid_number_with_an_even_number_of_digits(self):
self.assertIs(Luhn("095 245 88").valid(), True)
From e19101e7c20741e5b3899133ea222a482232c70e Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 18:39:59 +0100
Subject: [PATCH 223/932] updated word_count_test
---
exercises/practice/word-count/.meta/tests.toml | 3 +++
exercises/practice/word-count/word_count_test.py | 3 +++
2 files changed, 6 insertions(+)
diff --git a/exercises/practice/word-count/.meta/tests.toml b/exercises/practice/word-count/.meta/tests.toml
index 247f13746f7..1be425b33c2 100644
--- a/exercises/practice/word-count/.meta/tests.toml
+++ b/exercises/practice/word-count/.meta/tests.toml
@@ -52,3 +52,6 @@ description = "multiple spaces not detected as a word"
[50176e8a-fe8e-4f4c-b6b6-aa9cf8f20360]
description = "alternating word separators not detected as a word"
+
+[6d00f1db-901c-4bec-9829-d20eb3044557]
+description = "quotation for word with apostrophe"
diff --git a/exercises/practice/word-count/word_count_test.py b/exercises/practice/word-count/word_count_test.py
index 54af5f230af..b63a9eb357a 100644
--- a/exercises/practice/word-count/word_count_test.py
+++ b/exercises/practice/word-count/word_count_test.py
@@ -88,6 +88,9 @@ def test_alternating_word_separators_not_detected_as_a_word(self):
count_words(",\n,one,\n ,two \n 'three'"), {"one": 1, "two": 1, "three": 1}
)
+ def test_quotation_for_word_with_apostrophe(self):
+ self.assertEqual(count_words("can, can't, 'can't'"), {"can": 1, "can't": 2})
+
# Additional tests for this track
def test_tabs(self):
From f76bac8aaf1478318e0d3c8dfc17d067644f8f33 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 18:52:28 +0100
Subject: [PATCH 224/932] Updated matching_brackets_test
---
exercises/practice/matching-brackets/.meta/tests.toml | 3 +++
exercises/practice/matching-brackets/matching_brackets_test.py | 3 +++
2 files changed, 6 insertions(+)
diff --git a/exercises/practice/matching-brackets/.meta/tests.toml b/exercises/practice/matching-brackets/.meta/tests.toml
index da1314742a2..35a98a04217 100644
--- a/exercises/practice/matching-brackets/.meta/tests.toml
+++ b/exercises/practice/matching-brackets/.meta/tests.toml
@@ -48,6 +48,9 @@ description = "unpaired and nested brackets"
[a0205e34-c2ac-49e6-a88a-899508d7d68e]
description = "paired and wrong nested brackets"
+[1d5c093f-fc84-41fb-8c2a-e052f9581602]
+description = "paired and wrong nested brackets but innermost are correct"
+
[ef47c21b-bcfd-4998-844c-7ad5daad90a8]
description = "paired and incomplete brackets"
diff --git a/exercises/practice/matching-brackets/matching_brackets_test.py b/exercises/practice/matching-brackets/matching_brackets_test.py
index 86b429d2e06..fd23bd78403 100644
--- a/exercises/practice/matching-brackets/matching_brackets_test.py
+++ b/exercises/practice/matching-brackets/matching_brackets_test.py
@@ -47,6 +47,9 @@ def test_unpaired_and_nested_brackets(self):
def test_paired_and_wrong_nested_brackets(self):
self.assertEqual(is_paired("[({]})"), False)
+ def test_paired_and_wrong_nested_brackets_but_innermost_are_correct(self):
+ self.assertEqual(is_paired("[({}])"), False)
+
def test_paired_and_incomplete_brackets(self):
self.assertEqual(is_paired("{}["), False)
From efe7a6db35f59613546c5b8e58c699f925b65ff3 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 19:29:26 +0100
Subject: [PATCH 225/932] updated rectangles_test
---
exercises/practice/rectangles/.meta/tests.toml | 16 +++++++++++++---
exercises/practice/rectangles/rectangles_test.py | 16 ++++++++++++++++
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/rectangles/.meta/tests.toml b/exercises/practice/rectangles/.meta/tests.toml
index 63cd6c4d9ad..282015033ae 100644
--- a/exercises/practice/rectangles/.meta/tests.toml
+++ b/exercises/practice/rectangles/.meta/tests.toml
@@ -1,6 +1,13 @@
-# 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.
[485b7bab-4150-40aa-a8db-73013427d08c]
description = "no rows"
@@ -40,3 +47,6 @@ description = "corner is required for a rectangle to be complete"
[d78fe379-8c1b-4d3c-bdf7-29bfb6f6dc66]
description = "large input with many rectangles"
+
+[6ef24e0f-d191-46da-b929-4faca24b4cd2]
+description = "rectangles must have four sides"
diff --git a/exercises/practice/rectangles/rectangles_test.py b/exercises/practice/rectangles/rectangles_test.py
index 3cfa8181cfe..59c622de769 100644
--- a/exercises/practice/rectangles/rectangles_test.py
+++ b/exercises/practice/rectangles/rectangles_test.py
@@ -83,6 +83,22 @@ def test_large_input_with_many_rectangles(self):
60,
)
+ def test_rectangles_must_have_four_sides(self):
+ self.assertEqual(
+ rectangles(
+ [
+ "+-+ +-+",
+ "| | | |",
+ "+-+-+-+",
+ " | | ",
+ "+-+-+-+",
+ "| | | |",
+ "+-+ +-+",
+ ]
+ ),
+ 5,
+ )
+
if __name__ == "__main__":
unittest.main()
From 7b57a24ab1cdc8f5b0d2a2012b04b21058d38fae Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 17:39:30 +0100
Subject: [PATCH 226/932] Added new_test files
---
exercises/practice/say/.meta/tests.toml | 25 ++++++++++++++++++++++---
exercises/practice/say/say_test.py | 12 ++++++++++++
2 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/say/.meta/tests.toml b/exercises/practice/say/.meta/tests.toml
index df50fd17bb9..a5532e9ed30 100644
--- a/exercises/practice/say/.meta/tests.toml
+++ b/exercises/practice/say/.meta/tests.toml
@@ -1,6 +1,13 @@
-# 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.
[5d22a120-ba0c-428c-bd25-8682235d83e8]
description = "zero"
@@ -17,12 +24,24 @@ description = "twenty"
[d78601eb-4a84-4bfa-bf0e-665aeb8abe94]
description = "twenty-two"
+[f010d4ca-12c9-44e9-803a-27789841adb1]
+description = "thirty"
+
+[738ce12d-ee5c-4dfb-ad26-534753a98327]
+description = "ninety-nine"
+
[e417d452-129e-4056-bd5b-6eb1df334dce]
description = "one hundred"
[d6924f30-80ba-4597-acf6-ea3f16269da8]
description = "one hundred twenty-three"
+[2f061132-54bc-4fd4-b5df-0a3b778959b9]
+description = "two hundred"
+
+[feed6627-5387-4d38-9692-87c0dbc55c33]
+description = "nine hundred ninety-nine"
+
[3d83da89-a372-46d3-b10d-de0c792432b3]
description = "one thousand"
diff --git a/exercises/practice/say/say_test.py b/exercises/practice/say/say_test.py
index 0d14b6e7cd2..22deb5364cb 100644
--- a/exercises/practice/say/say_test.py
+++ b/exercises/practice/say/say_test.py
@@ -23,12 +23,24 @@ def test_twenty(self):
def test_twenty_two(self):
self.assertEqual(say(22), "twenty-two")
+ def test_thirty(self):
+ self.assertEqual(say(30), "thirty")
+
+ def test_ninety_nine(self):
+ self.assertEqual(say(99), "ninety-nine")
+
def test_one_hundred(self):
self.assertEqual(say(100), "one hundred")
def test_one_hundred_twenty_three(self):
self.assertEqual(say(123), "one hundred twenty-three")
+ def test_two_hundred(self):
+ self.assertEqual(say(200), "two hundred")
+
+ def test_nine_hundred_ninety_nine(self):
+ self.assertEqual(say(999), "nine hundred ninety-nine")
+
def test_one_thousand(self):
self.assertEqual(say(1000), "one thousand")
From cca3640d5304d83308b1c86ebb8a32b790e595fc Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 18:44:15 +0100
Subject: [PATCH 227/932] Updated crypto_square_test
---
.../practice/crypto-square/.meta/tests.toml | 16 +++++++++++++---
.../practice/crypto-square/crypto_square_test.py | 5 +++++
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/crypto-square/.meta/tests.toml b/exercises/practice/crypto-square/.meta/tests.toml
index 054544573b0..085d142eadb 100644
--- a/exercises/practice/crypto-square/.meta/tests.toml
+++ b/exercises/practice/crypto-square/.meta/tests.toml
@@ -1,10 +1,20 @@
-# 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.
[407c3837-9aa7-4111-ab63-ec54b58e8e9f]
description = "empty plaintext results in an empty ciphertext"
+[aad04a25-b8bb-4304-888b-581bea8e0040]
+description = "normalization results in empty plaintext"
+
[64131d65-6fd9-4f58-bdd8-4a2370fb481d]
description = "Lowercase"
diff --git a/exercises/practice/crypto-square/crypto_square_test.py b/exercises/practice/crypto-square/crypto_square_test.py
index 4829f905651..3715400ca3d 100644
--- a/exercises/practice/crypto-square/crypto_square_test.py
+++ b/exercises/practice/crypto-square/crypto_square_test.py
@@ -13,6 +13,11 @@ def test_empty_plaintext_results_in_an_empty_ciphertext(self):
expected = ""
self.assertEqual(cipher_text(value), expected)
+ def test_normalization_results_in_empty_plaintext(self):
+ value = "... --- ..."
+ expected = ""
+ self.assertEqual(cipher_text(value), expected)
+
def test_lowercase(self):
value = "A"
expected = "a"
From 2638645fc9036b8f2e9d9c6045f60edcb691cbe4 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 19:38:07 +0100
Subject: [PATCH 228/932] [Maintance] Updated complex_numbers_test
---
exercises/practice/complex-numbers/.meta/tests.toml | 3 +++
exercises/practice/complex-numbers/complex_numbers_test.py | 5 +++++
2 files changed, 8 insertions(+)
diff --git a/exercises/practice/complex-numbers/.meta/tests.toml b/exercises/practice/complex-numbers/.meta/tests.toml
index 1b36ba3b799..dffb1f2a318 100644
--- a/exercises/practice/complex-numbers/.meta/tests.toml
+++ b/exercises/practice/complex-numbers/.meta/tests.toml
@@ -102,6 +102,9 @@ description = "Complex exponential function -> Exponential of a purely real numb
[08eedacc-5a95-44fc-8789-1547b27a8702]
description = "Complex exponential function -> Exponential of a number with real and imaginary part"
+[d2de4375-7537-479a-aa0e-d474f4f09859]
+description = "Complex exponential function -> Exponential resulting in a number with real and imaginary part"
+
[06d793bf-73bd-4b02-b015-3030b2c952ec]
description = "Operations between real numbers and complex numbers -> Add real number to complex number"
diff --git a/exercises/practice/complex-numbers/complex_numbers_test.py b/exercises/practice/complex-numbers/complex_numbers_test.py
index 7580468d1d4..2eca7849697 100644
--- a/exercises/practice/complex-numbers/complex_numbers_test.py
+++ b/exercises/practice/complex-numbers/complex_numbers_test.py
@@ -148,6 +148,11 @@ def test_exponential_of_a_number_with_real_and_imaginary_part(self):
ComplexNumber(math.log(2), math.pi).exp(), ComplexNumber(-2, 0)
)
+ def test_exponential_resulting_in_a_number_with_real_and_imaginary_part(self):
+ self.assertAlmostEqual(
+ ComplexNumber(math.log(2) / 2, math.pi / 4).exp(), ComplexNumber(1, 1)
+ )
+
# Operations between real numbers and complex numbers
def test_add_real_number_to_complex_number(self):
From 8d5600de97a79c3c0ce4144a9517f7fa66eda137 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 19:43:44 +0100
Subject: [PATCH 229/932] [Mantaince] Updated dominoes_test
---
exercises/practice/dominoes/.meta/tests.toml | 16 +++++++++++++---
exercises/practice/dominoes/dominoes_test.py | 5 +++++
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/dominoes/.meta/tests.toml b/exercises/practice/dominoes/.meta/tests.toml
index 23ff84f906b..08c8e08d02b 100644
--- a/exercises/practice/dominoes/.meta/tests.toml
+++ b/exercises/practice/dominoes/.meta/tests.toml
@@ -1,6 +1,13 @@
-# 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.
[31a673f2-5e54-49fe-bd79-1c1dae476c9c]
description = "empty input = empty output"
@@ -37,3 +44,6 @@ description = "separate loops"
[cd061538-6046-45a7-ace9-6708fe8f6504]
description = "nine elements"
+
+[44704c7c-3adb-4d98-bd30-f45527cf8b49]
+description = "separate three-domino loops"
diff --git a/exercises/practice/dominoes/dominoes_test.py b/exercises/practice/dominoes/dominoes_test.py
index 3f0b289908f..a62f130cc20 100644
--- a/exercises/practice/dominoes/dominoes_test.py
+++ b/exercises/practice/dominoes/dominoes_test.py
@@ -78,6 +78,11 @@ def test_nine_elements(self):
output_chain = can_chain(input_dominoes)
self.assert_correct_chain(input_dominoes, output_chain)
+ def test_separate_three_domino_loops(self):
+ input_dominoes = [(1, 2), (2, 3), (3, 1), (4, 5), (5, 6), (6, 4)]
+ output_chain = can_chain(input_dominoes)
+ self.refute_correct_chain(input_dominoes, output_chain)
+
# Utility methods
def normalize_dominoes(self, dominoes):
From ee283023b51ffac586a3cd475c6c2e4c08025575 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 17:26:31 +0100
Subject: [PATCH 230/932] update_giga_second
---
exercises/practice/gigasecond/.meta/tests.toml | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/gigasecond/.meta/tests.toml b/exercises/practice/gigasecond/.meta/tests.toml
index 18672327f30..a7caf00dbc9 100644
--- a/exercises/practice/gigasecond/.meta/tests.toml
+++ b/exercises/practice/gigasecond/.meta/tests.toml
@@ -1,6 +1,13 @@
-# 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.
[92fbe71c-ea52-4fac-bd77-be38023cacf7]
description = "date only specification of time"
@@ -16,3 +23,6 @@ description = "full time specified"
[09d4e30e-728a-4b52-9005-be44a58d9eba]
description = "full time with day roll-over"
+
+[fcec307c-7529-49ab-b0fe-20309197618a]
+description = "does not mutate the input"
From dbd81f0205b8a411854546feab4c28b1efb085a4 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 20:05:50 +0100
Subject: [PATCH 231/932] [Mantaince] Updated gigasecond_test
---
exercises/practice/gigasecond/.meta/tests.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/gigasecond/.meta/tests.toml b/exercises/practice/gigasecond/.meta/tests.toml
index a7caf00dbc9..ef84c8666bd 100644
--- a/exercises/practice/gigasecond/.meta/tests.toml
+++ b/exercises/practice/gigasecond/.meta/tests.toml
@@ -26,3 +26,4 @@ description = "full time with day roll-over"
[fcec307c-7529-49ab-b0fe-20309197618a]
description = "does not mutate the input"
+include = false
\ No newline at end of file
From caa255acca787f093842d085a36331f96bfcafd8 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Fri, 2 Dec 2022 21:32:04 +0100
Subject: [PATCH 232/932] Update config.json
---
exercises/concept/tisbury-treasure-hunt/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/tisbury-treasure-hunt/.meta/config.json b/exercises/concept/tisbury-treasure-hunt/.meta/config.json
index e806bfb8463..3a8c476fe9f 100644
--- a/exercises/concept/tisbury-treasure-hunt/.meta/config.json
+++ b/exercises/concept/tisbury-treasure-hunt/.meta/config.json
@@ -1,6 +1,6 @@
{
"blurb": "Learn about tuples by helping out competitors in the Tisbury Treasure Hunt.",
- "icon": "proverb",
+ "icon": "tisbury-treasure-hunt",
"authors": ["bethanyg"],
"files": {
"solution": ["tuples.py"],
From add5e7c824149691d0a930f8ac788cf74c59ae6c Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 29 Nov 2022 22:17:55 +0100
Subject: [PATCH 233/932] fixes
---
exercises/concept/locomotive-engineer/.docs/instructions.md | 4 ++--
exercises/concept/locomotive-engineer/.meta/config.json | 1 +
exercises/concept/locomotive-engineer/locomotive_engineer.py | 4 ++--
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/exercises/concept/locomotive-engineer/.docs/instructions.md b/exercises/concept/locomotive-engineer/.docs/instructions.md
index c81cedcff70..b24ae484ec1 100644
--- a/exercises/concept/locomotive-engineer/.docs/instructions.md
+++ b/exercises/concept/locomotive-engineer/.docs/instructions.md
@@ -19,7 +19,7 @@ The function should then `return` the given IDs as a single `list`.
```python
>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
-[1, 7, 12, 3, 14, 8, 3]
+[1, 7, 12, 3, 14, 8, 5]
```
## 2. Fix the list of wagons
@@ -41,7 +41,7 @@ The function should then `return` a `list` with the modifications.
```python
>>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15])
-[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5]
+[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5]
```
## 3. Add missing stops
diff --git a/exercises/concept/locomotive-engineer/.meta/config.json b/exercises/concept/locomotive-engineer/.meta/config.json
index 86a8a8b6a35..4c10c882631 100644
--- a/exercises/concept/locomotive-engineer/.meta/config.json
+++ b/exercises/concept/locomotive-engineer/.meta/config.json
@@ -2,6 +2,7 @@
"blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system.",
"icon": "tracks-on-tracks-on-tracks",
"authors": ["meatball133","BethanyG"],
+ "contributors": ["IsaacG"],
"files": {
"solution": ["locomotive_engineer.py"],
"test": ["locomotive_engineer_test.py"],
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer.py b/exercises/concept/locomotive-engineer/locomotive_engineer.py
index 4166a24b684..d9291f65a32 100644
--- a/exercises/concept/locomotive-engineer/locomotive_engineer.py
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer.py
@@ -48,7 +48,7 @@ def extend_route_information(route, more_route_information):
def fix_wagon_depot(wagons_rows):
"""Fix the list of rows of wagons.
- :param wagons_rows: list[tuple] - the list of rows of wagons.
- :return: list[tuple] - list of rows of wagons.
+ :param wagons_rows: list[list[tuple]] - the list of rows of wagons.
+ :return: list[list[tuple]] - list of rows of wagons.
"""
pass
From 5b36121729c722674628235e17f65bc5cd02266c Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 29 Nov 2022 22:21:52 +0100
Subject: [PATCH 234/932] Update instructions.md
---
.../locomotive-engineer/.docs/instructions.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/exercises/concept/locomotive-engineer/.docs/instructions.md b/exercises/concept/locomotive-engineer/.docs/instructions.md
index b24ae484ec1..af914e4924f 100644
--- a/exercises/concept/locomotive-engineer/.docs/instructions.md
+++ b/exercises/concept/locomotive-engineer/.docs/instructions.md
@@ -102,14 +102,14 @@ Your function should return a `list` with the three "row" `lists` reordered to h
```python
>>> fix_wagon_depot([
- [(2, "red"), (4, "red"),(8, "red")],
- [(5, "blue"),(9, "blue"),(13,"blue")],
- [(3, "orange"),(7, "orange"), (11, "orange")],
+ [(2, "red"), (4, "red"), (8, "red")],
+ [(5, "blue"), (9, "blue"), (13,"blue")],
+ [(3, "orange"), (7, "orange"), (11, "orange")],
])
[
-[(2, "red"),(5, "blue"),(3, "orange")],
-[(4, "red"),(9, "blue"),(7, "orange")],
-[(8, "red"),(13,"blue"),(11, "orange")]
+[(2, "red"), (5, "blue"), (3, "orange")],
+[(4, "red"), (9, "blue"), (7, "orange")],
+[(8, "red"), (13,"blue"), (11, "orange")]
]
```
From 23ab8d8b528fdcda3bb61c1984ab3b08375bdd55 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 29 Nov 2022 22:24:06 +0100
Subject: [PATCH 235/932] Update about.md
---
concepts/unpacking-and-multiple-assignment/about.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index 5623b2f034d..e8abaa528d7 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -259,7 +259,7 @@ Usage of `*args`:
# This function is defined to take any number of positional arguments
>>> def my_function(*args):
-... print(args)
+... print(args)
# Arguments given to the function are packed into a tuple
@@ -328,8 +328,8 @@ If you don't follow this order then you will get an error.
Writing arguments in an incorrect order will result in an error:
```python
->>>def my_function(*args, a, b):
-... print(args)
+>>> def my_function(*args, a, b):
+... print(args)
>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
From db28e5829170afab872b5e6af5efc70415733107 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 11:47:54 +0100
Subject: [PATCH 236/932] Added resistor_color_trio_test
---
config.json | 14 +++++
.../resistor-color-trio/.docs/instructions.md | 54 +++++++++++++++++++
.../resistor-color-trio/.meta/config.json | 22 ++++++++
.../resistor-color-trio/.meta/example.py | 18 +++++++
.../resistor-color-trio/.meta/template.j2 | 15 ++++++
.../resistor-color-trio/.meta/tests.toml | 25 +++++++++
.../resistor_color_trio.py | 2 +
.../resistor_color_trio_test.py | 24 +++++++++
8 files changed, 174 insertions(+)
create mode 100644 exercises/practice/resistor-color-trio/.docs/instructions.md
create mode 100644 exercises/practice/resistor-color-trio/.meta/config.json
create mode 100644 exercises/practice/resistor-color-trio/.meta/example.py
create mode 100644 exercises/practice/resistor-color-trio/.meta/template.j2
create mode 100644 exercises/practice/resistor-color-trio/.meta/tests.toml
create mode 100644 exercises/practice/resistor-color-trio/resistor_color_trio.py
create mode 100644 exercises/practice/resistor-color-trio/resistor_color_trio_test.py
diff --git a/config.json b/config.json
index 7350c2d7c86..8735e247642 100644
--- a/config.json
+++ b/config.json
@@ -682,6 +682,20 @@
],
"difficulty": 2
},
+ {
+ "slug": "resistor-color-trio",
+ "name": "Resistor Color Trio",
+ "uuid": "089f06a6-0759-479c-8c00-d699525a1e22",
+ "practices": ["list-methods"],
+ "prerequisites": [
+ "basics",
+ "bools",
+ "lists",
+ "list-methods",
+ "numbers"
+ ],
+ "difficulty": 2
+ },
{
"slug": "twelve-days",
"name": "Twelve Days",
diff --git a/exercises/practice/resistor-color-trio/.docs/instructions.md b/exercises/practice/resistor-color-trio/.docs/instructions.md
new file mode 100644
index 00000000000..fcc76958a5f
--- /dev/null
+++ b/exercises/practice/resistor-color-trio/.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-trio/.meta/config.json b/exercises/practice/resistor-color-trio/.meta/config.json
new file mode 100644
index 00000000000..9dfb34d6baf
--- /dev/null
+++ b/exercises/practice/resistor-color-trio/.meta/config.json
@@ -0,0 +1,22 @@
+{
+ "blurb": "Convert color codes, as used on resistors, to a human-readable label.",
+ "authors": [
+ "meabtall133",
+ "bethanyg"
+ ],
+ "contributors": [
+ ],
+ "files": {
+ "solution": [
+ "resistor_color_trio.py"
+ ],
+ "test": [
+ "resistor_color_trio_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "source": "Maud de Vries, Erik Schierboom",
+ "source_url": "https://github.com/exercism/problem-specifications/issues/1549"
+}
diff --git a/exercises/practice/resistor-color-trio/.meta/example.py b/exercises/practice/resistor-color-trio/.meta/example.py
new file mode 100644
index 00000000000..a6588ea5a6d
--- /dev/null
+++ b/exercises/practice/resistor-color-trio/.meta/example.py
@@ -0,0 +1,18 @@
+COLORS = [
+ 'black',
+ 'brown',
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'blue',
+ 'violet',
+ 'grey',
+ 'white'
+]
+
+
+def label(colors):
+ value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
+ value *= 10 ** COLORS.index(colors[2])
+ return str(value) + ' ohms' if value < 1000 else str(value // 1000) + ' kiloohms'
diff --git a/exercises/practice/resistor-color-trio/.meta/template.j2 b/exercises/practice/resistor-color-trio/.meta/template.j2
new file mode 100644
index 00000000000..54814bed31d
--- /dev/null
+++ b/exercises/practice/resistor-color-trio/.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']}}"
+ )
+{%- 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-trio/.meta/tests.toml b/exercises/practice/resistor-color-trio/.meta/tests.toml
new file mode 100644
index 00000000000..dc6077e54fc
--- /dev/null
+++ b/exercises/practice/resistor-color-trio/.meta/tests.toml
@@ -0,0 +1,25 @@
+# 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.
+
+[d6863355-15b7-40bb-abe0-bfb1a25512ed]
+description = "Orange and orange and black"
+
+[1224a3a9-8c8e-4032-843a-5224e04647d6]
+description = "Blue and grey and brown"
+
+[b8bda7dc-6b95-4539-abb2-2ad51d66a207]
+description = "Red and black and red"
+
+[5b1e74bc-d838-4eda-bbb3-eaba988e733b]
+description = "Green and brown and orange"
+
+[f5d37ef9-1919-4719-a90d-a33c5a6934c9]
+description = "Yellow and violet and yellow"
diff --git a/exercises/practice/resistor-color-trio/resistor_color_trio.py b/exercises/practice/resistor-color-trio/resistor_color_trio.py
new file mode 100644
index 00000000000..1d36841cf10
--- /dev/null
+++ b/exercises/practice/resistor-color-trio/resistor_color_trio.py
@@ -0,0 +1,2 @@
+def label(colors):
+ pass
diff --git a/exercises/practice/resistor-color-trio/resistor_color_trio_test.py b/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
new file mode 100644
index 00000000000..302df179efb
--- /dev/null
+++ b/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
@@ -0,0 +1,24 @@
+import unittest
+
+from resistor_color_trio import (
+ label,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class ResistorColorTrioTest(unittest.TestCase):
+ def test_orange_and_orange_and_black(self):
+ self.assertEqual(label(["orange", "orange", "black"]), "33 ohms")
+
+ def test_blue_and_grey_and_brown(self):
+ self.assertEqual(label(["blue", "grey", "brown"]), "680 ohms")
+
+ def test_red_and_black_and_red(self):
+ self.assertEqual(label(["red", "black", "red"]), "2 kiloohms")
+
+ def test_green_and_brown_and_orange(self):
+ self.assertEqual(label(["green", "brown", "orange"]), "51 kiloohms")
+
+ def test_yellow_and_violet_and_yellow(self):
+ self.assertEqual(label(["yellow", "violet", "yellow"]), "470 kiloohms")
From 9a4e2b6cf63d4a2db5a03c8901e4bb6e72712784 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 11:49:11 +0100
Subject: [PATCH 237/932] Updated the config
---
config.json | 29 +++++++++++++++--------------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/config.json b/config.json
index 8735e247642..5b5c101935a 100644
--- a/config.json
+++ b/config.json
@@ -624,6 +624,21 @@
],
"difficulty": 2
},
+ {
+ "slug": "resistor-color-trio",
+ "name": "Resistor Color Trio",
+ "uuid": "089f06a6-0759-479c-8c00-d699525a1e22",
+ "practices": ["list-methods"],
+ "prerequisites": [
+ "basics",
+ "bools",
+ "lists",
+ "numbers",
+ "strings",
+ "comparisons"
+ ],
+ "difficulty": 2
+ },
{
"slug": "word-count",
"name": "Word Count",
@@ -682,20 +697,6 @@
],
"difficulty": 2
},
- {
- "slug": "resistor-color-trio",
- "name": "Resistor Color Trio",
- "uuid": "089f06a6-0759-479c-8c00-d699525a1e22",
- "practices": ["list-methods"],
- "prerequisites": [
- "basics",
- "bools",
- "lists",
- "list-methods",
- "numbers"
- ],
- "difficulty": 2
- },
{
"slug": "twelve-days",
"name": "Twelve Days",
From 9ea00e63c8ceccd913d7f39b2476cc76f8e46ee2 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 13:01:21 +0100
Subject: [PATCH 238/932] Fix uuid
---
config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config.json b/config.json
index 5b5c101935a..df8633bb8f3 100644
--- a/config.json
+++ b/config.json
@@ -627,7 +627,7 @@
{
"slug": "resistor-color-trio",
"name": "Resistor Color Trio",
- "uuid": "089f06a6-0759-479c-8c00-d699525a1e22",
+ "uuid": "f987a5b7-96f2-49c2-99e8-aef30d23dd58",
"practices": ["list-methods"],
"prerequisites": [
"basics",
From 37178d980eb244d2856b9f8ec83e93252caf3d28 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sat, 3 Dec 2022 21:54:23 +0100
Subject: [PATCH 239/932] fix
---
exercises/practice/resistor-color-trio/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/resistor-color-trio/.meta/config.json b/exercises/practice/resistor-color-trio/.meta/config.json
index 9dfb34d6baf..600b6cc227f 100644
--- a/exercises/practice/resistor-color-trio/.meta/config.json
+++ b/exercises/practice/resistor-color-trio/.meta/config.json
@@ -1,7 +1,7 @@
{
"blurb": "Convert color codes, as used on resistors, to a human-readable label.",
"authors": [
- "meabtall133",
+ "meatball133",
"bethanyg"
],
"contributors": [
From a0385ee2d353a518f17dcba5a629bf954eda25c4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Mon, 28 Nov 2022 07:24:50 -0600
Subject: [PATCH 240/932] Create config.json
---
exercises/practice/wordy/.approaches/config.json | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 exercises/practice/wordy/.approaches/config.json
diff --git a/exercises/practice/wordy/.approaches/config.json b/exercises/practice/wordy/.approaches/config.json
new file mode 100644
index 00000000000..ed71a84650f
--- /dev/null
+++ b/exercises/practice/wordy/.approaches/config.json
@@ -0,0 +1,15 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "d643e2b4-daee-422d-b8d3-2cad2f439db5",
+ "slug": "dunder-getattribute",
+ "title": "dunder with __getattribute__",
+ "blurb": "Use dunder methods with __getattribute__.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 00535db967ac68a6dcc9b8266b5fbae34d40b9de Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Mon, 28 Nov 2022 07:56:55 -0600
Subject: [PATCH 241/932] Create introduction.md
---
.../wordy/.approaches/introduction.md | 56 +++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 exercises/practice/wordy/.approaches/introduction.md
diff --git a/exercises/practice/wordy/.approaches/introduction.md b/exercises/practice/wordy/.approaches/introduction.md
new file mode 100644
index 00000000000..7dcb96cb1ed
--- /dev/null
+++ b/exercises/practice/wordy/.approaches/introduction.md
@@ -0,0 +1,56 @@
+# Introduction
+
+There are various ways to solve Wordy.
+Using [`eval`][eval] is a [convenient but dangerous][eval-danger] approach.
+Another approach could replace the operation words with [dunder][dunder] methods.
+They are called "dunder" methods because they have **d**ouble **under**scores at the beginning and end of the method name.
+They are also called magic methods.
+The dunder methods can be called by using the [`__getattribute__`][getattribute] method for [`int`][int].
+
+## General guidance
+
+Parsing should verify that the expression in words can be translated to a valid mathematical expression.
+
+## Approach: Dunder methods with `__getattribute__`
+
+```python
+OPS = {
+ "plus": "__add__",
+ "minus": "__sub__",
+ "multiplied by": "__mul__",
+ "divided by": "__truediv__"
+}
+
+
+def answer(question):
+ question = question.removeprefix("What is").removesuffix("?").strip()
+ if not question: raise ValueError("syntax error")
+ if question.isdigit(): return int(question)
+
+ foundOp = False
+ for name, op in OPS.items():
+ if name in question:
+ question = question.replace(name, op)
+ foundOp = True
+ if not foundOp: raise ValueError("unknown operation")
+
+ ret = question.split()
+ while len(ret) > 1:
+ try:
+ x, op, y, *tail = ret
+ if op not in OPS.values(): raise ValueError("syntax error")
+ ret = [int(x).__getattribute__(op)(int(y)), *tail]
+ except:
+ raise ValueError("syntax error")
+ return ret[0]
+
+```
+
+For more information, check the [dunder method with `__getattribute__` approach][approach-dunder-getattribute].
+
+[eval]: https://docs.python.org/3/library/functions.html?#eval
+[eval-danger]: https://diveintopython3.net/advanced-iterators.html#eval
+[dunder]: https://www.tutorialsteacher.com/python/magic-methods-in-python
+[getattribute]: https://docs.python.org/3/reference/datamodel.html?#object.__getattribute__
+[int]: https://docs.python.org/3/library/functions.html?#int
+[approach-dunder-getattribute]: https://exercism.org/tracks/python/exercises/wordy/approaches/dunder-getattribute
From b75a3350e39ccba3bda963eef0b89d8626b28c79 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Mon, 28 Nov 2022 07:59:34 -0600
Subject: [PATCH 242/932] Create snippet.md
---
.../wordy/.approaches/dunder-getattribute/snippet.md | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 exercises/practice/wordy/.approaches/dunder-getattribute/snippet.md
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.md b/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.md
new file mode 100644
index 00000000000..c3aeda2fe98
--- /dev/null
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.md
@@ -0,0 +1,10 @@
+```python
+while len(ret) > 1:
+ try:
+ x, op, y, *tail = ret
+ if op not in OPS.values(): raise ValueError("syntax error")
+ ret = [int(x).__getattribute__(op)(int(y)), *tail]
+ except:
+ raise ValueError("syntax error")
+return ret[0]
+```
From f93f8867a7d1fb673ab60bb01b79456527444fa4 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Mon, 28 Nov 2022 09:03:23 -0600
Subject: [PATCH 243/932] Update introduction.md
---
exercises/practice/wordy/.approaches/introduction.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/wordy/.approaches/introduction.md b/exercises/practice/wordy/.approaches/introduction.md
index 7dcb96cb1ed..99a5d5775f6 100644
--- a/exercises/practice/wordy/.approaches/introduction.md
+++ b/exercises/practice/wordy/.approaches/introduction.md
@@ -1,10 +1,14 @@
# Introduction
There are various ways to solve Wordy.
-Using [`eval`][eval] is a [convenient but dangerous][eval-danger] approach.
+Using [`eval`][eval] is a [convenient but potentially dangerous][eval-danger] approach.
Another approach could replace the operation words with [dunder][dunder] methods.
+
+```exercism/note
They are called "dunder" methods because they have **d**ouble **under**scores at the beginning and end of the method name.
They are also called magic methods.
+```
+
The dunder methods can be called by using the [`__getattribute__`][getattribute] method for [`int`][int].
## General guidance
From f5b593d8633b07dc310f0deeb4b2cec3c32bd448 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Mon, 28 Nov 2022 09:18:42 -0600
Subject: [PATCH 244/932] Update introduction.md
---
exercises/practice/wordy/.approaches/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/wordy/.approaches/introduction.md b/exercises/practice/wordy/.approaches/introduction.md
index 99a5d5775f6..32d6a92038b 100644
--- a/exercises/practice/wordy/.approaches/introduction.md
+++ b/exercises/practice/wordy/.approaches/introduction.md
@@ -56,5 +56,5 @@ For more information, check the [dunder method with `__getattribute__` approach]
[eval-danger]: https://diveintopython3.net/advanced-iterators.html#eval
[dunder]: https://www.tutorialsteacher.com/python/magic-methods-in-python
[getattribute]: https://docs.python.org/3/reference/datamodel.html?#object.__getattribute__
-[int]: https://docs.python.org/3/library/functions.html?#int
+[int]: https://docs.python.org/3/library/stdtypes.html#typesnumeric
[approach-dunder-getattribute]: https://exercism.org/tracks/python/exercises/wordy/approaches/dunder-getattribute
From 98ba0439f6801f4dd7f000808122a706a1e0a5eb Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Mon, 28 Nov 2022 09:32:12 -0600
Subject: [PATCH 245/932] Create content.md
Not finished. Saving work...
---
.../dunder-getattribute/content.md | 82 +++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 exercises/practice/wordy/.approaches/dunder-getattribute/content.md
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
new file mode 100644
index 00000000000..70df27c450a
--- /dev/null
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
@@ -0,0 +1,82 @@
+# Dunder methods with `__getattribute__`
+
+```python
+OPS = {
+ "plus": "__add__",
+ "minus": "__sub__",
+ "multiplied by": "__mul__",
+ "divided by": "__truediv__"
+}
+
+
+def answer(question):
+ question = question.removeprefix("What is").removesuffix("?").strip()
+ if not question: raise ValueError("syntax error")
+ if question.isdigit(): return int(question)
+
+ foundOp = False
+ for name, op in OPS.items():
+ if name in question:
+ question = question.replace(name, op)
+ foundOp = True
+ if not foundOp: raise ValueError("unknown operation")
+
+ ret = question.split()
+ while len(ret) > 1:
+ try:
+ x, op, y, *tail = ret
+ if op not in OPS.values(): raise ValueError("syntax error")
+ ret = [int(x).__getattribute__(op)(int(y)), *tail]
+ except:
+ raise ValueError("syntax error")
+ return ret[0]
+
+```
+
+This approach begins by defining a [dictionary][dictionaries] of the word keys with their related [dunder][dunder] methods.
+
+```exercism/note
+They are called "dunder" methods because they have **d**ouble **under**scores at the beginning and end of the method name.
+They are also called magic methods.
+```
+
+Since only whole numbers are involved, the dunder methods are those for [`int`][int].
+The supported methods for `int` can be found by using `print(dir(int))`.
+The built-in [`dir`][dir] function returns a list of valid attributes for an object.
+
+Python doesn't _enforce_ having real constant values,
+but the `OPS` dictionary is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+
+The input question to the `answer` function is cleaned using the [`removeprefix`][removeprefix], [`removesuffix`][removesuffix], and [`strip`][strip] methods.
+If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the [`ValueError`][value-error].
+
+Next, the [`isdigit`][isdigit] method is used to see if all of the remaining characters in the input are digits.
+If so, it uses the [`int()`][int-constructor] constructor to return the string as an integer.
+
+Next, the elements in the `OPS` dictionary are iterated.
+If the key name is in the input, then the [`replace()`][replace] method is used to replace the name in the input with the dunder method value.
+If none of the key names are found in the input, then a `ValueError` is returned for having an unknown operation.
+
+At this point the input question is [`split()`][split] into a list of its separate words, which is then iterated.
+
+The dunder methods can be called by using the [`__getattribute__`][getattribute] method for [`int`][int].
+
+[dictionaries]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
+[dunder]: https://www.tutorialsteacher.com/python/magic-methods-in-python
+[int]: https://docs.python.org/3/library/stdtypes.html#typesnumeric
+[dir]: https://docs.python.org/3/library/functions.html?#dir
+[const]: https://realpython.com/python-constants/
+[removeprefix]: https://docs.python.org/3/library/stdtypes.html#str.removeprefix
+[removesuffix]: https://docs.python.org/3/library/stdtypes.html#str.removesuffix
+[strip]: https://docs.python.org/3/library/stdtypes.html#str.strip
+[not]: https://docs.python.org/3/library/operator.html?#operator.__not__
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[value-error]: https://docs.python.org/3/library/exceptions.html?#ValueError
+[isdigit]: https://docs.python.org/3/library/stdtypes.html?#str.isdigit
+[int-constructor]: https://docs.python.org/3/library/functions.html?#int
+[replace]: https://docs.python.org/3/library/stdtypes.html?#str.replace
+[split]: https://docs.python.org/3/library/stdtypes.html?#str.split
+
+[getattribute]: https://docs.python.org/3/reference/datamodel.html?#object.__getattribute__
+
From ae80f0de25607ec2b8c8390a0e136a0827067c78 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Mon, 28 Nov 2022 10:58:23 -0600
Subject: [PATCH 246/932] Update content.md
---
.../dunder-getattribute/content.md | 33 +++++++++++++++----
1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
index 70df27c450a..7856b55e27f 100644
--- a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
@@ -42,14 +42,19 @@ They are also called magic methods.
Since only whole numbers are involved, the dunder methods are those for [`int`][int].
The supported methods for `int` can be found by using `print(dir(int))`.
-The built-in [`dir`][dir] function returns a list of valid attributes for an object.
+
+```exercism/note
+The built-in [`dir`](https://docs.python.org/3/library/functions.html?#dir) function returns a list of valid attributes for an object.
+```
Python doesn't _enforce_ having real constant values,
but the `OPS` dictionary is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
It indicates that the value is not intended to be changed.
The input question to the `answer` function is cleaned using the [`removeprefix`][removeprefix], [`removesuffix`][removesuffix], and [`strip`][strip] methods.
-If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the [`ValueError`][value-error].
+The method calls are [chained][method-chaining], so that the output from one call is the input for the next call.
+If the input has no characters left,
+it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the [`ValueError`][value-error] for having a syntax error.
Next, the [`isdigit`][isdigit] method is used to see if all of the remaining characters in the input are digits.
If so, it uses the [`int()`][int-constructor] constructor to return the string as an integer.
@@ -58,18 +63,31 @@ Next, the elements in the `OPS` dictionary are iterated.
If the key name is in the input, then the [`replace()`][replace] method is used to replace the name in the input with the dunder method value.
If none of the key names are found in the input, then a `ValueError` is returned for having an unknown operation.
-At this point the input question is [`split()`][split] into a list of its separate words, which is then iterated.
+At this point the input question is [`split()`][split] into a list of its words, which is then iterated while its [`len()`][len] is greater than 1.
-The dunder methods can be called by using the [`__getattribute__`][getattribute] method for [`int`][int].
+Within a [try][exception-handling], the list is [destructured][destructure] into `x, op, y, *tail`.
+If `op` is not in the supported dunder methods, it raises `ValueError("syntax error")`.
+If there are any other exceptions raised in the try, `except` raises `ValueError("syntax error")`
+
+Next, it converts `x` to an `int` and calls the [`__getattribute__`][getattribute] for its dunder method and calls it,
+passing it `y` converted to an `int`.
+
+It sets the list to the result of the dunder method plus the remaining elements in `*tail`.
+
+```exercism/note
+The `*` prefix in `*tail` [unpacks](https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/) the `tail` list back into its elements.
+```
+
+When the loop exhausts, the first element of the list is selected as the function return value.
[dictionaries]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
[dunder]: https://www.tutorialsteacher.com/python/magic-methods-in-python
[int]: https://docs.python.org/3/library/stdtypes.html#typesnumeric
-[dir]: https://docs.python.org/3/library/functions.html?#dir
[const]: https://realpython.com/python-constants/
[removeprefix]: https://docs.python.org/3/library/stdtypes.html#str.removeprefix
[removesuffix]: https://docs.python.org/3/library/stdtypes.html#str.removesuffix
[strip]: https://docs.python.org/3/library/stdtypes.html#str.strip
+[method-chaining]: https://www.tutorialspoint.com/Explain-Python-class-method-chaining
[not]: https://docs.python.org/3/library/operator.html?#operator.__not__
[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
[value-error]: https://docs.python.org/3/library/exceptions.html?#ValueError
@@ -77,6 +95,7 @@ The dunder methods can be called by using the [`__getattribute__`][getattribute]
[int-constructor]: https://docs.python.org/3/library/functions.html?#int
[replace]: https://docs.python.org/3/library/stdtypes.html?#str.replace
[split]: https://docs.python.org/3/library/stdtypes.html?#str.split
-
+[len]: https://docs.python.org/3/library/functions.html?#len
+[exception-handling]: https://docs.python.org/3/tutorial/errors.html#handling-exceptions
+[destructure]: https://riptutorial.com/python/example/14981/destructuring-assignment
[getattribute]: https://docs.python.org/3/reference/datamodel.html?#object.__getattribute__
-
From 997a55552e26d1858d000b434b8b48f090ddf3ae Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:38:04 -0600
Subject: [PATCH 247/932] Update and rename snippet.md to snippet.txt
---
.../.approaches/dunder-getattribute/{snippet.md => snippet.txt} | 2 --
1 file changed, 2 deletions(-)
rename exercises/practice/wordy/.approaches/dunder-getattribute/{snippet.md => snippet.txt} (94%)
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.md b/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.txt
similarity index 94%
rename from exercises/practice/wordy/.approaches/dunder-getattribute/snippet.md
rename to exercises/practice/wordy/.approaches/dunder-getattribute/snippet.txt
index c3aeda2fe98..d3cc3d16701 100644
--- a/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.md
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.txt
@@ -1,4 +1,3 @@
-```python
while len(ret) > 1:
try:
x, op, y, *tail = ret
@@ -7,4 +6,3 @@ while len(ret) > 1:
except:
raise ValueError("syntax error")
return ret[0]
-```
From 3bd743d72cc5d64f8fe46d59aef9c1290e1ecf39 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 3 Dec 2022 13:37:25 -0800
Subject: [PATCH 248/932] Apply suggestions from code review
---
.../wordy/.approaches/dunder-getattribute/content.md | 6 +++---
exercises/practice/wordy/.approaches/introduction.md | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
index 7856b55e27f..ed156c6ed57 100644
--- a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
@@ -14,12 +14,12 @@ def answer(question):
if not question: raise ValueError("syntax error")
if question.isdigit(): return int(question)
- foundOp = False
+ found_op = False
for name, op in OPS.items():
if name in question:
question = question.replace(name, op)
- foundOp = True
- if not foundOp: raise ValueError("unknown operation")
+ found_op = True
+ if not found_op: raise ValueError("unknown operation")
ret = question.split()
while len(ret) > 1:
diff --git a/exercises/practice/wordy/.approaches/introduction.md b/exercises/practice/wordy/.approaches/introduction.md
index 32d6a92038b..c8a5feb6f37 100644
--- a/exercises/practice/wordy/.approaches/introduction.md
+++ b/exercises/practice/wordy/.approaches/introduction.md
@@ -31,12 +31,12 @@ def answer(question):
if not question: raise ValueError("syntax error")
if question.isdigit(): return int(question)
- foundOp = False
+ found_op = False
for name, op in OPS.items():
if name in question:
question = question.replace(name, op)
- foundOp = True
- if not foundOp: raise ValueError("unknown operation")
+ found_op = True
+ if not found_op: raise ValueError("unknown operation")
ret = question.split()
while len(ret) > 1:
From 6182702de12443ed2a0ff375fa08f28b2f3ae3b5 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sat, 3 Dec 2022 22:51:18 +0100
Subject: [PATCH 249/932] Add link to concept
---
.../practice/wordy/.approaches/dunder-getattribute/content.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
index ed156c6ed57..f64bc6fac30 100644
--- a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
@@ -76,6 +76,7 @@ It sets the list to the result of the dunder method plus the remaining elements
```exercism/note
The `*` prefix in `*tail` [unpacks](https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/) the `tail` list back into its elements.
+This concept is also aparat of [unpacking-and-multiple-assignment](https://exercism.org/tracks/python/concepts/unpacking-and-multiple-assignment) concept in the syllabus..
```
When the loop exhausts, the first element of the list is selected as the function return value.
From bae4589faffb51543d1b96050fc2568051a44a84 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 11:22:27 +0100
Subject: [PATCH 250/932] Added bottle_song
---
config.json | 20 +++
.../bottle-song/.docs/instructions.md | 57 ++++++++
.../practice/bottle-song/.meta/config.json | 19 +++
.../practice/bottle-song/.meta/example.py | 34 +++++
.../practice/bottle-song/.meta/template.j2 | 23 +++
.../practice/bottle-song/.meta/tests.toml | 31 ++++
exercises/practice/bottle-song/bottle_song.py | 2 +
.../practice/bottle-song/bottle_song_test.py | 136 ++++++++++++++++++
8 files changed, 322 insertions(+)
create mode 100644 exercises/practice/bottle-song/.docs/instructions.md
create mode 100644 exercises/practice/bottle-song/.meta/config.json
create mode 100644 exercises/practice/bottle-song/.meta/example.py
create mode 100644 exercises/practice/bottle-song/.meta/template.j2
create mode 100644 exercises/practice/bottle-song/.meta/tests.toml
create mode 100644 exercises/practice/bottle-song/bottle_song.py
create mode 100644 exercises/practice/bottle-song/bottle_song_test.py
diff --git a/config.json b/config.json
index df8633bb8f3..912e9b6e3e2 100644
--- a/config.json
+++ b/config.json
@@ -2151,6 +2151,26 @@
"difficulty": 3,
"status": "deprecated"
},
+ {
+ "slug": "bottle-song",
+ "name": "Bottle Song",
+ "uuid": "70bec74a-0677-40c9-b9c9-cbcc49a2eae4",
+ "practices": ["generators"],
+ "prerequisites": [
+ "basics",
+ "conditionals",
+ "dicts",
+ "lists",
+ "list-methods",
+ "loops",
+ "numbers",
+ "strings",
+ "string-methods",
+ "tuples"
+ ],
+ "difficulty": 3,
+ "status": "deprecated"
+ },
{
"slug": "trinary",
"name": "Trinary",
diff --git a/exercises/practice/bottle-song/.docs/instructions.md b/exercises/practice/bottle-song/.docs/instructions.md
new file mode 100644
index 00000000000..febdfc86395
--- /dev/null
+++ b/exercises/practice/bottle-song/.docs/instructions.md
@@ -0,0 +1,57 @@
+# Instructions
+
+Recite the lyrics to that popular children's repetitive song: Ten Green Bottles.
+
+Note that not all verses are identical.
+
+```text
+Ten green bottles hanging on the wall,
+Ten green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be nine green bottles hanging on the wall.
+
+Nine green bottles hanging on the wall,
+Nine green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be eight green bottles hanging on the wall.
+
+Eight green bottles hanging on the wall,
+Eight green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be seven green bottles hanging on the wall.
+
+Seven green bottles hanging on the wall,
+Seven green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be six green bottles hanging on the wall.
+
+Six green bottles hanging on the wall,
+Six green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be five green bottles hanging on the wall.
+
+Five green bottles hanging on the wall,
+Five green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be four green bottles hanging on the wall.
+
+Four green bottles hanging on the wall,
+Four green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be three green bottles hanging on the wall.
+
+Three green bottles hanging on the wall,
+Three green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be two green bottles hanging on the wall.
+
+Two green bottles hanging on the wall,
+Two green bottles hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be one green bottle hanging on the wall.
+
+One green bottle hanging on the wall,
+One green bottle hanging on the wall,
+And if one green bottle should accidentally fall,
+There'll be no green bottles hanging on the wall.
+```
diff --git a/exercises/practice/bottle-song/.meta/config.json b/exercises/practice/bottle-song/.meta/config.json
new file mode 100644
index 00000000000..7845207fe59
--- /dev/null
+++ b/exercises/practice/bottle-song/.meta/config.json
@@ -0,0 +1,19 @@
+{
+ "blurb": "Produce the lyrics to the popular children's repetitive song: Ten Green Bottles.",
+ "authors": ["meatball133", "BethanyG"],
+ "contributors": [
+ ],
+ "files": {
+ "solution": [
+ "bottle_song.py"
+ ],
+ "test": [
+ "bottle_song_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "source": "Wikipedia",
+ "source_url": "https://en.wikipedia.org/wiki/Ten_Green_Bottles"
+}
diff --git a/exercises/practice/bottle-song/.meta/example.py b/exercises/practice/bottle-song/.meta/example.py
new file mode 100644
index 00000000000..18e1167da3d
--- /dev/null
+++ b/exercises/practice/bottle-song/.meta/example.py
@@ -0,0 +1,34 @@
+NUMBERS = {10: "ten", 9: "nine", 8: "eight", 7: "seven", 6: "six", 5: "five", 4: "four", 3: "three", 2: "two", 1: "one", 0: "no"}
+
+def recite(start, take=1):
+ results = []
+ for idx in range(start, start - take, -1):
+ results.extend(verse(idx))
+ if idx > start - take + 1:
+ results.append('')
+ return results
+
+
+def verse(number):
+ return [
+ *main_verse(number),
+ "And if one green bottle should accidentally fall,",
+ last_verse(number)
+ ]
+
+def main_verse(number):
+ if number == 1:
+ return [
+ f'One green bottle hanging on the wall,',
+ f'One green bottle hanging on the wall,',
+ ]
+ else:
+ return [
+ f'{NUMBERS[number].capitalize()} green bottles hanging on the wall,',
+ f'{NUMBERS[number].capitalize()} green bottles hanging on the wall,',]
+
+def last_verse(number):
+ if number -1 == 1:
+ return f"There'll be one green bottle hanging on the wall."
+ else:
+ return f"There'll be {NUMBERS[number-1]} green bottles hanging on the wall."
diff --git a/exercises/practice/bottle-song/.meta/template.j2 b/exercises/practice/bottle-song/.meta/template.j2
new file mode 100644
index 00000000000..37e45402da9
--- /dev/null
+++ b/exercises/practice/bottle-song/.meta/template.j2
@@ -0,0 +1,23 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{{ macros.header() }}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {% for subcase in case["cases"] -%}
+ {% for subsubcase in subcase["cases"] -%}
+ def test_{{ subsubcase["description"] | to_snake }}(self):
+ {% set start = subsubcase["input"]["startBottles"] -%}
+ {% set take = subsubcase["input"]["takeDown"] -%}
+ expected = {{ subsubcase["expected"] }}
+ {%- if take == 1 %}
+ self.assertEqual({{ subsubcase["property"] }}(start={{ start}}), expected)
+ {% else %}
+ self.assertEqual({{ subsubcase["property"] }}(start={{ start}}, take={{ take }}), expected)
+ {% endif %}
+
+ {% endfor %}
+ {% endfor %}
+ {% endfor %}
+
+
+{{ macros.footer() }}
diff --git a/exercises/practice/bottle-song/.meta/tests.toml b/exercises/practice/bottle-song/.meta/tests.toml
new file mode 100644
index 00000000000..1f6e40a37c7
--- /dev/null
+++ b/exercises/practice/bottle-song/.meta/tests.toml
@@ -0,0 +1,31 @@
+# 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.
+
+[d4ccf8fc-01dc-48c0-a201-4fbeb30f2d03]
+description = "verse -> single verse -> first generic verse"
+
+[0f0aded3-472a-4c64-b842-18d4f1f5f030]
+description = "verse -> single verse -> last generic verse"
+
+[f61f3c97-131f-459e-b40a-7428f3ed99d9]
+description = "verse -> single verse -> verse with 2 bottles"
+
+[05eadba9-5dbd-401e-a7e8-d17cc9baa8e0]
+description = "verse -> single verse -> verse with 1 bottle"
+
+[a4a28170-83d6-4dc1-bd8b-319b6abb6a80]
+description = "lyrics -> multiple verses -> first two verses"
+
+[3185d438-c5ac-4ce6-bcd3-02c9ff1ed8db]
+description = "lyrics -> multiple verses -> last three verses"
+
+[28c1584a-0e51-4b65-9ae2-fbc0bf4bbb28]
+description = "lyrics -> multiple verses -> all verses"
diff --git a/exercises/practice/bottle-song/bottle_song.py b/exercises/practice/bottle-song/bottle_song.py
new file mode 100644
index 00000000000..9a5e67fc8b4
--- /dev/null
+++ b/exercises/practice/bottle-song/bottle_song.py
@@ -0,0 +1,2 @@
+def recite(start, take=1):
+ pass
diff --git a/exercises/practice/bottle-song/bottle_song_test.py b/exercises/practice/bottle-song/bottle_song_test.py
new file mode 100644
index 00000000000..89877dc4947
--- /dev/null
+++ b/exercises/practice/bottle-song/bottle_song_test.py
@@ -0,0 +1,136 @@
+import unittest
+
+from bottle_song import (
+ recite,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class BottleSongTest(unittest.TestCase):
+ def test_first_generic_verse(self):
+ expected = [
+ "Ten green bottles hanging on the wall,",
+ "Ten green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be nine green bottles hanging on the wall.",
+ ]
+ self.assertEqual(recite(start=10), expected)
+
+ def test_last_generic_verse(self):
+ expected = [
+ "Three green bottles hanging on the wall,",
+ "Three green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be two green bottles hanging on the wall.",
+ ]
+ self.assertEqual(recite(start=3), expected)
+
+ def test_verse_with_2_bottles(self):
+ expected = [
+ "Two green bottles hanging on the wall,",
+ "Two green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be one green bottle hanging on the wall.",
+ ]
+ self.assertEqual(recite(start=2), expected)
+
+ def test_verse_with_1_bottle(self):
+ expected = [
+ "One green bottle hanging on the wall,",
+ "One green bottle hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be no green bottles hanging on the wall.",
+ ]
+ self.assertEqual(recite(start=1), expected)
+
+ def test_first_two_verses(self):
+ expected = [
+ "Ten green bottles hanging on the wall,",
+ "Ten green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be nine green bottles hanging on the wall.",
+ "",
+ "Nine green bottles hanging on the wall,",
+ "Nine green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be eight green bottles hanging on the wall.",
+ ]
+ self.assertEqual(recite(start=10, take=2), expected)
+
+ def test_last_three_verses(self):
+ expected = [
+ "Three green bottles hanging on the wall,",
+ "Three green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be two green bottles hanging on the wall.",
+ "",
+ "Two green bottles hanging on the wall,",
+ "Two green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be one green bottle hanging on the wall.",
+ "",
+ "One green bottle hanging on the wall,",
+ "One green bottle hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be no green bottles hanging on the wall.",
+ ]
+ self.assertEqual(recite(start=3, take=3), expected)
+
+ def test_all_verses(self):
+ expected = [
+ "Ten green bottles hanging on the wall,",
+ "Ten green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be nine green bottles hanging on the wall.",
+ "",
+ "Nine green bottles hanging on the wall,",
+ "Nine green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be eight green bottles hanging on the wall.",
+ "",
+ "Eight green bottles hanging on the wall,",
+ "Eight green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be seven green bottles hanging on the wall.",
+ "",
+ "Seven green bottles hanging on the wall,",
+ "Seven green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be six green bottles hanging on the wall.",
+ "",
+ "Six green bottles hanging on the wall,",
+ "Six green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be five green bottles hanging on the wall.",
+ "",
+ "Five green bottles hanging on the wall,",
+ "Five green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be four green bottles hanging on the wall.",
+ "",
+ "Four green bottles hanging on the wall,",
+ "Four green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be three green bottles hanging on the wall.",
+ "",
+ "Three green bottles hanging on the wall,",
+ "Three green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be two green bottles hanging on the wall.",
+ "",
+ "Two green bottles hanging on the wall,",
+ "Two green bottles hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be one green bottle hanging on the wall.",
+ "",
+ "One green bottle hanging on the wall,",
+ "One green bottle hanging on the wall,",
+ "And if one green bottle should accidentally fall,",
+ "There'll be no green bottles hanging on the wall.",
+ ]
+ self.assertEqual(recite(start=10, take=10), expected)
+
+
+if __name__ == "__main__":
+ unittest.main()
From 1579133bc28270d8d41dacc356a072a826247889 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 11:24:55 +0100
Subject: [PATCH 251/932] fxi config
---
config.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/config.json b/config.json
index 912e9b6e3e2..2c0e439bb7a 100644
--- a/config.json
+++ b/config.json
@@ -997,9 +997,9 @@
"difficulty": 3
},
{
- "slug": "beer-song",
- "name": "Beer Song",
- "uuid": "b7984882-65df-4993-a878-7872c776592a",
+ "slug": "bottle-song",
+ "name": "Bottle Song",
+ "uuid": "70bec74a-0677-40c9-b9c9-cbcc49a2eae4",
"practices": ["generators"],
"prerequisites": [
"basics",
@@ -2152,9 +2152,9 @@
"status": "deprecated"
},
{
- "slug": "bottle-song",
- "name": "Bottle Song",
- "uuid": "70bec74a-0677-40c9-b9c9-cbcc49a2eae4",
+ "slug": "beer-song",
+ "name": "Beer Song",
+ "uuid": "b7984882-65df-4993-a878-7872c776592a",
"practices": ["generators"],
"prerequisites": [
"basics",
From 746abfc27043f574f40deb888977fa8bbc86f2d2 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 11:29:54 +0100
Subject: [PATCH 252/932] fixes
---
config.json | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/config.json b/config.json
index 2c0e439bb7a..0df05d3736b 100644
--- a/config.json
+++ b/config.json
@@ -2155,19 +2155,8 @@
"slug": "beer-song",
"name": "Beer Song",
"uuid": "b7984882-65df-4993-a878-7872c776592a",
- "practices": ["generators"],
- "prerequisites": [
- "basics",
- "conditionals",
- "dicts",
- "lists",
- "list-methods",
- "loops",
- "numbers",
- "strings",
- "string-methods",
- "tuples"
- ],
+ "practices": [],
+ "prerequisites": [],
"difficulty": 3,
"status": "deprecated"
},
From 31e2c88a28a53834510ce31856a65f970292a51a Mon Sep 17 00:00:00 2001
From: Meatball
Date: Fri, 2 Dec 2022 23:10:21 +0100
Subject: [PATCH 253/932] started work
---
config.json | 19 ++++++++-------
.../practice/proverb/.docs/instructions.md | 6 +++--
exercises/practice/proverb/.meta/config.json | 2 +-
exercises/practice/proverb/.meta/template.j2 | 24 +++++++++++++++++++
4 files changed, 39 insertions(+), 12 deletions(-)
create mode 100644 exercises/practice/proverb/.meta/template.j2
diff --git a/config.json b/config.json
index 0df05d3736b..2d2ab01976f 100644
--- a/config.json
+++ b/config.json
@@ -653,6 +653,16 @@
],
"difficulty": 2
},
+ {
+ "slug": "proverb",
+ "name": "Proverb",
+ "uuid": "9fd94229-f974-45bb-97ea-8bfe484f6eb3",
+ "practices": ["unpacking-and-multiple-assignment"],
+ "prerequisites": ["dicts",
+ "unpacking-and-multiple-assignment"],
+ "difficulty": 2,
+ "status": "wip"
+ },
{
"slug": "yacht",
"name": "Yacht",
@@ -2061,15 +2071,6 @@
"difficulty": 2,
"status": "deprecated"
},
- {
- "slug": "proverb",
- "name": "Proverb",
- "uuid": "9fd94229-f974-45bb-97ea-8bfe484f6eb3",
- "practices": [],
- "prerequisites": [],
- "difficulty": 2,
- "status": "deprecated"
- },
{
"slug": "nucleotide-count",
"name": "Nucleotide Count",
diff --git a/exercises/practice/proverb/.docs/instructions.md b/exercises/practice/proverb/.docs/instructions.md
index cf3b4c8b283..f6fb859325f 100644
--- a/exercises/practice/proverb/.docs/instructions.md
+++ b/exercises/practice/proverb/.docs/instructions.md
@@ -2,7 +2,8 @@
For want of a horseshoe nail, a kingdom was lost, or so the saying goes.
-Given a list of inputs, generate the relevant proverb. For example, given the list `["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]`, you will output the full text of this proverbial rhyme:
+Given a list of inputs, generate the relevant proverb.
+For example, given the list `["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]`, you will output the full text of this proverbial rhyme:
```text
For want of a nail the shoe was lost.
@@ -14,4 +15,5 @@ For want of a battle the kingdom was lost.
And all for the want of a nail.
```
-Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content. No line of the output text should be a static, unchanging string; all should vary according to the input given.
+Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content.
+No line of the output text should be a static, unchanging string; all should vary according to the input given.
diff --git a/exercises/practice/proverb/.meta/config.json b/exercises/practice/proverb/.meta/config.json
index 690feff3c8d..6bebac6f248 100644
--- a/exercises/practice/proverb/.meta/config.json
+++ b/exercises/practice/proverb/.meta/config.json
@@ -26,5 +26,5 @@
]
},
"source": "Wikipedia",
- "source_url": "http://en.wikipedia.org/wiki/For_Want_of_a_Nail"
+ "source_url": "https://en.wikipedia.org/wiki/For_Want_of_a_Nail"
}
diff --git a/exercises/practice/proverb/.meta/template.j2 b/exercises/practice/proverb/.meta/template.j2
new file mode 100644
index 00000000000..e90fe713074
--- /dev/null
+++ b/exercises/practice/proverb/.meta/template.j2
@@ -0,0 +1,24 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{{ macros.header() }}
+{{ "# PLEASE TAKE NOTE: Expected result lists for these test cases use **implicit line joining.**" }}
+{{ "# A new line in a result list below **does not** always equal a new list element." }}
+{{ "# Check comma placement carefully!" }}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {# All test cases in this exercise are nested, so use two for loops -#}
+ {% for supercase in cases -%}{% for case in supercase["cases"] -%}
+ {% set input = case["input"] -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ {% if supercase["description"] == "verse" -%}
+ expected = [
+ {{ macros.linebreak(case["expected"][0]) }}
+ ]
+ {% else -%}
+ expected = [recite(n, n)[0] for n in range(
+ {{- input["startVerse"] }},
+ {{- input["endVerse"] + 1 }})]
+ {% endif -%}
+ self.assertEqual({{ case["property"]}}(
+ {{- input["startVerse"] }},
+ {{- input["endVerse"] }}), expected)
+ {% endfor %}{% endfor %}
From a1a0350b2d3dbfa603258da21d7f3525b0f25e5d Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 16:21:18 +0100
Subject: [PATCH 254/932] Added extra tests
---
.../proverb/.meta/additional_tests.json | 36 +++++
exercises/practice/proverb/.meta/config.json | 4 +-
exercises/practice/proverb/.meta/template.j2 | 46 +++---
exercises/practice/proverb/proverb_test.py | 139 +++++++++++++-----
4 files changed, 170 insertions(+), 55 deletions(-)
create mode 100644 exercises/practice/proverb/.meta/additional_tests.json
diff --git a/exercises/practice/proverb/.meta/additional_tests.json b/exercises/practice/proverb/.meta/additional_tests.json
new file mode 100644
index 00000000000..8a6a670577c
--- /dev/null
+++ b/exercises/practice/proverb/.meta/additional_tests.json
@@ -0,0 +1,36 @@
+{
+ "cases": [
+ {
+ "description": "sentence without lower bound",
+ "property": "proverb",
+ "input": {
+ "strings": ["nail"],
+ "extra": {"qualifier": "horseshoe"}
+ },
+ "expected": ["And all for the want of a horseshoe nail."]
+ },
+ {
+ "description": "sentence without upper bound",
+ "property": "proverb",
+ "input": {
+ "strings": [
+ "nail",
+ "shoe",
+ "horse",
+ "rider",
+ "message",
+ "battle",
+ "kingdom"
+ ],
+ "extra": {"qualifier": "horseshoe"}
+ },
+ "expected": ["For want of a nail the shoe was lost.",
+ "For want of a shoe the horse was lost.",
+ "For want of a horse the rider was lost.",
+ "For want of a rider the message was lost.",
+ "For want of a message the battle was lost.",
+ "For want of a battle the kingdom was lost.",
+ "And all for the want of a horseshoe nail."]
+ }
+ ]
+}
diff --git a/exercises/practice/proverb/.meta/config.json b/exercises/practice/proverb/.meta/config.json
index 6bebac6f248..d413b0455bc 100644
--- a/exercises/practice/proverb/.meta/config.json
+++ b/exercises/practice/proverb/.meta/config.json
@@ -1,7 +1,9 @@
{
"blurb": "For want of a horseshoe nail, a kingdom was lost, or so the saying goes. Output the full text of this proverbial rhyme.",
"authors": [
- "betegelse"
+ "betegelse",
+ "meatball133",
+ "BethanyG"
],
"contributors": [
"behrtam",
diff --git a/exercises/practice/proverb/.meta/template.j2 b/exercises/practice/proverb/.meta/template.j2
index e90fe713074..91def8fee26 100644
--- a/exercises/practice/proverb/.meta/template.j2
+++ b/exercises/practice/proverb/.meta/template.j2
@@ -1,24 +1,36 @@
{%- import "generator_macros.j2" as macros with context -%}
-{{ macros.header() }}
+{{ macros.header(imports=["proverb"]) }}
{{ "# PLEASE TAKE NOTE: Expected result lists for these test cases use **implicit line joining.**" }}
{{ "# A new line in a result list below **does not** always equal a new list element." }}
{{ "# Check comma placement carefully!" }}
class {{ exercise | camel_case }}Test(unittest.TestCase):
{# All test cases in this exercise are nested, so use two for loops -#}
- {% for supercase in cases -%}{% for case in supercase["cases"] -%}
- {% set input = case["input"] -%}
- def test_{{ case["description"] | to_snake }}(self):
- {% if supercase["description"] == "verse" -%}
- expected = [
- {{ macros.linebreak(case["expected"][0]) }}
- ]
- {% else -%}
- expected = [recite(n, n)[0] for n in range(
- {{- input["startVerse"] }},
- {{- input["endVerse"] + 1 }})]
- {% endif -%}
- self.assertEqual({{ case["property"]}}(
- {{- input["startVerse"] }},
- {{- input["endVerse"] }}), expected)
- {% endfor %}{% endfor %}
+ {%- macro test_case(case) -%}
+ {% set input = case["input"] -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ self.assertEqual(proverb(
+ {% if input["strings"]|length >= 1 %}
+ {% for item in input["strings"] %}
+ "{{ item }}",
+ {% endfor %}
+ {% else %}
+ ""
+ {% endif %}
+ {% if input["extra"] %}
+ qualifier="{{ input["extra"]["qualifier"] }}"
+ {% endif %}
+ ),
+ {{ case["expected"] }}
+ )
+ {%- endmacro -%}
+
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
+
+ # Track-specific tests
+ {% for cases in additional_cases -%}
+ {{ test_case(cases) }}
+ {% endfor %}
+
diff --git a/exercises/practice/proverb/proverb_test.py b/exercises/practice/proverb/proverb_test.py
index c57c71b905d..511cde36c14 100644
--- a/exercises/practice/proverb/proverb_test.py
+++ b/exercises/practice/proverb/proverb_test.py
@@ -1,52 +1,117 @@
import unittest
-from proverb import proverb
+from proverb import (
+ proverb,
+)
+# Tests adapted from `problem-specifications//canonical-data.json`
+# PLEASE TAKE NOTE: Expected result lists for these test cases use **implicit line joining.**
+# A new line in a result list below **does not** always equal a new list element.
+# Check comma placement carefully!
-# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
class ProverbTest(unittest.TestCase):
def test_zero_pieces(self):
- self.assertEqual(proverb([]), "")
+ self.assertEqual(proverb(""), [])
def test_one_piece(self):
- inputs = ["nail"]
- expected = "And all for the want of a nail."
- self.assertEqual(proverb(inputs), expected)
+ self.assertEqual(
+ proverb(
+ "nail",
+ ),
+ ["And all for the want of a nail."],
+ )
def test_two_pieces(self):
- inputs = ["nail", "shoe"]
- expected = "\n".join(["For want of a nail the shoe was lost.",
- "And all for the want of a nail."])
- self.assertEqual(proverb(inputs), expected)
+ self.assertEqual(
+ proverb(
+ "nail",
+ "shoe",
+ ),
+ [
+ "For want of a nail the shoe was lost.",
+ "And all for the want of a nail.",
+ ],
+ )
def test_three_pieces(self):
- inputs = ["nail", "shoe", "horse"]
- expected = "\n".join(["For want of a nail the shoe was lost.",
- "For want of a shoe the horse was lost.",
- "And all for the want of a nail."])
- self.assertEqual(proverb(inputs), expected)
+ self.assertEqual(
+ proverb(
+ "nail",
+ "shoe",
+ "horse",
+ ),
+ [
+ "For want of a nail the shoe was lost.",
+ "For want of a shoe the horse was lost.",
+ "And all for the want of a nail.",
+ ],
+ )
def test_full_proverb(self):
- inputs = ["nail", "shoe", "horse", "rider",
- "message", "battle", "kingdom"]
- expected = "\n".join(["For want of a nail the shoe was lost.",
- "For want of a shoe the horse was lost.",
- "For want of a horse the rider was lost.",
- "For want of a rider the message was lost.",
- "For want of a message the battle was lost.",
- "For want of a battle the kingdom was lost.",
- "And all for the want of a nail."])
- self.assertEqual(proverb(inputs), expected)
-
- def test_four_pieces_modernised(self):
- inputs = ["pin", "gun", "soldier", "battle"]
- expected = "\n".join(["For want of a pin the gun was lost.",
- "For want of a gun the soldier was lost.",
- "For want of a soldier the battle was lost.",
- "And all for the want of a pin."])
- self.assertEqual(proverb(inputs), expected)
-
-
-if __name__ == '__main__':
- unittest.main()
+ self.assertEqual(
+ proverb(
+ "nail",
+ "shoe",
+ "horse",
+ "rider",
+ "message",
+ "battle",
+ "kingdom",
+ ),
+ [
+ "For want of a nail the shoe was lost.",
+ "For want of a shoe the horse was lost.",
+ "For want of a horse the rider was lost.",
+ "For want of a rider the message was lost.",
+ "For want of a message the battle was lost.",
+ "For want of a battle the kingdom was lost.",
+ "And all for the want of a nail.",
+ ],
+ )
+
+ def test_four_pieces_modernized(self):
+ self.assertEqual(
+ proverb(
+ "pin",
+ "gun",
+ "soldier",
+ "battle",
+ ),
+ [
+ "For want of a pin the gun was lost.",
+ "For want of a gun the soldier was lost.",
+ "For want of a soldier the battle was lost.",
+ "And all for the want of a pin.",
+ ],
+ )
+
+ # Track-specific tests
+ def test_sentence_without_lower_bound(self):
+ self.assertEqual(
+ proverb("nail", qualifier="horseshoe"),
+ ["And all for the want of a horseshoe nail."],
+ )
+
+ def test_sentence_without_upper_bound(self):
+ self.assertEqual(
+ proverb(
+ "nail",
+ "shoe",
+ "horse",
+ "rider",
+ "message",
+ "battle",
+ "kingdom",
+ qualifier="horseshoe",
+ ),
+ [
+ "For want of a nail the shoe was lost.",
+ "For want of a shoe the horse was lost.",
+ "For want of a horse the rider was lost.",
+ "For want of a rider the message was lost.",
+ "For want of a message the battle was lost.",
+ "For want of a battle the kingdom was lost.",
+ "And all for the want of a horseshoe nail.",
+ ],
+ )
From 022cf760cc0a0786caffba553575993c459515d4 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 17:28:16 +0100
Subject: [PATCH 255/932] Added example
---
exercises/practice/proverb/.meta/example.py | 12 ++++++++----
exercises/practice/proverb/.meta/template.j2 | 2 +-
exercises/practice/proverb/proverb_test.py | 2 +-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/proverb/.meta/example.py b/exercises/practice/proverb/.meta/example.py
index ec434ec7cec..2bb6be6eef1 100644
--- a/exercises/practice/proverb/.meta/example.py
+++ b/exercises/practice/proverb/.meta/example.py
@@ -1,7 +1,11 @@
-def proverb(rhyme_items):
+def proverb(*rhyme_items, qualifier=None):
+ print(rhyme_items)
if not rhyme_items:
- return ''
+ return []
phrases = [f'For want of a {element_1} the {element_2} was lost.'
for element_1, element_2 in zip(rhyme_items, rhyme_items[1:])]
- phrases.append(f'And all for the want of a {rhyme_items[0]}.')
- return '\n'.join(phrases)
+ if qualifier:
+ phrases.append(f'And all for the want of a {qualifier} {rhyme_items[0]}.')
+ else:
+ phrases.append(f'And all for the want of a {rhyme_items[0]}.')
+ return phrases
diff --git a/exercises/practice/proverb/.meta/template.j2 b/exercises/practice/proverb/.meta/template.j2
index 91def8fee26..b856ec87aed 100644
--- a/exercises/practice/proverb/.meta/template.j2
+++ b/exercises/practice/proverb/.meta/template.j2
@@ -15,7 +15,7 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
"{{ item }}",
{% endfor %}
{% else %}
- ""
+
{% endif %}
{% if input["extra"] %}
qualifier="{{ input["extra"]["qualifier"] }}"
diff --git a/exercises/practice/proverb/proverb_test.py b/exercises/practice/proverb/proverb_test.py
index 511cde36c14..00abe2e05f4 100644
--- a/exercises/practice/proverb/proverb_test.py
+++ b/exercises/practice/proverb/proverb_test.py
@@ -12,7 +12,7 @@
class ProverbTest(unittest.TestCase):
def test_zero_pieces(self):
- self.assertEqual(proverb(""), [])
+ self.assertEqual(proverb(), [])
def test_one_piece(self):
self.assertEqual(
From eb633aefb399117aea83fd3d2970e674ccf7d09e Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 21:37:27 +0100
Subject: [PATCH 256/932] Updated jinja file
---
exercises/practice/proverb/.meta/example.py | 2 +-
exercises/practice/proverb/.meta/template.j2 | 21 +++-----
exercises/practice/proverb/proverb.py | 2 +-
exercises/practice/proverb/proverb_test.py | 57 ++++++--------------
4 files changed, 27 insertions(+), 55 deletions(-)
diff --git a/exercises/practice/proverb/.meta/example.py b/exercises/practice/proverb/.meta/example.py
index 2bb6be6eef1..8e6f30f5510 100644
--- a/exercises/practice/proverb/.meta/example.py
+++ b/exercises/practice/proverb/.meta/example.py
@@ -1,4 +1,4 @@
-def proverb(*rhyme_items, qualifier=None):
+def proverb(*rhyme_items, qualifier):
print(rhyme_items)
if not rhyme_items:
return []
diff --git a/exercises/practice/proverb/.meta/template.j2 b/exercises/practice/proverb/.meta/template.j2
index b856ec87aed..cf50a261903 100644
--- a/exercises/practice/proverb/.meta/template.j2
+++ b/exercises/practice/proverb/.meta/template.j2
@@ -9,20 +9,14 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{%- macro test_case(case) -%}
{% set input = case["input"] -%}
def test_{{ case["description"] | to_snake }}(self):
- self.assertEqual(proverb(
- {% if input["strings"]|length >= 1 %}
- {% for item in input["strings"] %}
- "{{ item }}",
- {% endfor %}
- {% else %}
-
- {% endif %}
- {% if input["extra"] %}
+ input_data = {{ input["strings"] }}
+ self.assertEqual(proverb(*input_data,
+ {%- if input["extra"] -%}
qualifier="{{ input["extra"]["qualifier"] }}"
- {% endif %}
- ),
- {{ case["expected"] }}
- )
+ {%- else -%}
+ qualifier=None
+ {%- endif -%}
+ ),{{ case["expected"] }})
{%- endmacro -%}
{% for case in cases -%}
@@ -30,6 +24,7 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% endfor %}
# Track-specific tests
+
{% for cases in additional_cases -%}
{{ test_case(cases) }}
{% endfor %}
diff --git a/exercises/practice/proverb/proverb.py b/exercises/practice/proverb/proverb.py
index aa9c1fa304b..d1be410c112 100644
--- a/exercises/practice/proverb/proverb.py
+++ b/exercises/practice/proverb/proverb.py
@@ -1,2 +1,2 @@
-def proverb(rhyme_items):
+def proverb():
pass
diff --git a/exercises/practice/proverb/proverb_test.py b/exercises/practice/proverb/proverb_test.py
index 00abe2e05f4..c3a59d42b6b 100644
--- a/exercises/practice/proverb/proverb_test.py
+++ b/exercises/practice/proverb/proverb_test.py
@@ -12,22 +12,19 @@
class ProverbTest(unittest.TestCase):
def test_zero_pieces(self):
- self.assertEqual(proverb(), [])
+ input_data = []
+ self.assertEqual(proverb(*input_data, qualifier=None), [])
def test_one_piece(self):
+ input_data = ["nail"]
self.assertEqual(
- proverb(
- "nail",
- ),
- ["And all for the want of a nail."],
+ proverb(*input_data, qualifier=None), ["And all for the want of a nail."]
)
def test_two_pieces(self):
+ input_data = ["nail", "shoe"]
self.assertEqual(
- proverb(
- "nail",
- "shoe",
- ),
+ proverb(*input_data, qualifier=None),
[
"For want of a nail the shoe was lost.",
"And all for the want of a nail.",
@@ -35,12 +32,9 @@ def test_two_pieces(self):
)
def test_three_pieces(self):
+ input_data = ["nail", "shoe", "horse"]
self.assertEqual(
- proverb(
- "nail",
- "shoe",
- "horse",
- ),
+ proverb(*input_data, qualifier=None),
[
"For want of a nail the shoe was lost.",
"For want of a shoe the horse was lost.",
@@ -49,16 +43,9 @@ def test_three_pieces(self):
)
def test_full_proverb(self):
+ input_data = ["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]
self.assertEqual(
- proverb(
- "nail",
- "shoe",
- "horse",
- "rider",
- "message",
- "battle",
- "kingdom",
- ),
+ proverb(*input_data, qualifier=None),
[
"For want of a nail the shoe was lost.",
"For want of a shoe the horse was lost.",
@@ -71,13 +58,9 @@ def test_full_proverb(self):
)
def test_four_pieces_modernized(self):
+ input_data = ["pin", "gun", "soldier", "battle"]
self.assertEqual(
- proverb(
- "pin",
- "gun",
- "soldier",
- "battle",
- ),
+ proverb(*input_data, qualifier=None),
[
"For want of a pin the gun was lost.",
"For want of a gun the soldier was lost.",
@@ -87,24 +70,18 @@ def test_four_pieces_modernized(self):
)
# Track-specific tests
+
def test_sentence_without_lower_bound(self):
+ input_data = ["nail"]
self.assertEqual(
- proverb("nail", qualifier="horseshoe"),
+ proverb(*input_data, qualifier="horseshoe"),
["And all for the want of a horseshoe nail."],
)
def test_sentence_without_upper_bound(self):
+ input_data = ["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]
self.assertEqual(
- proverb(
- "nail",
- "shoe",
- "horse",
- "rider",
- "message",
- "battle",
- "kingdom",
- qualifier="horseshoe",
- ),
+ proverb(*input_data, qualifier="horseshoe"),
[
"For want of a nail the shoe was lost.",
"For want of a shoe the horse was lost.",
From 3490eda71c943c294105ab8f08841e212f1f0764 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 21:41:43 +0100
Subject: [PATCH 257/932] fix
---
exercises/practice/proverb/.meta/template.j2 | 4 ----
1 file changed, 4 deletions(-)
diff --git a/exercises/practice/proverb/.meta/template.j2 b/exercises/practice/proverb/.meta/template.j2
index cf50a261903..3ef67fe21d6 100644
--- a/exercises/practice/proverb/.meta/template.j2
+++ b/exercises/practice/proverb/.meta/template.j2
@@ -1,11 +1,7 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.header(imports=["proverb"]) }}
-{{ "# PLEASE TAKE NOTE: Expected result lists for these test cases use **implicit line joining.**" }}
-{{ "# A new line in a result list below **does not** always equal a new list element." }}
-{{ "# Check comma placement carefully!" }}
class {{ exercise | camel_case }}Test(unittest.TestCase):
- {# All test cases in this exercise are nested, so use two for loops -#}
{%- macro test_case(case) -%}
{% set input = case["input"] -%}
def test_{{ case["description"] | to_snake }}(self):
From f6ecae281c754ca3c1ea16e01461f24a49739f2e Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 21:44:27 +0100
Subject: [PATCH 258/932] reversed
---
exercises/practice/proverb/.meta/template.j2 | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/exercises/practice/proverb/.meta/template.j2 b/exercises/practice/proverb/.meta/template.j2
index 3ef67fe21d6..cf50a261903 100644
--- a/exercises/practice/proverb/.meta/template.j2
+++ b/exercises/practice/proverb/.meta/template.j2
@@ -1,7 +1,11 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.header(imports=["proverb"]) }}
+{{ "# PLEASE TAKE NOTE: Expected result lists for these test cases use **implicit line joining.**" }}
+{{ "# A new line in a result list below **does not** always equal a new list element." }}
+{{ "# Check comma placement carefully!" }}
class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {# All test cases in this exercise are nested, so use two for loops -#}
{%- macro test_case(case) -%}
{% set input = case["input"] -%}
def test_{{ case["description"] | to_snake }}(self):
From 509342fd8a68b40dbd88d43a446dc3201b8cac4a Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 4 Dec 2022 14:05:58 -0800
Subject: [PATCH 259/932] Create instructions.append.md
Added qualifier explanation and link to unpanking-and-multiple-assignment.
---
exercises/practice/proverb/.docs/instructions.append.md | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 exercises/practice/proverb/.docs/instructions.append.md
diff --git a/exercises/practice/proverb/.docs/instructions.append.md b/exercises/practice/proverb/.docs/instructions.append.md
new file mode 100644
index 00000000000..c7797d0d69d
--- /dev/null
+++ b/exercises/practice/proverb/.docs/instructions.append.md
@@ -0,0 +1,9 @@
+# Instructions append
+
+In [concept:python/unpacking-and-multiple-assignment](https://github.com/exercism/python/tree/main/concepts/unpacking-and-multiple-assignment), you learned multiple techniques for working with `lists`/`tuples` of arbitrary length as well as function arguments of arbitrary length.
+This exercise would be a great place to practice those techniques.
+
+## How this exercise is implemented for Python
+
+The test cases for this track add an additional keyword argument called `qualifier`.
+You should use this keyword arguments value to modify the final verse of your Proverb.
From 9e8621a47fab125af1a658b8ae37f51ce3ddc11d Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 4 Dec 2022 23:15:16 +0100
Subject: [PATCH 260/932] fix
---
config.json | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/config.json b/config.json
index 2d2ab01976f..b2a3c8ebc38 100644
--- a/config.json
+++ b/config.json
@@ -660,8 +660,7 @@
"practices": ["unpacking-and-multiple-assignment"],
"prerequisites": ["dicts",
"unpacking-and-multiple-assignment"],
- "difficulty": 2,
- "status": "wip"
+ "difficulty": 2
},
{
"slug": "yacht",
From 689b2cf3d2e8164f86244929829fb40b52a3486b Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 4 Dec 2022 23:40:23 +0100
Subject: [PATCH 261/932] updated test case
---
exercises/practice/proverb/.meta/additional_tests.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/proverb/.meta/additional_tests.json b/exercises/practice/proverb/.meta/additional_tests.json
index 8a6a670577c..0d1cac001f9 100644
--- a/exercises/practice/proverb/.meta/additional_tests.json
+++ b/exercises/practice/proverb/.meta/additional_tests.json
@@ -1,7 +1,7 @@
{
"cases": [
{
- "description": "sentence without lower bound",
+ "description": "an optional qualifier can be added",
"property": "proverb",
"input": {
"strings": ["nail"],
@@ -10,7 +10,7 @@
"expected": ["And all for the want of a horseshoe nail."]
},
{
- "description": "sentence without upper bound",
+ "description": "an optional qualifier in the final consequences",
"property": "proverb",
"input": {
"strings": [
From 1fa29ba6cc74ff3cefe6da9e8fe7003a632e7ab6 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sun, 4 Dec 2022 23:48:49 +0100
Subject: [PATCH 262/932] fix
---
exercises/practice/proverb/proverb_test.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/proverb/proverb_test.py b/exercises/practice/proverb/proverb_test.py
index c3a59d42b6b..69905dadf4a 100644
--- a/exercises/practice/proverb/proverb_test.py
+++ b/exercises/practice/proverb/proverb_test.py
@@ -71,14 +71,14 @@ def test_four_pieces_modernized(self):
# Track-specific tests
- def test_sentence_without_lower_bound(self):
+ def test_an_optional_qualifier_can_be_added(self):
input_data = ["nail"]
self.assertEqual(
proverb(*input_data, qualifier="horseshoe"),
["And all for the want of a horseshoe nail."],
)
- def test_sentence_without_upper_bound(self):
+ def test_an_optional_qualifier_in_the_final_consequences(self):
input_data = ["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]
self.assertEqual(
proverb(*input_data, qualifier="horseshoe"),
From 5dacb7aba9399a7ac0ad774863137c9c805d0cbe Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Sun, 4 Dec 2022 12:40:09 +0100
Subject: [PATCH 263/932] Reformat exercise config files
This runs the `bin/configlet fmt --update` command to
reformat the exercise configs.
---
.../concept/black-jack/.meta/config.json | 32 ++++++++++++++-----
.../concept/card-games/.meta/config.json | 29 ++++++++++++-----
.../concept/cater-waiter/.meta/config.json | 31 ++++++++++++------
.../.meta/config.json | 28 +++++++++++-----
.../currency-exchange/.meta/config.json | 31 +++++++++++++-----
.../ellens-alien-game/.meta/config.json | 29 ++++++++++++-----
.../.meta/config.json | 31 ++++++++++++------
.../guidos-gorgeous-lasagna/.meta/config.json | 25 +++++++++++----
.../inventory-management/.meta/config.json | 29 ++++++++++++-----
.../little-sisters-essay/.meta/config.json | 27 +++++++++++-----
.../little-sisters-vocab/.meta/config.json | 23 +++++++++----
.../locomotive-engineer/.meta/config.json | 29 ++++++++++++-----
.../concept/log-levels/.meta/config.json | 28 +++++++++++-----
.../making-the-grade/.meta/config.json | 27 +++++++++++-----
.../meltdown-mitigation/.meta/config.json | 27 +++++++++++-----
.../concept/plane-tickets/.meta/config.json | 26 ++++++++++-----
.../concept/pretty-leaflet/.meta/config.json | 27 +++++++++++-----
.../restaurant-rozalynn/.meta/config.json | 23 +++++++++----
.../tisbury-treasure-hunt/.meta/config.json | 22 +++++++++----
exercises/practice/acronym/.meta/config.json | 2 +-
.../practice/affine-cipher/.meta/config.json | 2 +-
.../practice/all-your-base/.meta/config.json | 4 +--
.../practice/allergies/.meta/config.json | 2 +-
.../practice/alphametics/.meta/config.json | 4 +--
exercises/practice/anagram/.meta/config.json | 2 +-
.../armstrong-numbers/.meta/config.json | 2 +-
.../practice/atbash-cipher/.meta/config.json | 2 +-
.../practice/bank-account/.meta/config.json | 4 +--
.../practice/beer-song/.meta/config.json | 2 +-
.../binary-search-tree/.meta/config.json | 2 +-
.../practice/binary-search/.meta/config.json | 2 +-
exercises/practice/bob/.meta/config.json | 2 +-
.../practice/book-store/.meta/config.json | 2 +-
exercises/practice/bowling/.meta/config.json | 2 +-
exercises/practice/change/.meta/config.json | 2 +-
.../circular-buffer/.meta/config.json | 2 +-
exercises/practice/clock/.meta/config.json | 2 +-
.../collatz-conjecture/.meta/config.json | 2 +-
.../complex-numbers/.meta/config.json | 2 +-
exercises/practice/connect/.meta/config.json | 4 +--
.../practice/crypto-square/.meta/config.json | 2 +-
.../practice/custom-set/.meta/config.json | 4 +--
exercises/practice/darts/.meta/config.json | 2 +-
exercises/practice/diamond/.meta/config.json | 2 +-
.../difference-of-squares/.meta/config.json | 2 +-
.../practice/diffie-hellman/.meta/config.json | 2 +-
.../practice/dnd-character/.meta/config.json | 2 +-
exercises/practice/dominoes/.meta/config.json | 4 +--
exercises/practice/dot-dsl/.meta/config.json | 2 +-
exercises/practice/etl/.meta/config.json | 2 +-
.../practice/flatten-array/.meta/config.json | 2 +-
.../practice/food-chain/.meta/config.json | 2 +-
exercises/practice/forth/.meta/config.json | 4 +--
.../practice/gigasecond/.meta/config.json | 2 +-
.../practice/go-counting/.meta/config.json | 4 +--
.../practice/grade-school/.meta/config.json | 2 +-
exercises/practice/grains/.meta/config.json | 2 +-
exercises/practice/grep/.meta/config.json | 2 +-
exercises/practice/hamming/.meta/config.json | 2 +-
exercises/practice/hangman/.meta/config.json | 4 +--
.../practice/hello-world/.meta/config.json | 2 +-
.../practice/high-scores/.meta/config.json | 2 +-
exercises/practice/house/.meta/config.json | 2 +-
.../practice/isbn-verifier/.meta/config.json | 2 +-
exercises/practice/isogram/.meta/config.json | 2 +-
.../kindergarten-garden/.meta/config.json | 2 +-
exercises/practice/knapsack/.meta/config.json | 2 +-
.../largest-series-product/.meta/config.json | 2 +-
exercises/practice/leap/.meta/config.json | 2 +-
exercises/practice/ledger/.meta/config.json | 4 +--
.../practice/linked-list/.meta/config.json | 2 +-
exercises/practice/list-ops/.meta/config.json | 4 +--
exercises/practice/luhn/.meta/config.json | 2 +-
exercises/practice/markdown/.meta/config.json | 4 +--
.../matching-brackets/.meta/config.json | 2 +-
exercises/practice/matrix/.meta/config.json | 2 +-
exercises/practice/meetup/.meta/config.json | 2 +-
.../practice/minesweeper/.meta/config.json | 4 +--
.../practice/nth-prime/.meta/config.json | 2 +-
.../practice/ocr-numbers/.meta/config.json | 2 +-
exercises/practice/paasio/.meta/config.json | 2 +-
.../palindrome-products/.meta/config.json | 2 +-
exercises/practice/pangram/.meta/config.json | 2 +-
.../perfect-numbers/.meta/config.json | 2 +-
.../practice/phone-number/.meta/config.json | 2 +-
.../practice/pig-latin/.meta/config.json | 2 +-
exercises/practice/poker/.meta/config.json | 2 +-
exercises/practice/pov/.meta/config.json | 2 +-
.../practice/prime-factors/.meta/config.json | 2 +-
.../protein-translation/.meta/config.json | 2 +-
.../pythagorean-triplet/.meta/config.json | 2 +-
.../practice/queen-attack/.meta/config.json | 2 +-
.../rail-fence-cipher/.meta/config.json | 2 +-
.../practice/raindrops/.meta/config.json | 2 +-
.../rational-numbers/.meta/config.json | 2 +-
exercises/practice/react/.meta/config.json | 4 +--
.../practice/rectangles/.meta/config.json | 4 +--
.../resistor-color-duo/.meta/config.json | 2 +-
.../resistor-color-trio/.meta/config.json | 4 +--
.../practice/resistor-color/.meta/config.json | 2 +-
exercises/practice/rest-api/.meta/config.json | 4 +--
.../practice/reverse-string/.meta/config.json | 2 +-
.../rna-transcription/.meta/config.json | 2 +-
.../practice/robot-name/.meta/config.json | 2 +-
.../robot-simulator/.meta/config.json | 2 +-
.../practice/roman-numerals/.meta/config.json | 2 +-
.../rotational-cipher/.meta/config.json | 2 +-
.../run-length-encoding/.meta/config.json | 2 +-
.../practice/saddle-points/.meta/config.json | 2 +-
.../practice/satellite/.meta/config.json | 4 +--
exercises/practice/say/.meta/config.json | 2 +-
.../scale-generator/.meta/config.json | 4 +--
.../practice/scrabble-score/.meta/config.json | 2 +-
.../secret-handshake/.meta/config.json | 2 +-
exercises/practice/series/.meta/config.json | 2 +-
.../practice/sgf-parsing/.meta/config.json | 4 +--
exercises/practice/sieve/.meta/config.json | 2 +-
.../practice/simple-cipher/.meta/config.json | 2 +-
.../simple-linked-list/.meta/config.json | 2 +-
.../practice/space-age/.meta/config.json | 2 +-
.../practice/spiral-matrix/.meta/config.json | 2 +-
exercises/practice/sublist/.meta/config.json | 4 +--
.../sum-of-multiples/.meta/config.json | 2 +-
.../practice/tournament/.meta/config.json | 4 +--
.../practice/transpose/.meta/config.json | 2 +-
.../practice/tree-building/.meta/config.json | 4 +--
exercises/practice/triangle/.meta/config.json | 2 +-
.../practice/twelve-days/.meta/config.json | 2 +-
.../practice/two-bucket/.meta/config.json | 2 +-
exercises/practice/two-fer/.meta/config.json | 2 +-
.../.meta/config.json | 2 +-
.../practice/word-count/.meta/config.json | 2 +-
.../practice/word-search/.meta/config.json | 4 +--
exercises/practice/wordy/.meta/config.json | 2 +-
exercises/practice/yacht/.meta/config.json | 2 +-
.../practice/zebra-puzzle/.meta/config.json | 2 +-
exercises/practice/zipper/.meta/config.json | 4 +--
137 files changed, 516 insertions(+), 294 deletions(-)
diff --git a/exercises/concept/black-jack/.meta/config.json b/exercises/concept/black-jack/.meta/config.json
index f1ff6a193d6..a23fa9e8f92 100644
--- a/exercises/concept/black-jack/.meta/config.json
+++ b/exercises/concept/black-jack/.meta/config.json
@@ -1,11 +1,27 @@
{
- "blurb": "Learn about comparisons by implementing some Black Jack judging rules.",
- "authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007", "pranasziaukas", "bethanyG"],
- "icon": "poker",
- "contributors": ["PaulT89"],
+ "authors": [
+ "Ticktakto",
+ "Yabby1997",
+ "limm-jk",
+ "OMEGA-Y",
+ "wnstj2007",
+ "pranasziaukas",
+ "bethanyG"
+ ],
+ "contributors": [
+ "PaulT89"
+ ],
"files": {
- "solution": ["black_jack.py"],
- "test": ["black_jack_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "black_jack.py"
+ ],
+ "test": [
+ "black_jack_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "poker",
+ "blurb": "Learn about comparisons by implementing some Black Jack judging rules."
}
diff --git a/exercises/concept/card-games/.meta/config.json b/exercises/concept/card-games/.meta/config.json
index 8e5645485aa..f7d39caa016 100644
--- a/exercises/concept/card-games/.meta/config.json
+++ b/exercises/concept/card-games/.meta/config.json
@@ -1,11 +1,24 @@
{
- "blurb": "Learn about lists by tracking hands in card games.",
- "icon": "poker",
- "contributors": ["valentin-p", "pranasziaukas"],
- "authors": ["itamargal", "isaacg", "bethanyg"],
+ "authors": [
+ "itamargal",
+ "isaacg",
+ "bethanyg"
+ ],
+ "contributors": [
+ "valentin-p",
+ "pranasziaukas"
+ ],
"files": {
- "solution": ["lists.py"],
- "test": ["lists_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "lists.py"
+ ],
+ "test": [
+ "lists_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "poker",
+ "blurb": "Learn about lists by tracking hands in card games."
}
diff --git a/exercises/concept/cater-waiter/.meta/config.json b/exercises/concept/cater-waiter/.meta/config.json
index c5f7635f0ff..89145f52b73 100644
--- a/exercises/concept/cater-waiter/.meta/config.json
+++ b/exercises/concept/cater-waiter/.meta/config.json
@@ -1,12 +1,25 @@
{
- "blurb": "Learn about sets by managing the menus and ingredients for your catering company's event.",
- "icon": "meetup",
- "authors": ["bethanyg"],
- "contributors": ["zepam"],
+ "authors": [
+ "bethanyg"
+ ],
+ "contributors": [
+ "zepam"
+ ],
"files": {
- "solution": ["sets.py"],
- "test": ["sets_test.py"],
- "exemplar": [".meta/exemplar.py"],
- "editor": ["sets_test_data.py", "sets_categories_data.py"]
- }
+ "solution": [
+ "sets.py"
+ ],
+ "test": [
+ "sets_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ],
+ "editor": [
+ "sets_test_data.py",
+ "sets_categories_data.py"
+ ]
+ },
+ "icon": "meetup",
+ "blurb": "Learn about sets by managing the menus and ingredients for your catering company's event."
}
diff --git a/exercises/concept/chaitanas-colossal-coaster/.meta/config.json b/exercises/concept/chaitanas-colossal-coaster/.meta/config.json
index f5acc99ec02..335d2f9d7d5 100644
--- a/exercises/concept/chaitanas-colossal-coaster/.meta/config.json
+++ b/exercises/concept/chaitanas-colossal-coaster/.meta/config.json
@@ -1,11 +1,23 @@
{
- "blurb": "Learn useful list methods helping Chaitana manage the lines for her colossal roller coaster.",
- "icon": "spiral-matrix",
- "contributors": ["valentin-p", "pranasziaukas"],
- "authors": ["mohanrajanr", "BethanyG"],
+ "authors": [
+ "mohanrajanr",
+ "BethanyG"
+ ],
+ "contributors": [
+ "valentin-p",
+ "pranasziaukas"
+ ],
"files": {
- "solution": ["list_methods.py"],
- "test": ["list_methods_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "list_methods.py"
+ ],
+ "test": [
+ "list_methods_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "spiral-matrix",
+ "blurb": "Learn useful list methods helping Chaitana manage the lines for her colossal roller coaster."
}
diff --git a/exercises/concept/currency-exchange/.meta/config.json b/exercises/concept/currency-exchange/.meta/config.json
index e002690297c..c9825d3c609 100644
--- a/exercises/concept/currency-exchange/.meta/config.json
+++ b/exercises/concept/currency-exchange/.meta/config.json
@@ -1,11 +1,26 @@
{
- "blurb": "Learn about numbers by solving Chandler's currency exchange conundrums.",
- "icon": "hyperia-forex",
- "authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007", "J08K"],
- "contributors": ["pranasziaukas"],
+ "authors": [
+ "Ticktakto",
+ "Yabby1997",
+ "limm-jk",
+ "OMEGA-Y",
+ "wnstj2007",
+ "J08K"
+ ],
+ "contributors": [
+ "pranasziaukas"
+ ],
"files": {
- "solution": ["exchange.py"],
- "test": ["exchange_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "exchange.py"
+ ],
+ "test": [
+ "exchange_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "hyperia-forex",
+ "blurb": "Learn about numbers by solving Chandler's currency exchange conundrums."
}
diff --git a/exercises/concept/ellens-alien-game/.meta/config.json b/exercises/concept/ellens-alien-game/.meta/config.json
index 92ed8179c6a..111ebdc6adf 100644
--- a/exercises/concept/ellens-alien-game/.meta/config.json
+++ b/exercises/concept/ellens-alien-game/.meta/config.json
@@ -1,11 +1,24 @@
{
- "blurb": "Learn about classes by creating an Alien for Ellen's game.",
- "icon": "character-study",
- "authors": ["PaulT89", "BethanyG"],
- "contributors": ["DjangoFett", "kotp", "IsaacG"],
+ "authors": [
+ "PaulT89",
+ "BethanyG"
+ ],
+ "contributors": [
+ "DjangoFett",
+ "kotp",
+ "IsaacG"
+ ],
"files": {
- "solution": ["classes.py"],
- "test": ["classes_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "classes.py"
+ ],
+ "test": [
+ "classes_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "character-study",
+ "blurb": "Learn about classes by creating an Alien for Ellen's game."
}
diff --git a/exercises/concept/ghost-gobble-arcade-game/.meta/config.json b/exercises/concept/ghost-gobble-arcade-game/.meta/config.json
index a304f1f67a9..320ea646773 100644
--- a/exercises/concept/ghost-gobble-arcade-game/.meta/config.json
+++ b/exercises/concept/ghost-gobble-arcade-game/.meta/config.json
@@ -1,13 +1,26 @@
{
- "blurb": "Learn about bools by setting up the rules for the Ghost Gobble arcade game.",
- "icon": "matching-brackets",
- "authors": ["neenjaw"],
- "contributors": ["cmccandless", "bethanyg"],
+ "authors": [
+ "neenjaw"
+ ],
+ "contributors": [
+ "cmccandless",
+ "bethanyg"
+ ],
"files": {
- "solution": ["arcade_game.py"],
- "test": ["arcade_game_test.py"],
- "exemplar": [".meta/exemplar.py"]
+ "solution": [
+ "arcade_game.py"
+ ],
+ "test": [
+ "arcade_game_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
},
- "forked_from": ["elixir/pacman-rules"],
- "language_versions": ">=3.5"
+ "language_versions": ">=3.5",
+ "forked_from": [
+ "elixir/pacman-rules"
+ ],
+ "icon": "matching-brackets",
+ "blurb": "Learn about bools by setting up the rules for the Ghost Gobble arcade game."
}
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.meta/config.json b/exercises/concept/guidos-gorgeous-lasagna/.meta/config.json
index 457426ce9db..f6de0d57f13 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.meta/config.json
+++ b/exercises/concept/guidos-gorgeous-lasagna/.meta/config.json
@@ -1,11 +1,22 @@
{
- "blurb": "Learn the basics of Python by cooking Guido's Gorgeous Lasagna.",
- "icon": "lasagna",
- "authors": ["BethanyG"],
+ "authors": [
+ "BethanyG"
+ ],
"files": {
- "solution": ["lasagna.py"],
- "test": ["lasagna_test.py"],
- "exemplar": [".meta/exemplar.py"]
+ "solution": [
+ "lasagna.py"
+ ],
+ "test": [
+ "lasagna_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
},
- "forked_from": ["csharp/lucians-luscious-lasagna", "ruby/lasagna"]
+ "forked_from": [
+ "csharp/lucians-luscious-lasagna",
+ "ruby/lasagna"
+ ],
+ "icon": "lasagna",
+ "blurb": "Learn the basics of Python by cooking Guido's Gorgeous Lasagna."
}
diff --git a/exercises/concept/inventory-management/.meta/config.json b/exercises/concept/inventory-management/.meta/config.json
index 1f0fb8e1a75..c08624ae423 100644
--- a/exercises/concept/inventory-management/.meta/config.json
+++ b/exercises/concept/inventory-management/.meta/config.json
@@ -1,11 +1,24 @@
{
- "blurb": "Learn about dicts by managing warehouse inventory.",
- "icon": "high-scores",
- "authors": ["j08k"],
- "contributors": ["valentin-p", "bethanyG", "mukeshgurpude", "kotp"],
+ "authors": [
+ "j08k"
+ ],
+ "contributors": [
+ "valentin-p",
+ "bethanyG",
+ "mukeshgurpude",
+ "kotp"
+ ],
"files": {
- "solution": ["dicts.py"],
- "test": ["dicts_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "dicts.py"
+ ],
+ "test": [
+ "dicts_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "high-scores",
+ "blurb": "Learn about dicts by managing warehouse inventory."
}
diff --git a/exercises/concept/little-sisters-essay/.meta/config.json b/exercises/concept/little-sisters-essay/.meta/config.json
index 708b0c992e0..ea803001dd1 100644
--- a/exercises/concept/little-sisters-essay/.meta/config.json
+++ b/exercises/concept/little-sisters-essay/.meta/config.json
@@ -1,11 +1,22 @@
{
- "blurb": "Learn about string methods while improving your little sister's school essay.",
- "icon": "anagrams",
- "contributors": ["valentin-p", "bethanyg"],
- "authors": ["kimolivia"],
+ "authors": [
+ "kimolivia"
+ ],
+ "contributors": [
+ "valentin-p",
+ "bethanyg"
+ ],
"files": {
- "solution": ["string_methods.py"],
- "test": ["string_methods_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "string_methods.py"
+ ],
+ "test": [
+ "string_methods_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "anagrams",
+ "blurb": "Learn about string methods while improving your little sister's school essay."
}
diff --git a/exercises/concept/little-sisters-vocab/.meta/config.json b/exercises/concept/little-sisters-vocab/.meta/config.json
index d7eecdcea9f..c18c248b8c5 100644
--- a/exercises/concept/little-sisters-vocab/.meta/config.json
+++ b/exercises/concept/little-sisters-vocab/.meta/config.json
@@ -1,10 +1,19 @@
{
- "blurb": "Learn about strings by helping your little sister with her vocabulary homework.",
- "icon": "two-fer",
- "authors": ["aldraco", "bethanyg"],
+ "authors": [
+ "aldraco",
+ "bethanyg"
+ ],
"files": {
- "solution": ["strings.py"],
- "test": ["strings_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "strings.py"
+ ],
+ "test": [
+ "strings_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "two-fer",
+ "blurb": "Learn about strings by helping your little sister with her vocabulary homework."
}
diff --git a/exercises/concept/locomotive-engineer/.meta/config.json b/exercises/concept/locomotive-engineer/.meta/config.json
index 4c10c882631..e9110e93a0c 100644
--- a/exercises/concept/locomotive-engineer/.meta/config.json
+++ b/exercises/concept/locomotive-engineer/.meta/config.json
@@ -1,12 +1,25 @@
{
- "blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system.",
- "icon": "tracks-on-tracks-on-tracks",
- "authors": ["meatball133","BethanyG"],
- "contributors": ["IsaacG"],
+ "authors": [
+ "meatball133",
+ "BethanyG"
+ ],
+ "contributors": [
+ "IsaacG"
+ ],
"files": {
- "solution": ["locomotive_engineer.py"],
- "test": ["locomotive_engineer_test.py"],
- "exemplar": [".meta/exemplar.py"]
+ "solution": [
+ "locomotive_engineer.py"
+ ],
+ "test": [
+ "locomotive_engineer_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
},
- "forked_from": ["javascript/train-driver"]
+ "forked_from": [
+ "javascript/train-driver"
+ ],
+ "icon": "tracks-on-tracks-on-tracks",
+ "blurb": "Learn about unpacking and multiple assignment in Python while helping Linus with his train control system."
}
diff --git a/exercises/concept/log-levels/.meta/config.json b/exercises/concept/log-levels/.meta/config.json
index 8cb94aec2af..fc14d0ef3e3 100644
--- a/exercises/concept/log-levels/.meta/config.json
+++ b/exercises/concept/log-levels/.meta/config.json
@@ -1,12 +1,24 @@
{
- "blurb": "Learn about enums by automating your tedious logging-level tasks.",
- "icon": "log-levels",
- "contributors": ["valentin-p"],
- "authors": ["mohanrajanr"],
+ "authors": [
+ "mohanrajanr"
+ ],
+ "contributors": [
+ "valentin-p"
+ ],
"files": {
- "solution": ["enums.py"],
- "test": ["enums_test.py"],
- "exemplar": [".meta/exemplar.py"]
+ "solution": [
+ "enums.py"
+ ],
+ "test": [
+ "enums_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
},
- "forked_from": ["csharp/logs-logs-logs"]
+ "forked_from": [
+ "csharp/logs-logs-logs"
+ ],
+ "icon": "log-levels",
+ "blurb": "Learn about enums by automating your tedious logging-level tasks."
}
diff --git a/exercises/concept/making-the-grade/.meta/config.json b/exercises/concept/making-the-grade/.meta/config.json
index 8e1788ab628..d9edd466380 100644
--- a/exercises/concept/making-the-grade/.meta/config.json
+++ b/exercises/concept/making-the-grade/.meta/config.json
@@ -1,11 +1,22 @@
{
- "blurb": "Learn about loops by grading and organizing your students exam scores.",
- "icon": "grep",
- "authors": ["mohanrajanr", "BethanyG"],
- "contributors": ["pranasziaukas"],
+ "authors": [
+ "mohanrajanr",
+ "BethanyG"
+ ],
+ "contributors": [
+ "pranasziaukas"
+ ],
"files": {
- "solution": ["loops.py"],
- "test": ["loops_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "loops.py"
+ ],
+ "test": [
+ "loops_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "grep",
+ "blurb": "Learn about loops by grading and organizing your students exam scores."
}
diff --git a/exercises/concept/meltdown-mitigation/.meta/config.json b/exercises/concept/meltdown-mitigation/.meta/config.json
index 78c54743d02..eb88d8b411a 100644
--- a/exercises/concept/meltdown-mitigation/.meta/config.json
+++ b/exercises/concept/meltdown-mitigation/.meta/config.json
@@ -1,11 +1,22 @@
{
- "blurb": "Learn about conditionals and avoid a meltdown by developing a simple control system for a Nuclear Reactor.",
- "icon": "circular-buffer",
- "authors": ["sachsom95", "BethanyG"],
- "contributors": ["kbuc"],
+ "authors": [
+ "sachsom95",
+ "BethanyG"
+ ],
+ "contributors": [
+ "kbuc"
+ ],
"files": {
- "solution": ["conditionals.py"],
- "test": ["conditionals_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "conditionals.py"
+ ],
+ "test": [
+ "conditionals_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "circular-buffer",
+ "blurb": "Learn about conditionals and avoid a meltdown by developing a simple control system for a Nuclear Reactor."
}
diff --git a/exercises/concept/plane-tickets/.meta/config.json b/exercises/concept/plane-tickets/.meta/config.json
index 96559aa24c9..016ff0cef23 100644
--- a/exercises/concept/plane-tickets/.meta/config.json
+++ b/exercises/concept/plane-tickets/.meta/config.json
@@ -1,11 +1,21 @@
{
- "blurb": "Learn about generators by assigning seats to passengers.",
- "authors": ["J08K"],
- "icon": "poker",
- "contributors": ["BethanyG"],
+ "authors": [
+ "J08K"
+ ],
+ "contributors": [
+ "BethanyG"
+ ],
"files": {
- "solution": ["plane_tickets.py"],
- "test": ["plane_tickets_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "plane_tickets.py"
+ ],
+ "test": [
+ "plane_tickets_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "poker",
+ "blurb": "Learn about generators by assigning seats to passengers."
}
diff --git a/exercises/concept/pretty-leaflet/.meta/config.json b/exercises/concept/pretty-leaflet/.meta/config.json
index 9b5b07c4161..432e26f3494 100644
--- a/exercises/concept/pretty-leaflet/.meta/config.json
+++ b/exercises/concept/pretty-leaflet/.meta/config.json
@@ -1,11 +1,22 @@
{
- "blurb": "Learn about string formatting by \"prettifying\" promotional leaflets.",
- "icon": "scale-generator",
- "contributors": ["j08k", "BethanyG"],
- "authors": ["valentin-p"],
+ "authors": [
+ "valentin-p"
+ ],
+ "contributors": [
+ "j08k",
+ "BethanyG"
+ ],
"files": {
- "solution": ["string_formatting.py"],
- "test": ["string_formatting_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "string_formatting.py"
+ ],
+ "test": [
+ "string_formatting_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "scale-generator",
+ "blurb": "Learn about string formatting by \"prettifying\" promotional leaflets."
}
diff --git a/exercises/concept/restaurant-rozalynn/.meta/config.json b/exercises/concept/restaurant-rozalynn/.meta/config.json
index 3c4cfed0b63..5b181496489 100644
--- a/exercises/concept/restaurant-rozalynn/.meta/config.json
+++ b/exercises/concept/restaurant-rozalynn/.meta/config.json
@@ -1,10 +1,19 @@
{
- "blurb": "Learn about None by managing the reservations at Restaurant Rozalynn.",
- "icon": "kindergarten-garden",
- "authors": ["mohanrajanr", "BethanyG"],
+ "authors": [
+ "mohanrajanr",
+ "BethanyG"
+ ],
"files": {
- "solution": ["none.py"],
- "test": ["none_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "none.py"
+ ],
+ "test": [
+ "none_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "kindergarten-garden",
+ "blurb": "Learn about None by managing the reservations at Restaurant Rozalynn."
}
diff --git a/exercises/concept/tisbury-treasure-hunt/.meta/config.json b/exercises/concept/tisbury-treasure-hunt/.meta/config.json
index 3a8c476fe9f..f3d1ad373ff 100644
--- a/exercises/concept/tisbury-treasure-hunt/.meta/config.json
+++ b/exercises/concept/tisbury-treasure-hunt/.meta/config.json
@@ -1,10 +1,18 @@
{
- "blurb": "Learn about tuples by helping out competitors in the Tisbury Treasure Hunt.",
- "icon": "tisbury-treasure-hunt",
- "authors": ["bethanyg"],
+ "authors": [
+ "bethanyg"
+ ],
"files": {
- "solution": ["tuples.py"],
- "test": ["tuples_test.py"],
- "exemplar": [".meta/exemplar.py"]
- }
+ "solution": [
+ "tuples.py"
+ ],
+ "test": [
+ "tuples_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "tisbury-treasure-hunt",
+ "blurb": "Learn about tuples by helping out competitors in the Tisbury Treasure Hunt."
}
diff --git a/exercises/practice/acronym/.meta/config.json b/exercises/practice/acronym/.meta/config.json
index cac0adf9677..e29fff682f9 100644
--- a/exercises/practice/acronym/.meta/config.json
+++ b/exercises/practice/acronym/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Convert a long phrase to its acronym.",
"authors": [
"rootulp"
],
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "Convert a long phrase to its acronym.",
"source": "Julien Vanier",
"source_url": "https://github.com/monkbroc"
}
diff --git a/exercises/practice/affine-cipher/.meta/config.json b/exercises/practice/affine-cipher/.meta/config.json
index fb6606be5d4..3d23ccaaef5 100644
--- a/exercises/practice/affine-cipher/.meta/config.json
+++ b/exercises/practice/affine-cipher/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Create an implementation of the Affine cipher, an ancient encryption algorithm from the Middle East.",
"authors": [
"jackattack24"
],
@@ -20,6 +19,7 @@
".meta/example.py"
]
},
+ "blurb": "Create an implementation of the Affine cipher, an ancient encryption algorithm from the Middle East.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Affine_cipher"
}
diff --git a/exercises/practice/all-your-base/.meta/config.json b/exercises/practice/all-your-base/.meta/config.json
index 6862f653079..57499d1e202 100644
--- a/exercises/practice/all-your-base/.meta/config.json
+++ b/exercises/practice/all-your-base/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Convert a number, represented as a sequence of digits in one base, to any other base.",
"authors": [
"behrtam"
],
@@ -23,5 +22,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Convert a number, represented as a sequence of digits in one base, to any other base."
}
diff --git a/exercises/practice/allergies/.meta/config.json b/exercises/practice/allergies/.meta/config.json
index d38070cec6a..b918bdbccef 100644
--- a/exercises/practice/allergies/.meta/config.json
+++ b/exercises/practice/allergies/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies.",
"authors": [
"sjakobi"
],
@@ -30,6 +29,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies.",
"source": "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/alphametics/.meta/config.json b/exercises/practice/alphametics/.meta/config.json
index e609cb5d998..38bc5e3a56c 100644
--- a/exercises/practice/alphametics/.meta/config.json
+++ b/exercises/practice/alphametics/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Write a function to solve alphametics puzzles.",
"authors": [
"behrtam"
],
@@ -27,5 +26,6 @@
".meta/example.py"
]
},
- "test_runner": false
+ "test_runner": false,
+ "blurb": "Write a function to solve alphametics puzzles."
}
diff --git a/exercises/practice/anagram/.meta/config.json b/exercises/practice/anagram/.meta/config.json
index d989bc79477..6dee5e1551c 100644
--- a/exercises/practice/anagram/.meta/config.json
+++ b/exercises/practice/anagram/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a word and a list of possible anagrams, select the correct sublist.",
"authors": [],
"contributors": [
"behrtam",
@@ -33,6 +32,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a word and a list of possible anagrams, select the correct sublist.",
"source": "Inspired by the Extreme Startup game",
"source_url": "https://github.com/rchatley/extreme_startup"
}
diff --git a/exercises/practice/armstrong-numbers/.meta/config.json b/exercises/practice/armstrong-numbers/.meta/config.json
index 0490a9f2210..2febbd205d3 100644
--- a/exercises/practice/armstrong-numbers/.meta/config.json
+++ b/exercises/practice/armstrong-numbers/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Determine if a number is an Armstrong number.",
"authors": [
"pheanex"
],
@@ -22,6 +21,7 @@
".meta/example.py"
]
},
+ "blurb": "Determine if a number is an Armstrong number.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Narcissistic_number"
}
diff --git a/exercises/practice/atbash-cipher/.meta/config.json b/exercises/practice/atbash-cipher/.meta/config.json
index a1c24673613..9f678c6f203 100644
--- a/exercises/practice/atbash-cipher/.meta/config.json
+++ b/exercises/practice/atbash-cipher/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.",
"authors": [
"betegelse"
],
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Atbash"
}
diff --git a/exercises/practice/bank-account/.meta/config.json b/exercises/practice/bank-account/.meta/config.json
index 06616793dde..8ab9aa0d888 100644
--- a/exercises/practice/bank-account/.meta/config.json
+++ b/exercises/practice/bank-account/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Simulate a bank account supporting opening/closing, withdraws, and deposits of money. Watch out for concurrent transactions!",
"authors": [],
"contributors": [
"cmccandless",
@@ -16,5 +15,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Simulate a bank account supporting opening/closing, withdraws, and deposits of money. Watch out for concurrent transactions!"
}
diff --git a/exercises/practice/beer-song/.meta/config.json b/exercises/practice/beer-song/.meta/config.json
index a59b25cd4a5..834501ed387 100644
--- a/exercises/practice/beer-song/.meta/config.json
+++ b/exercises/practice/beer-song/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Produce the lyrics to that beloved classic, that field-trip favorite: 99 Bottles of Beer on the Wall.",
"authors": [],
"contributors": [
"behrtam",
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Produce the lyrics to that beloved classic, that field-trip favorite: 99 Bottles of Beer on the Wall.",
"source": "Learn to Program by Chris Pine",
"source_url": "https://pine.fm/LearnToProgram/?Chapter=06"
}
diff --git a/exercises/practice/binary-search-tree/.meta/config.json b/exercises/practice/binary-search-tree/.meta/config.json
index c374f8caab9..8750a7497f9 100644
--- a/exercises/practice/binary-search-tree/.meta/config.json
+++ b/exercises/practice/binary-search-tree/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Insert and search for numbers in a binary tree.",
"authors": [
"rodrigocam"
],
@@ -22,6 +21,7 @@
".meta/example.py"
]
},
+ "blurb": "Insert and search for numbers in a binary tree.",
"source": "Josh Cheek",
"source_url": "https://twitter.com/josh_cheek"
}
diff --git a/exercises/practice/binary-search/.meta/config.json b/exercises/practice/binary-search/.meta/config.json
index cc3fb2823f6..8b76b593d18 100644
--- a/exercises/practice/binary-search/.meta/config.json
+++ b/exercises/practice/binary-search/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement a binary search algorithm.",
"authors": [
"michaelkunc"
],
@@ -30,6 +29,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement a binary search algorithm.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Binary_search_algorithm"
}
diff --git a/exercises/practice/bob/.meta/config.json b/exercises/practice/bob/.meta/config.json
index 1c91b437606..ec0fc617762 100644
--- a/exercises/practice/bob/.meta/config.json
+++ b/exercises/practice/bob/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Bob is a lackadaisical teenager. In conversation, his responses are very limited.",
"authors": [],
"contributors": [
"0xae",
@@ -42,6 +41,7 @@
".meta/example.py"
]
},
+ "blurb": "Bob is a lackadaisical teenager. In conversation, his responses are very limited.",
"source": "Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial.",
"source_url": "https://pine.fm/LearnToProgram/?Chapter=06"
}
diff --git a/exercises/practice/book-store/.meta/config.json b/exercises/practice/book-store/.meta/config.json
index ed1c0f5556e..87dd6d2cc65 100644
--- a/exercises/practice/book-store/.meta/config.json
+++ b/exercises/practice/book-store/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts of multiple-book purchases.",
"authors": [
"behrtam"
],
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts of multiple-book purchases.",
"source": "Inspired by the harry potter kata from Cyber-Dojo.",
"source_url": "https://cyber-dojo.org"
}
diff --git a/exercises/practice/bowling/.meta/config.json b/exercises/practice/bowling/.meta/config.json
index 475fd3bf71b..e97b0e69c81 100644
--- a/exercises/practice/bowling/.meta/config.json
+++ b/exercises/practice/bowling/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Score a bowling game.",
"authors": [
"aes421"
],
@@ -22,6 +21,7 @@
".meta/example.py"
]
},
+ "blurb": "Score a bowling game.",
"source": "The Bowling Game Kata from UncleBob",
"source_url": "https://web.archive.org/web/20221001111000/http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata"
}
diff --git a/exercises/practice/change/.meta/config.json b/exercises/practice/change/.meta/config.json
index 191ea1fede3..2d117feec9a 100644
--- a/exercises/practice/change/.meta/config.json
+++ b/exercises/practice/change/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Correctly determine change to be given using the least number of coins.",
"authors": [
"justani"
],
@@ -26,6 +25,7 @@
".meta/example.py"
]
},
+ "blurb": "Correctly determine change to be given using the least number of coins.",
"source": "Software Craftsmanship - Coin Change Kata",
"source_url": "https://web.archive.org/web/20130115115225/http://craftsmanship.sv.cmu.edu:80/exercises/coin-change-kata"
}
diff --git a/exercises/practice/circular-buffer/.meta/config.json b/exercises/practice/circular-buffer/.meta/config.json
index ba40c18f705..09fc60b45d8 100644
--- a/exercises/practice/circular-buffer/.meta/config.json
+++ b/exercises/practice/circular-buffer/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "A data structure that uses a single, fixed-size buffer as if it were connected end-to-end.",
"authors": [
"behrtam"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "A data structure that uses a single, fixed-size buffer as if it were connected end-to-end.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Circular_buffer"
}
diff --git a/exercises/practice/clock/.meta/config.json b/exercises/practice/clock/.meta/config.json
index 441a88c49a3..0f1b8fa94b2 100644
--- a/exercises/practice/clock/.meta/config.json
+++ b/exercises/practice/clock/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement a clock that handles times without dates.",
"authors": [
"chrisftw"
],
@@ -30,6 +29,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement a clock that handles times without dates.",
"source": "Pairing session with Erin Drummond",
"source_url": "https://twitter.com/ebdrummond"
}
diff --git a/exercises/practice/collatz-conjecture/.meta/config.json b/exercises/practice/collatz-conjecture/.meta/config.json
index 93ed61f514a..e5eda73e1df 100644
--- a/exercises/practice/collatz-conjecture/.meta/config.json
+++ b/exercises/practice/collatz-conjecture/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Calculate the number of steps to reach 1 using the Collatz conjecture.",
"authors": [
"zwaltman"
],
@@ -26,6 +25,7 @@
".meta/example.py"
]
},
+ "blurb": "Calculate the number of steps to reach 1 using the Collatz conjecture.",
"source": "An unsolved problem in mathematics named after mathematician Lothar Collatz",
"source_url": "https://en.wikipedia.org/wiki/3x_%2B_1_problem"
}
diff --git a/exercises/practice/complex-numbers/.meta/config.json b/exercises/practice/complex-numbers/.meta/config.json
index 55a9efb45b6..b79e6544a5e 100644
--- a/exercises/practice/complex-numbers/.meta/config.json
+++ b/exercises/practice/complex-numbers/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement complex numbers.",
"authors": [],
"contributors": [
"cmccandless",
@@ -24,6 +23,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement complex numbers.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Complex_number"
}
diff --git a/exercises/practice/connect/.meta/config.json b/exercises/practice/connect/.meta/config.json
index b8ca64254d0..dcabaa7b8f5 100644
--- a/exercises/practice/connect/.meta/config.json
+++ b/exercises/practice/connect/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Compute the result for a game of Hex / Polygon.",
"authors": [
"yunchih"
],
@@ -21,5 +20,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Compute the result for a game of Hex / Polygon."
}
diff --git a/exercises/practice/crypto-square/.meta/config.json b/exercises/practice/crypto-square/.meta/config.json
index c8e34bd0516..7c7de94c30a 100644
--- a/exercises/practice/crypto-square/.meta/config.json
+++ b/exercises/practice/crypto-square/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement the classic method for composing secret messages called a square code.",
"authors": [
"betegelse"
],
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement the classic method for composing secret messages called a square code.",
"source": "J Dalbey's Programming Practice problems",
"source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
diff --git a/exercises/practice/custom-set/.meta/config.json b/exercises/practice/custom-set/.meta/config.json
index b3d9a8de5de..f3c73c2b52f 100644
--- a/exercises/practice/custom-set/.meta/config.json
+++ b/exercises/practice/custom-set/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Create a custom set type.",
"authors": [
"cmccandless"
],
@@ -18,5 +17,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Create a custom set type."
}
diff --git a/exercises/practice/darts/.meta/config.json b/exercises/practice/darts/.meta/config.json
index 294ae201602..a306b38c150 100644
--- a/exercises/practice/darts/.meta/config.json
+++ b/exercises/practice/darts/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Write a function that returns the earned points in a single toss of a Darts game.",
"authors": [
"GascaK"
],
@@ -25,5 +24,6 @@
".meta/example.py"
]
},
+ "blurb": "Write a function that returns the earned points in a single toss of a Darts game.",
"source": "Inspired by an exercise created by a professor Della Paolera in Argentina"
}
diff --git a/exercises/practice/diamond/.meta/config.json b/exercises/practice/diamond/.meta/config.json
index 0108dfaa9bc..f349d659cb4 100644
--- a/exercises/practice/diamond/.meta/config.json
+++ b/exercises/practice/diamond/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a letter, print a diamond starting with 'A' with the supplied letter at the widest point.",
"authors": [
"behrtam"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a letter, print a diamond starting with 'A' with the supplied letter at the widest point.",
"source": "Seb Rose",
"source_url": "https://web.archive.org/web/20220807163751/http://claysnow.co.uk/recycling-tests-in-tdd/"
}
diff --git a/exercises/practice/difference-of-squares/.meta/config.json b/exercises/practice/difference-of-squares/.meta/config.json
index 3dc17de6735..fe714d49db7 100644
--- a/exercises/practice/difference-of-squares/.meta/config.json
+++ b/exercises/practice/difference-of-squares/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.",
"authors": [
"sjakobi"
],
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.",
"source": "Problem 6 at Project Euler",
"source_url": "https://projecteuler.net/problem=6"
}
diff --git a/exercises/practice/diffie-hellman/.meta/config.json b/exercises/practice/diffie-hellman/.meta/config.json
index feb9623d4ec..81e8cb85bd6 100644
--- a/exercises/practice/diffie-hellman/.meta/config.json
+++ b/exercises/practice/diffie-hellman/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Diffie-Hellman key exchange.",
"authors": [],
"contributors": [
"cmccandless",
@@ -20,6 +19,7 @@
".meta/example.py"
]
},
+ "blurb": "Diffie-Hellman key exchange.",
"source": "Wikipedia, 1024 bit key from www.cryptopp.com/wiki.",
"source_url": "https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange"
}
diff --git a/exercises/practice/dnd-character/.meta/config.json b/exercises/practice/dnd-character/.meta/config.json
index 087026ecb4b..8f2c3108d9f 100644
--- a/exercises/practice/dnd-character/.meta/config.json
+++ b/exercises/practice/dnd-character/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Randomly generate Dungeons & Dragons characters.",
"authors": [
"GascaK"
],
@@ -20,6 +19,7 @@
".meta/example.py"
]
},
+ "blurb": "Randomly generate Dungeons & Dragons characters.",
"source": "Simon Shine, Erik Schierboom",
"source_url": "https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945"
}
diff --git a/exercises/practice/dominoes/.meta/config.json b/exercises/practice/dominoes/.meta/config.json
index 0a0d049c460..2b7746f65b9 100644
--- a/exercises/practice/dominoes/.meta/config.json
+++ b/exercises/practice/dominoes/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Make a chain of dominoes.",
"authors": [
"cmccandless"
],
@@ -21,5 +20,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Make a chain of dominoes."
}
diff --git a/exercises/practice/dot-dsl/.meta/config.json b/exercises/practice/dot-dsl/.meta/config.json
index b285955f86e..928e8b2aa76 100644
--- a/exercises/practice/dot-dsl/.meta/config.json
+++ b/exercises/practice/dot-dsl/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Write a Domain Specific Language similar to the Graphviz dot language.",
"authors": [
"ackerleytng"
],
@@ -22,6 +21,7 @@
".meta/example.py"
]
},
+ "blurb": "Write a Domain Specific Language similar to the Graphviz dot language.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/DOT_(graph_description_language)"
}
diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json
index 777e51cb845..4d99e6314da 100644
--- a/exercises/practice/etl/.meta/config.json
+++ b/exercises/practice/etl/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "We are going to do the `Transform` step of an Extract-Transform-Load.",
"authors": [],
"contributors": [
"alirezaghey",
@@ -28,6 +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.",
"source_url": "https://turing.edu"
}
diff --git a/exercises/practice/flatten-array/.meta/config.json b/exercises/practice/flatten-array/.meta/config.json
index 818addda389..bd0ec35c03b 100644
--- a/exercises/practice/flatten-array/.meta/config.json
+++ b/exercises/practice/flatten-array/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Take a nested list and return a single list with all values except nil/null.",
"authors": [
"treyhunner"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Take a nested list and return a single list with all values except nil/null.",
"source": "Interview Question",
"source_url": "https://reference.wolfram.com/language/ref/Flatten.html"
}
diff --git a/exercises/practice/food-chain/.meta/config.json b/exercises/practice/food-chain/.meta/config.json
index 1f359e9b0e2..4a42e70766e 100644
--- a/exercises/practice/food-chain/.meta/config.json
+++ b/exercises/practice/food-chain/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'.",
"authors": [
"luanjpb"
],
@@ -21,6 +20,7 @@
".meta/example.py"
]
},
+ "blurb": "Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/There_Was_an_Old_Lady_Who_Swallowed_a_Fly"
}
diff --git a/exercises/practice/forth/.meta/config.json b/exercises/practice/forth/.meta/config.json
index 3cc0263fe5b..569eddd367b 100644
--- a/exercises/practice/forth/.meta/config.json
+++ b/exercises/practice/forth/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement an evaluator for a very simple subset of Forth.",
"authors": [
"cmccandless"
],
@@ -23,5 +22,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Implement an evaluator for a very simple subset of Forth."
}
diff --git a/exercises/practice/gigasecond/.meta/config.json b/exercises/practice/gigasecond/.meta/config.json
index b0af657f37f..59b2d30cc9e 100644
--- a/exercises/practice/gigasecond/.meta/config.json
+++ b/exercises/practice/gigasecond/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a moment, determine the moment that would be after a gigasecond has passed.",
"authors": [],
"contributors": [
"behrtam",
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a moment, determine the moment that would be after a gigasecond has passed.",
"source": "Chapter 9 in Chris Pine's online Learn to Program tutorial.",
"source_url": "https://pine.fm/LearnToProgram/?Chapter=09"
}
diff --git a/exercises/practice/go-counting/.meta/config.json b/exercises/practice/go-counting/.meta/config.json
index 06e04c3431c..0cfd3ff8bc3 100644
--- a/exercises/practice/go-counting/.meta/config.json
+++ b/exercises/practice/go-counting/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Count the scored points on a Go board.",
"authors": [
"yunchih"
],
@@ -21,5 +20,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Count the scored points on a Go board."
}
diff --git a/exercises/practice/grade-school/.meta/config.json b/exercises/practice/grade-school/.meta/config.json
index 6fa1b39a156..1a93a3e238e 100644
--- a/exercises/practice/grade-school/.meta/config.json
+++ b/exercises/practice/grade-school/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given students' names along with the grade that they are in, create a roster for the school.",
"authors": [],
"contributors": [
"behrtam",
@@ -28,5 +27,6 @@
".meta/example.py"
]
},
+ "blurb": "Given students' names along with the grade that they are in, create a roster for the school.",
"source": "A pairing session with Phil Battos at gSchool"
}
diff --git a/exercises/practice/grains/.meta/config.json b/exercises/practice/grains/.meta/config.json
index 12b2a81dcad..4e59df7431c 100644
--- a/exercises/practice/grains/.meta/config.json
+++ b/exercises/practice/grains/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.",
"authors": [],
"contributors": [
"behrtam",
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.",
"source": "The CodeRanch Cattle Drive, Assignment 6",
"source_url": "https://coderanch.com/wiki/718824/Grains"
}
diff --git a/exercises/practice/grep/.meta/config.json b/exercises/practice/grep/.meta/config.json
index 57c0ec8e079..b79d15e0c75 100644
--- a/exercises/practice/grep/.meta/config.json
+++ b/exercises/practice/grep/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Search a file for lines matching a regular expression pattern. Return the line number and contents of each matching line.",
"authors": [
"behrtam"
],
@@ -26,6 +25,7 @@
".meta/example.py"
]
},
+ "blurb": "Search a file for lines matching a regular expression pattern. Return the line number and contents of each matching line.",
"source": "Conversation with Nate Foster.",
"source_url": "https://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf"
}
diff --git a/exercises/practice/hamming/.meta/config.json b/exercises/practice/hamming/.meta/config.json
index 34b97637ba0..1280ded9127 100644
--- a/exercises/practice/hamming/.meta/config.json
+++ b/exercises/practice/hamming/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Calculate the Hamming difference between two DNA strands.",
"authors": [
"betegelse"
],
@@ -32,6 +31,7 @@
".meta/example.py"
]
},
+ "blurb": "Calculate the Hamming difference between two DNA strands.",
"source": "The Calculating Point Mutations problem at Rosalind",
"source_url": "https://rosalind.info/problems/hamm/"
}
diff --git a/exercises/practice/hangman/.meta/config.json b/exercises/practice/hangman/.meta/config.json
index acd8ab23c62..1d051487536 100644
--- a/exercises/practice/hangman/.meta/config.json
+++ b/exercises/practice/hangman/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement the logic of the hangman game using functional reactive programming.",
"authors": [
"junming403"
],
@@ -19,5 +18,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Implement the logic of the hangman game using functional reactive programming."
}
diff --git a/exercises/practice/hello-world/.meta/config.json b/exercises/practice/hello-world/.meta/config.json
index c3520cfdeea..fc3599fd070 100644
--- a/exercises/practice/hello-world/.meta/config.json
+++ b/exercises/practice/hello-world/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "The classical introductory exercise. Just say \"Hello, World!\".",
"authors": [
"michaelem"
],
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "The classical introductory exercise. Just say \"Hello, World!\".",
"source": "This is an exercise to introduce users to using Exercism",
"source_url": "https://en.wikipedia.org/wiki/%22Hello,_world!%22_program"
}
diff --git a/exercises/practice/high-scores/.meta/config.json b/exercises/practice/high-scores/.meta/config.json
index 37ec344ff54..c7121131581 100644
--- a/exercises/practice/high-scores/.meta/config.json
+++ b/exercises/practice/high-scores/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Manage a player's High Score list.",
"authors": [
"behrtam"
],
@@ -25,5 +24,6 @@
".meta/example.py"
]
},
+ "blurb": "Manage a player's High Score list.",
"source": "Tribute to the eighties' arcade game Frogger"
}
diff --git a/exercises/practice/house/.meta/config.json b/exercises/practice/house/.meta/config.json
index 7de79da60f8..328c8c913ee 100644
--- a/exercises/practice/house/.meta/config.json
+++ b/exercises/practice/house/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Output the nursery rhyme 'This is the House that Jack Built'.",
"authors": [
"betegelse"
],
@@ -26,6 +25,7 @@
".meta/example.py"
]
},
+ "blurb": "Output the nursery rhyme 'This is the House that Jack Built'.",
"source": "British nursery rhyme",
"source_url": "https://en.wikipedia.org/wiki/This_Is_The_House_That_Jack_Built"
}
diff --git a/exercises/practice/isbn-verifier/.meta/config.json b/exercises/practice/isbn-verifier/.meta/config.json
index 9225f768254..78b6dcceb10 100644
--- a/exercises/practice/isbn-verifier/.meta/config.json
+++ b/exercises/practice/isbn-verifier/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Check if a given string is a valid ISBN-10 number.",
"authors": [
"pheanex"
],
@@ -22,6 +21,7 @@
".meta/example.py"
]
},
+ "blurb": "Check if a given string is a valid ISBN-10 number.",
"source": "Converting a string into a number and some basic processing utilizing a relatable real world example.",
"source_url": "https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation"
}
diff --git a/exercises/practice/isogram/.meta/config.json b/exercises/practice/isogram/.meta/config.json
index 3c6bcb4e853..302b30a592c 100644
--- a/exercises/practice/isogram/.meta/config.json
+++ b/exercises/practice/isogram/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Determine if a word or phrase is an isogram.",
"authors": [
"behrtam"
],
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "Determine if a word or phrase is an isogram.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Isogram"
}
diff --git a/exercises/practice/kindergarten-garden/.meta/config.json b/exercises/practice/kindergarten-garden/.meta/config.json
index 396235f79d9..58767fd3908 100644
--- a/exercises/practice/kindergarten-garden/.meta/config.json
+++ b/exercises/practice/kindergarten-garden/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.",
"authors": [
"sjakobi"
],
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.",
"source": "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/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json
index d76f3d08816..91b48036a24 100644
--- a/exercises/practice/knapsack/.meta/config.json
+++ b/exercises/practice/knapsack/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a knapsack that can only carry a certain weight, determine which items to put in the knapsack in order to maximize their combined value.",
"authors": [
"omer-g"
],
@@ -21,6 +20,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a knapsack that can only carry a certain weight, determine which items to put in the knapsack in order to maximize their combined value.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Knapsack_problem"
}
diff --git a/exercises/practice/largest-series-product/.meta/config.json b/exercises/practice/largest-series-product/.meta/config.json
index 8b12e5b55cc..138f3592ecd 100644
--- a/exercises/practice/largest-series-product/.meta/config.json
+++ b/exercises/practice/largest-series-product/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a string of digits, calculate the largest product for a contiguous substring of digits of length n.",
"authors": [
"sjakobi"
],
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a string of digits, calculate the largest product for a contiguous substring of digits of length n.",
"source": "A variation on Problem 8 at Project Euler",
"source_url": "https://projecteuler.net/problem=8"
}
diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json
index 687638ae0b4..fd2bd240870 100644
--- a/exercises/practice/leap/.meta/config.json
+++ b/exercises/practice/leap/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a year, report if it is a leap year.",
"authors": [],
"contributors": [
"AnAccountForReportingBugs",
@@ -33,6 +32,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a year, report if it is a leap year.",
"source": "CodeRanch Cattle Drive, Assignment 3",
"source_url": "https://coderanch.com/t/718816/Leap"
}
diff --git a/exercises/practice/ledger/.meta/config.json b/exercises/practice/ledger/.meta/config.json
index 114fd310567..eeb5e2ccdf1 100644
--- a/exercises/practice/ledger/.meta/config.json
+++ b/exercises/practice/ledger/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Refactor a ledger printer.",
"authors": [
"cmccandless"
],
@@ -17,5 +16,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Refactor a ledger printer."
}
diff --git a/exercises/practice/linked-list/.meta/config.json b/exercises/practice/linked-list/.meta/config.json
index e239b92ffd7..c2d286f6678 100644
--- a/exercises/practice/linked-list/.meta/config.json
+++ b/exercises/practice/linked-list/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement a doubly linked list.",
"authors": [
"behrtam"
],
@@ -23,5 +22,6 @@
".meta/example.py"
]
},
+ "blurb": "Implement a doubly linked list.",
"source": "Classic computer science topic"
}
diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json
index 91424481b46..efc025a3543 100644
--- a/exercises/practice/list-ops/.meta/config.json
+++ b/exercises/practice/list-ops/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement basic list operations.",
"authors": [
"behrtam"
],
@@ -25,5 +24,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Implement basic list operations."
}
diff --git a/exercises/practice/luhn/.meta/config.json b/exercises/practice/luhn/.meta/config.json
index ead518b8e1f..c9a7c188e7d 100644
--- a/exercises/practice/luhn/.meta/config.json
+++ b/exercises/practice/luhn/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a number determine whether or not it is valid per the Luhn formula.",
"authors": [
"sjakobi"
],
@@ -34,6 +33,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a number determine whether or not it is valid per the Luhn formula.",
"source": "The Luhn Algorithm on Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Luhn_algorithm"
}
diff --git a/exercises/practice/markdown/.meta/config.json b/exercises/practice/markdown/.meta/config.json
index c607b6d085c..012bed83145 100644
--- a/exercises/practice/markdown/.meta/config.json
+++ b/exercises/practice/markdown/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Refactor a Markdown parser.",
"authors": [
"Paul-Ilyin"
],
@@ -22,5 +21,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Refactor a Markdown parser."
}
diff --git a/exercises/practice/matching-brackets/.meta/config.json b/exercises/practice/matching-brackets/.meta/config.json
index 77af8aa3e4b..b07c1abb835 100644
--- a/exercises/practice/matching-brackets/.meta/config.json
+++ b/exercises/practice/matching-brackets/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Make sure the brackets and braces all match.",
"authors": [
"behrtam"
],
@@ -28,5 +27,6 @@
".meta/example.py"
]
},
+ "blurb": "Make sure the brackets and braces all match.",
"source": "Ginna Baker"
}
diff --git a/exercises/practice/matrix/.meta/config.json b/exercises/practice/matrix/.meta/config.json
index 5b9aaf85ba6..544b7bb0d6b 100644
--- a/exercises/practice/matrix/.meta/config.json
+++ b/exercises/practice/matrix/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a string representing a matrix of numbers, return the rows and columns of that matrix.",
"authors": [
"sjakobi"
],
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a string representing a matrix of numbers, return the rows and columns of that matrix.",
"source": "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/meetup/.meta/config.json b/exercises/practice/meetup/.meta/config.json
index 693d87b2573..a001dda1433 100644
--- a/exercises/practice/meetup/.meta/config.json
+++ b/exercises/practice/meetup/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Calculate the date of meetups.",
"authors": [
"sjakobi"
],
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Calculate the date of meetups.",
"source": "Jeremy Hinegardner mentioned a Boulder meetup that happens on the Wednesteenth of every month",
"source_url": "https://twitter.com/copiousfreetime"
}
diff --git a/exercises/practice/minesweeper/.meta/config.json b/exercises/practice/minesweeper/.meta/config.json
index 3e08f9e376f..a4f6101f451 100644
--- a/exercises/practice/minesweeper/.meta/config.json
+++ b/exercises/practice/minesweeper/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Add the numbers to a minesweeper board.",
"authors": [
"betegelse"
],
@@ -29,5 +28,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Add the numbers to a minesweeper board."
}
diff --git a/exercises/practice/nth-prime/.meta/config.json b/exercises/practice/nth-prime/.meta/config.json
index c38d07e93a4..e4466db8c87 100644
--- a/exercises/practice/nth-prime/.meta/config.json
+++ b/exercises/practice/nth-prime/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a number n, determine what the nth prime is.",
"authors": [
"sjakobi"
],
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a number n, determine what the nth prime is.",
"source": "A variation on Problem 7 at Project Euler",
"source_url": "https://projecteuler.net/problem=7"
}
diff --git a/exercises/practice/ocr-numbers/.meta/config.json b/exercises/practice/ocr-numbers/.meta/config.json
index 669baa24a04..b37fe0d45f2 100644
--- a/exercises/practice/ocr-numbers/.meta/config.json
+++ b/exercises/practice/ocr-numbers/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or whether it is garbled.",
"authors": [
"betegelse"
],
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or whether it is garbled.",
"source": "Inspired by the Bank OCR kata",
"source_url": "https://codingdojo.org/kata/BankOCR/"
}
diff --git a/exercises/practice/paasio/.meta/config.json b/exercises/practice/paasio/.meta/config.json
index b8582c8dce8..a9c0b9c0fda 100644
--- a/exercises/practice/paasio/.meta/config.json
+++ b/exercises/practice/paasio/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Report network IO statistics.",
"authors": [
"AnAccountForReportingBugs"
],
@@ -18,6 +17,7 @@
".meta/example.py"
]
},
+ "blurb": "Report network IO statistics.",
"source": "Brian Matsuo",
"source_url": "https://github.com/bmatsuo"
}
diff --git a/exercises/practice/palindrome-products/.meta/config.json b/exercises/practice/palindrome-products/.meta/config.json
index 936f9e8b92a..fa833ed0c6b 100644
--- a/exercises/practice/palindrome-products/.meta/config.json
+++ b/exercises/practice/palindrome-products/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Detect palindrome products in a given range.",
"authors": [
"sjakobi"
],
@@ -30,6 +29,7 @@
".meta/example.py"
]
},
+ "blurb": "Detect palindrome products in a given range.",
"source": "Problem 4 at Project Euler",
"source_url": "https://projecteuler.net/problem=4"
}
diff --git a/exercises/practice/pangram/.meta/config.json b/exercises/practice/pangram/.meta/config.json
index 952e1f6357d..3d57bb9f854 100644
--- a/exercises/practice/pangram/.meta/config.json
+++ b/exercises/practice/pangram/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Determine if a sentence is a pangram.",
"authors": [
"behrtam"
],
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Determine if a sentence is a pangram.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Pangram"
}
diff --git a/exercises/practice/perfect-numbers/.meta/config.json b/exercises/practice/perfect-numbers/.meta/config.json
index 208b5094ecf..a77ccea6e9f 100644
--- a/exercises/practice/perfect-numbers/.meta/config.json
+++ b/exercises/practice/perfect-numbers/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers.",
"authors": [
"behrtam"
],
@@ -26,6 +25,7 @@
".meta/example.py"
]
},
+ "blurb": "Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers.",
"source": "Taken from Chapter 2 of Functional Thinking by Neal Ford.",
"source_url": "https://www.oreilly.com/library/view/functional-thinking/9781449365509/"
}
diff --git a/exercises/practice/phone-number/.meta/config.json b/exercises/practice/phone-number/.meta/config.json
index db2af4d694e..f2fe78bd3fa 100644
--- a/exercises/practice/phone-number/.meta/config.json
+++ b/exercises/practice/phone-number/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Clean up user-entered phone numbers so that they can be sent SMS messages.",
"authors": [],
"contributors": [
"AnAccountForReportingBugs",
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Clean up user-entered phone numbers so that they can be sent SMS messages.",
"source": "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/pig-latin/.meta/config.json b/exercises/practice/pig-latin/.meta/config.json
index b7fe96161a5..6dd8c462e33 100644
--- a/exercises/practice/pig-latin/.meta/config.json
+++ b/exercises/practice/pig-latin/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement a program that translates from English to Pig Latin.",
"authors": [
"behrtam"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement a program that translates from English to Pig Latin.",
"source": "The Pig Latin exercise at Test First Teaching by Ultrasaurus",
"source_url": "https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/"
}
diff --git a/exercises/practice/poker/.meta/config.json b/exercises/practice/poker/.meta/config.json
index 13812c3ea6e..7c4301c90bf 100644
--- a/exercises/practice/poker/.meta/config.json
+++ b/exercises/practice/poker/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Pick the best hand(s) from a list of poker hands.",
"authors": [
"MartinDelille"
],
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Pick the best hand(s) from a list of poker hands.",
"source": "Inspired by the training course from Udacity.",
"source_url": "https://www.udacity.com/course/viewer#!/c-cs212/"
}
diff --git a/exercises/practice/pov/.meta/config.json b/exercises/practice/pov/.meta/config.json
index 5b7a35254fe..cdc96ff934a 100644
--- a/exercises/practice/pov/.meta/config.json
+++ b/exercises/practice/pov/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Reparent a graph on a selected node.",
"authors": [
"cmccandless"
],
@@ -21,6 +20,7 @@
".meta/example.py"
]
},
+ "blurb": "Reparent a graph on a selected node.",
"source": "Adaptation of exercise from 4clojure",
"source_url": "https://www.4clojure.com/"
}
diff --git a/exercises/practice/prime-factors/.meta/config.json b/exercises/practice/prime-factors/.meta/config.json
index 1d89a38d6b7..f50d3fc5f6d 100644
--- a/exercises/practice/prime-factors/.meta/config.json
+++ b/exercises/practice/prime-factors/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Compute the prime factors of a given natural number.",
"authors": [],
"contributors": [
"behrtam",
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Compute the prime factors of a given natural number.",
"source": "The Prime Factors Kata by Uncle Bob",
"source_url": "https://web.archive.org/web/20221026171801/http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata"
}
diff --git a/exercises/practice/protein-translation/.meta/config.json b/exercises/practice/protein-translation/.meta/config.json
index 77ef86f422c..3a2569b1a82 100644
--- a/exercises/practice/protein-translation/.meta/config.json
+++ b/exercises/practice/protein-translation/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Translate RNA sequences into proteins.",
"authors": [
"cptjackson"
],
@@ -26,5 +25,6 @@
".meta/example.py"
]
},
+ "blurb": "Translate RNA sequences into proteins.",
"source": "Tyler Long"
}
diff --git a/exercises/practice/pythagorean-triplet/.meta/config.json b/exercises/practice/pythagorean-triplet/.meta/config.json
index 209e162326b..03d35139ac6 100644
--- a/exercises/practice/pythagorean-triplet/.meta/config.json
+++ b/exercises/practice/pythagorean-triplet/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product a * b * c.",
"authors": [
"betegelse"
],
@@ -32,6 +31,7 @@
".meta/example.py"
]
},
+ "blurb": "There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product a * b * c.",
"source": "Problem 9 at Project Euler",
"source_url": "https://projecteuler.net/problem=9"
}
diff --git a/exercises/practice/queen-attack/.meta/config.json b/exercises/practice/queen-attack/.meta/config.json
index a4558a4e5a6..91336ad151c 100644
--- a/exercises/practice/queen-attack/.meta/config.json
+++ b/exercises/practice/queen-attack/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given the position of two queens on a chess board, indicate whether or not they are positioned so that they can attack each other.",
"authors": [
"betegelse"
],
@@ -32,6 +31,7 @@
".meta/example.py"
]
},
+ "blurb": "Given the position of two queens on a chess board, indicate whether or not they are positioned so that they can attack each other.",
"source": "J Dalbey's Programming Practice problems",
"source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
diff --git a/exercises/practice/rail-fence-cipher/.meta/config.json b/exercises/practice/rail-fence-cipher/.meta/config.json
index 982acd240ae..9a904ef1dac 100644
--- a/exercises/practice/rail-fence-cipher/.meta/config.json
+++ b/exercises/practice/rail-fence-cipher/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement encoding and decoding for the rail fence cipher.",
"authors": [
"behrtam"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement encoding and decoding for the rail fence cipher.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher"
}
diff --git a/exercises/practice/raindrops/.meta/config.json b/exercises/practice/raindrops/.meta/config.json
index 2863a8da7f5..463a523712b 100644
--- a/exercises/practice/raindrops/.meta/config.json
+++ b/exercises/practice/raindrops/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Convert a number to a string, the content of which depends on the number's factors.",
"authors": [],
"contributors": [
"behrtam",
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Convert a number to a string, the content of which depends on the number's factors.",
"source": "A variation on FizzBuzz, a famous technical interview question that is intended to weed out potential candidates. That question is itself derived from Fizz Buzz, a popular children's game for teaching division.",
"source_url": "https://en.wikipedia.org/wiki/Fizz_buzz"
}
diff --git a/exercises/practice/rational-numbers/.meta/config.json b/exercises/practice/rational-numbers/.meta/config.json
index 6fb2bce69b0..67afa131c2e 100644
--- a/exercises/practice/rational-numbers/.meta/config.json
+++ b/exercises/practice/rational-numbers/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement rational numbers.",
"authors": [],
"contributors": [
"AnAccountForReportingBugs",
@@ -21,6 +20,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement rational numbers.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Rational_number"
}
diff --git a/exercises/practice/react/.meta/config.json b/exercises/practice/react/.meta/config.json
index 9e1d6a2b31a..71e3b58ad1e 100644
--- a/exercises/practice/react/.meta/config.json
+++ b/exercises/practice/react/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement a basic reactive system.",
"authors": [
"cmccandless"
],
@@ -19,5 +18,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Implement a basic reactive system."
}
diff --git a/exercises/practice/rectangles/.meta/config.json b/exercises/practice/rectangles/.meta/config.json
index a716c2c1e90..4010f7d67ad 100644
--- a/exercises/practice/rectangles/.meta/config.json
+++ b/exercises/practice/rectangles/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Count the rectangles in an ASCII diagram.",
"authors": [],
"contributors": [
"aboutroots",
@@ -23,5 +22,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Count the rectangles in an ASCII diagram."
}
diff --git a/exercises/practice/resistor-color-duo/.meta/config.json b/exercises/practice/resistor-color-duo/.meta/config.json
index 2384d5aa182..f8177d2b06d 100644
--- a/exercises/practice/resistor-color-duo/.meta/config.json
+++ b/exercises/practice/resistor-color-duo/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Convert color codes, as used on resistors, to a numeric value.",
"authors": [
"ee7"
],
@@ -22,6 +21,7 @@
".meta/example.py"
]
},
+ "blurb": "Convert color codes, as used on resistors, to a numeric value.",
"source": "Maud de Vries, Erik Schierboom",
"source_url": "https://github.com/exercism/problem-specifications/issues/1464"
}
diff --git a/exercises/practice/resistor-color-trio/.meta/config.json b/exercises/practice/resistor-color-trio/.meta/config.json
index 600b6cc227f..68dad4cd2a0 100644
--- a/exercises/practice/resistor-color-trio/.meta/config.json
+++ b/exercises/practice/resistor-color-trio/.meta/config.json
@@ -1,11 +1,8 @@
{
- "blurb": "Convert color codes, as used on resistors, to a human-readable label.",
"authors": [
"meatball133",
"bethanyg"
],
- "contributors": [
- ],
"files": {
"solution": [
"resistor_color_trio.py"
@@ -17,6 +14,7 @@
".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/.meta/config.json b/exercises/practice/resistor-color/.meta/config.json
index 3d1ac09574c..08c69e138e5 100644
--- a/exercises/practice/resistor-color/.meta/config.json
+++ b/exercises/practice/resistor-color/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Convert a resistor band's color to its numeric representation.",
"authors": [
"gabriel376"
],
@@ -20,6 +19,7 @@
".meta/example.py"
]
},
+ "blurb": "Convert a resistor band's color to its numeric representation.",
"source": "Maud de Vries, Erik Schierboom",
"source_url": "https://github.com/exercism/problem-specifications/issues/1458"
}
diff --git a/exercises/practice/rest-api/.meta/config.json b/exercises/practice/rest-api/.meta/config.json
index 63b86416c56..889bf7831d1 100644
--- a/exercises/practice/rest-api/.meta/config.json
+++ b/exercises/practice/rest-api/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement a RESTful API for tracking IOUs.",
"authors": [
"cmccandless"
],
@@ -23,5 +22,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Implement a RESTful API for tracking IOUs."
}
diff --git a/exercises/practice/reverse-string/.meta/config.json b/exercises/practice/reverse-string/.meta/config.json
index c000c4ca73c..1df068a9580 100644
--- a/exercises/practice/reverse-string/.meta/config.json
+++ b/exercises/practice/reverse-string/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Reverse a string.",
"authors": [
"sjwarner-bp"
],
@@ -23,6 +22,7 @@
".meta/example.py"
]
},
+ "blurb": "Reverse a string.",
"source": "Introductory challenge to reverse an input string",
"source_url": "https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb"
}
diff --git a/exercises/practice/rna-transcription/.meta/config.json b/exercises/practice/rna-transcription/.meta/config.json
index b310b8c0361..636aa7ed318 100644
--- a/exercises/practice/rna-transcription/.meta/config.json
+++ b/exercises/practice/rna-transcription/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a DNA strand, return its RNA Complement Transcription.",
"authors": [
"BrianHicks"
],
@@ -33,6 +32,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a DNA strand, return its RNA Complement Transcription.",
"source": "Hyperphysics",
"source_url": "https://web.archive.org/web/20220408112140/http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html"
}
diff --git a/exercises/practice/robot-name/.meta/config.json b/exercises/practice/robot-name/.meta/config.json
index 3122086f65c..648bc8e9fcc 100644
--- a/exercises/practice/robot-name/.meta/config.json
+++ b/exercises/practice/robot-name/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Manage robot factory settings.",
"authors": [],
"contributors": [
"ariso353",
@@ -26,5 +25,6 @@
".meta/example.py"
]
},
+ "blurb": "Manage robot factory settings.",
"source": "A debugging session with Paul Blackwell at gSchool."
}
diff --git a/exercises/practice/robot-simulator/.meta/config.json b/exercises/practice/robot-simulator/.meta/config.json
index 863eb1a9e0e..aa08e312027 100644
--- a/exercises/practice/robot-simulator/.meta/config.json
+++ b/exercises/practice/robot-simulator/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Write a robot simulator.",
"authors": [
"behrtam"
],
@@ -27,5 +26,6 @@
".meta/example.py"
]
},
+ "blurb": "Write a robot simulator.",
"source": "Inspired by an interview question at a famous company."
}
diff --git a/exercises/practice/roman-numerals/.meta/config.json b/exercises/practice/roman-numerals/.meta/config.json
index ea6611e8fb9..eab034a9f92 100644
--- a/exercises/practice/roman-numerals/.meta/config.json
+++ b/exercises/practice/roman-numerals/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Write a function to convert from normal numbers to Roman Numerals.",
"authors": [],
"contributors": [
"behrtam",
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Write a function to convert from normal numbers to Roman Numerals.",
"source": "The Roman Numeral Kata",
"source_url": "https://codingdojo.org/kata/RomanNumerals/"
}
diff --git a/exercises/practice/rotational-cipher/.meta/config.json b/exercises/practice/rotational-cipher/.meta/config.json
index 412506fa986..d21c3ca018a 100644
--- a/exercises/practice/rotational-cipher/.meta/config.json
+++ b/exercises/practice/rotational-cipher/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.",
"authors": [
"krapes"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Caesar_cipher"
}
diff --git a/exercises/practice/run-length-encoding/.meta/config.json b/exercises/practice/run-length-encoding/.meta/config.json
index 8b6dc72f95a..7d8f389afee 100644
--- a/exercises/practice/run-length-encoding/.meta/config.json
+++ b/exercises/practice/run-length-encoding/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement run-length encoding and decoding.",
"authors": [
"behrtam"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement run-length encoding and decoding.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Run-length_encoding"
}
diff --git a/exercises/practice/saddle-points/.meta/config.json b/exercises/practice/saddle-points/.meta/config.json
index 7deb5f4aaf0..ba7ae52c4e5 100644
--- a/exercises/practice/saddle-points/.meta/config.json
+++ b/exercises/practice/saddle-points/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Detect saddle points in a matrix.",
"authors": [
"betegelse"
],
@@ -35,6 +34,7 @@
".meta/example.py"
]
},
+ "blurb": "Detect saddle points in a matrix.",
"source": "J Dalbey's Programming Practice problems",
"source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
diff --git a/exercises/practice/satellite/.meta/config.json b/exercises/practice/satellite/.meta/config.json
index 0a7eda0bd7b..19dadf3cd94 100644
--- a/exercises/practice/satellite/.meta/config.json
+++ b/exercises/practice/satellite/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Rebuild binary trees from pre-order and in-order traversals.",
"authors": [
"AnAccountForReportingBugs"
],
@@ -21,5 +20,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Rebuild binary trees from pre-order and in-order traversals."
}
diff --git a/exercises/practice/say/.meta/config.json b/exercises/practice/say/.meta/config.json
index 506533a1138..1090a04a475 100644
--- a/exercises/practice/say/.meta/config.json
+++ b/exercises/practice/say/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a number from 0 to 999,999,999,999, spell out that number in English.",
"authors": [
"behrtam"
],
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a number from 0 to 999,999,999,999, spell out that number in English.",
"source": "A variation on the JavaRanch CattleDrive, Assignment 4",
"source_url": "https://coderanch.com/wiki/718804"
}
diff --git a/exercises/practice/scale-generator/.meta/config.json b/exercises/practice/scale-generator/.meta/config.json
index a359c42fc0d..26c07d56d6c 100644
--- a/exercises/practice/scale-generator/.meta/config.json
+++ b/exercises/practice/scale-generator/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Generate musical scales, given a starting note and a set of intervals.",
"authors": [
"cptjackson"
],
@@ -23,5 +22,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Generate musical scales, given a starting note and a set of intervals."
}
diff --git a/exercises/practice/scrabble-score/.meta/config.json b/exercises/practice/scrabble-score/.meta/config.json
index dafe376ccaa..d8ff8fb31c6 100644
--- a/exercises/practice/scrabble-score/.meta/config.json
+++ b/exercises/practice/scrabble-score/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a word, compute the Scrabble score for that word.",
"authors": [],
"contributors": [
"behrtam",
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a word, compute the Scrabble score for that word.",
"source": "Inspired by the Extreme Startup game",
"source_url": "https://github.com/rchatley/extreme_startup"
}
diff --git a/exercises/practice/secret-handshake/.meta/config.json b/exercises/practice/secret-handshake/.meta/config.json
index ee9aeb52a13..cca237b478e 100644
--- a/exercises/practice/secret-handshake/.meta/config.json
+++ b/exercises/practice/secret-handshake/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a decimal number, convert it to the appropriate sequence of events for a secret handshake.",
"authors": [
"betegelse"
],
@@ -30,6 +29,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a decimal number, convert it to the appropriate sequence of events for a secret handshake.",
"source": "Bert, in Mary Poppins",
"source_url": "https://www.imdb.com/title/tt0058331/quotes/qt0437047"
}
diff --git a/exercises/practice/series/.meta/config.json b/exercises/practice/series/.meta/config.json
index 9953afd602c..3faaa9ae201 100644
--- a/exercises/practice/series/.meta/config.json
+++ b/exercises/practice/series/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a string of digits, output all the contiguous substrings of length `n` in that string.",
"authors": [
"sjakobi"
],
@@ -27,6 +26,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a string of digits, output all the contiguous substrings of length `n` in that string.",
"source": "A subset of the Problem 8 at Project Euler",
"source_url": "https://projecteuler.net/problem=8"
}
diff --git a/exercises/practice/sgf-parsing/.meta/config.json b/exercises/practice/sgf-parsing/.meta/config.json
index b8461120b44..284ea0fb2f9 100644
--- a/exercises/practice/sgf-parsing/.meta/config.json
+++ b/exercises/practice/sgf-parsing/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Parsing a Smart Game Format string.",
"authors": [
"cmccandless"
],
@@ -22,5 +21,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Parsing a Smart Game Format string."
}
diff --git a/exercises/practice/sieve/.meta/config.json b/exercises/practice/sieve/.meta/config.json
index ccf94249879..7bf97ee2c83 100644
--- a/exercises/practice/sieve/.meta/config.json
+++ b/exercises/practice/sieve/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Use the Sieve of Eratosthenes to find all the primes from 2 up to a given number.",
"authors": [
"sjakobi"
],
@@ -29,6 +28,7 @@
".meta/example.py"
]
},
+ "blurb": "Use the Sieve of Eratosthenes to find all the primes from 2 up to a given number.",
"source": "Sieve of Eratosthenes at Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"
}
diff --git a/exercises/practice/simple-cipher/.meta/config.json b/exercises/practice/simple-cipher/.meta/config.json
index 2176f407c5d..0dc1687acfe 100644
--- a/exercises/practice/simple-cipher/.meta/config.json
+++ b/exercises/practice/simple-cipher/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement a simple shift cipher like Caesar and a more secure substitution cipher.",
"authors": [
"betegelse"
],
@@ -31,6 +30,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement a simple shift cipher like Caesar and a more secure substitution cipher.",
"source": "Substitution Cipher at Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Substitution_cipher"
}
diff --git a/exercises/practice/simple-linked-list/.meta/config.json b/exercises/practice/simple-linked-list/.meta/config.json
index 7dd52524438..2fc136a325f 100644
--- a/exercises/practice/simple-linked-list/.meta/config.json
+++ b/exercises/practice/simple-linked-list/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Write a simple linked list implementation that uses Elements and a List.",
"authors": [
"cmccandless"
],
@@ -20,6 +19,7 @@
".meta/example.py"
]
},
+ "blurb": "Write a simple linked list implementation that uses Elements and a List.",
"source": "Inspired by 'Data Structures and Algorithms with Object-Oriented Design Patterns in Ruby', singly linked-lists.",
"source_url": "https://web.archive.org/web/20160731005714/http://brpreiss.com/books/opus8/html/page96.html"
}
diff --git a/exercises/practice/space-age/.meta/config.json b/exercises/practice/space-age/.meta/config.json
index 738e0fe39e1..174af6bfc3b 100644
--- a/exercises/practice/space-age/.meta/config.json
+++ b/exercises/practice/space-age/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given an age in seconds, calculate how old someone is in terms of a given planet's solar years.",
"authors": [],
"contributors": [
"abhijitparida",
@@ -28,6 +27,7 @@
".meta/example.py"
]
},
+ "blurb": "Given an age in seconds, calculate how old someone is in terms of a given planet's solar years.",
"source": "Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial.",
"source_url": "https://pine.fm/LearnToProgram/?Chapter=01"
}
diff --git a/exercises/practice/spiral-matrix/.meta/config.json b/exercises/practice/spiral-matrix/.meta/config.json
index 7fedd424c05..84a0d9aa4b3 100644
--- a/exercises/practice/spiral-matrix/.meta/config.json
+++ b/exercises/practice/spiral-matrix/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": " Given the size, return a square matrix of numbers in spiral order.",
"authors": [
"chgraef"
],
@@ -21,6 +20,7 @@
".meta/example.py"
]
},
+ "blurb": " Given the size, return a square matrix of numbers in spiral order.",
"source": "Reddit r/dailyprogrammer challenge #320 [Easy] Spiral Ascension.",
"source_url": "https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/"
}
diff --git a/exercises/practice/sublist/.meta/config.json b/exercises/practice/sublist/.meta/config.json
index 63f53773883..f4aeb47de5b 100644
--- a/exercises/practice/sublist/.meta/config.json
+++ b/exercises/practice/sublist/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Write a function to determine if a list is a sublist of another list.",
"authors": [
"betegelse"
],
@@ -30,5 +29,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Write a function to determine if a list is a sublist of another list."
}
diff --git a/exercises/practice/sum-of-multiples/.meta/config.json b/exercises/practice/sum-of-multiples/.meta/config.json
index bc2eba2a1cf..cc5f1ce8711 100644
--- a/exercises/practice/sum-of-multiples/.meta/config.json
+++ b/exercises/practice/sum-of-multiples/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a number, find the sum of all the multiples of particular numbers up to but not including that number.",
"authors": [
"sjakobi"
],
@@ -31,6 +30,7 @@
".meta/example.py"
]
},
+ "blurb": "Given a number, find the sum of all the multiples of particular numbers up to but not including that number.",
"source": "A variation on Problem 1 at Project Euler",
"source_url": "https://projecteuler.net/problem=1"
}
diff --git a/exercises/practice/tournament/.meta/config.json b/exercises/practice/tournament/.meta/config.json
index 2b6214d536c..8f7263e78bf 100644
--- a/exercises/practice/tournament/.meta/config.json
+++ b/exercises/practice/tournament/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Tally the results of a small football competition.",
"authors": [
"behrtam"
],
@@ -24,5 +23,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Tally the results of a small football competition."
}
diff --git a/exercises/practice/transpose/.meta/config.json b/exercises/practice/transpose/.meta/config.json
index 44dc8d83642..5eb2b08ef74 100644
--- a/exercises/practice/transpose/.meta/config.json
+++ b/exercises/practice/transpose/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Take input text and output it transposed.",
"authors": [
"nithin-vijayan"
],
@@ -26,6 +25,7 @@
".meta/example.py"
]
},
+ "blurb": "Take input text and output it transposed.",
"source": "Reddit r/dailyprogrammer challenge #270 [Easy].",
"source_url": "https://www.reddit.com/r/dailyprogrammer/comments/4msu2x/challenge_270_easy_transpose_the_input_text"
}
diff --git a/exercises/practice/tree-building/.meta/config.json b/exercises/practice/tree-building/.meta/config.json
index 1e798edb593..00d1da4b4b2 100644
--- a/exercises/practice/tree-building/.meta/config.json
+++ b/exercises/practice/tree-building/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Refactor a tree building algorithm.",
"authors": [
"clapmyhands"
],
@@ -19,5 +18,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Refactor a tree building algorithm."
}
diff --git a/exercises/practice/triangle/.meta/config.json b/exercises/practice/triangle/.meta/config.json
index d5264660fe3..041bf28ccfb 100644
--- a/exercises/practice/triangle/.meta/config.json
+++ b/exercises/practice/triangle/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Determine if a triangle is equilateral, isosceles, or scalene.",
"authors": [],
"contributors": [
"behrtam",
@@ -31,6 +30,7 @@
".meta/example.py"
]
},
+ "blurb": "Determine if a triangle is equilateral, isosceles, or scalene.",
"source": "The Ruby Koans triangle project, parts 1 & 2",
"source_url": "https://web.archive.org/web/20220831105330/http://rubykoans.com"
}
diff --git a/exercises/practice/twelve-days/.meta/config.json b/exercises/practice/twelve-days/.meta/config.json
index 2ed3689f68e..06c0b7011f3 100644
--- a/exercises/practice/twelve-days/.meta/config.json
+++ b/exercises/practice/twelve-days/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Output the lyrics to 'The Twelve Days of Christmas'.",
"authors": [
"sjakobi"
],
@@ -26,6 +25,7 @@
".meta/example.py"
]
},
+ "blurb": "Output the lyrics to 'The Twelve Days of Christmas'.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/The_Twelve_Days_of_Christmas_(song)"
}
diff --git a/exercises/practice/two-bucket/.meta/config.json b/exercises/practice/two-bucket/.meta/config.json
index 177aedc4e45..9934289f1f3 100644
--- a/exercises/practice/two-bucket/.meta/config.json
+++ b/exercises/practice/two-bucket/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given two buckets of different size, demonstrate how to measure an exact number of liters.",
"authors": [
"parthsharma2"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Given two buckets of different size, demonstrate how to measure an exact number of liters.",
"source": "Water Pouring Problem",
"source_url": "https://demonstrations.wolfram.com/WaterPouringProblem/"
}
diff --git a/exercises/practice/two-fer/.meta/config.json b/exercises/practice/two-fer/.meta/config.json
index 25f2cb7beac..24e71e76f70 100644
--- a/exercises/practice/two-fer/.meta/config.json
+++ b/exercises/practice/two-fer/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Create a sentence of the form \"One for X, one for me.\".",
"authors": [
"samwincott"
],
@@ -26,5 +25,6 @@
".meta/example.py"
]
},
+ "blurb": "Create a sentence of the form \"One for X, one for me.\".",
"source_url": "https://github.com/exercism/problem-specifications/issues/757"
}
diff --git a/exercises/practice/variable-length-quantity/.meta/config.json b/exercises/practice/variable-length-quantity/.meta/config.json
index 837cd6305a8..dc5843abbfd 100644
--- a/exercises/practice/variable-length-quantity/.meta/config.json
+++ b/exercises/practice/variable-length-quantity/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Implement variable length quantity encoding and decoding.",
"authors": [
"behrtam"
],
@@ -25,6 +24,7 @@
".meta/example.py"
]
},
+ "blurb": "Implement variable length quantity encoding and decoding.",
"source": "A poor Splice developer having to implement MIDI encoding/decoding.",
"source_url": "https://splice.com"
}
diff --git a/exercises/practice/word-count/.meta/config.json b/exercises/practice/word-count/.meta/config.json
index ffbe0db4764..c7134476f91 100644
--- a/exercises/practice/word-count/.meta/config.json
+++ b/exercises/practice/word-count/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Given a phrase, count the occurrences of each word in that phrase.",
"authors": [],
"contributors": [
"behrtam",
@@ -34,5 +33,6 @@
".meta/example.py"
]
},
+ "blurb": "Given a phrase, count the occurrences of each word in that phrase.",
"source": "This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour."
}
diff --git a/exercises/practice/word-search/.meta/config.json b/exercises/practice/word-search/.meta/config.json
index 635a740ed59..84c6ecddd25 100644
--- a/exercises/practice/word-search/.meta/config.json
+++ b/exercises/practice/word-search/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Create a program to solve a word search puzzle.",
"authors": [
"behrtam"
],
@@ -23,5 +22,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Create a program to solve a word search puzzle."
}
diff --git a/exercises/practice/wordy/.meta/config.json b/exercises/practice/wordy/.meta/config.json
index 9b388efefe1..07c63417075 100644
--- a/exercises/practice/wordy/.meta/config.json
+++ b/exercises/practice/wordy/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Parse and evaluate simple math word problems returning the answer as an integer.",
"authors": [
"betegelse"
],
@@ -35,6 +34,7 @@
".meta/example.py"
]
},
+ "blurb": "Parse and evaluate simple math word problems returning the answer as an integer.",
"source": "Inspired by one of the generated questions in the Extreme Startup game.",
"source_url": "https://github.com/rchatley/extreme_startup"
}
diff --git a/exercises/practice/yacht/.meta/config.json b/exercises/practice/yacht/.meta/config.json
index 5ae8137622e..78c685cb518 100644
--- a/exercises/practice/yacht/.meta/config.json
+++ b/exercises/practice/yacht/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Score a single throw of dice in the game Yacht.",
"authors": [],
"contributors": [
"aaditkamat",
@@ -20,6 +19,7 @@
".meta/example.py"
]
},
+ "blurb": "Score a single throw of dice in the game Yacht.",
"source": "James Kilfiger, using wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Yacht_(dice_game)"
}
diff --git a/exercises/practice/zebra-puzzle/.meta/config.json b/exercises/practice/zebra-puzzle/.meta/config.json
index b2375178345..b0b2da5f85e 100644
--- a/exercises/practice/zebra-puzzle/.meta/config.json
+++ b/exercises/practice/zebra-puzzle/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Solve the zebra puzzle.",
"authors": [
"sjakobi"
],
@@ -24,6 +23,7 @@
".meta/example.py"
]
},
+ "blurb": "Solve the zebra puzzle.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Zebra_Puzzle"
}
diff --git a/exercises/practice/zipper/.meta/config.json b/exercises/practice/zipper/.meta/config.json
index 13d91995cb7..c26c55507fa 100644
--- a/exercises/practice/zipper/.meta/config.json
+++ b/exercises/practice/zipper/.meta/config.json
@@ -1,5 +1,4 @@
{
- "blurb": "Creating a zipper for a binary tree.",
"authors": [
"cmccandless"
],
@@ -19,5 +18,6 @@
"example": [
".meta/example.py"
]
- }
+ },
+ "blurb": "Creating a zipper for a binary tree."
}
From 4a5ae757410c6de2b0870ca49b427fd8cc37dd2a Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 3 Dec 2022 00:25:20 -0800
Subject: [PATCH 264/932] Update poker from problem specs.
---
exercises/practice/poker/.meta/tests.toml | 17 +++++++++++++-
exercises/practice/poker/poker_test.py | 27 +++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/poker/.meta/tests.toml b/exercises/practice/poker/.meta/tests.toml
index 27a1fad29ef..76ac892d933 100644
--- a/exercises/practice/poker/.meta/tests.toml
+++ b/exercises/practice/poker/.meta/tests.toml
@@ -63,6 +63,9 @@ description = "aces can end a straight (10 J Q K A)"
[76856b0d-35cd-49ce-a492-fe5db53abc02]
description = "aces can start a straight (A 2 3 4 5)"
+[e214b7df-dcba-45d3-a2e5-342d8c46c286]
+description = "aces cannot be in the middle of a straight (Q K A 2 3)"
+
[6980c612-bbff-4914-b17a-b044e4e69ea1]
description = "both hands with a straight, tie goes to highest ranked card"
@@ -96,5 +99,17 @@ description = "with multiple decks, both hands with identical four of a kind, ti
[923bd910-dc7b-4f7d-a330-8b42ec10a3ac]
description = "straight flush beats four of a kind"
+[d9629e22-c943-460b-a951-2134d1b43346]
+description = "aces can end a straight flush (10 J Q K A)"
+
+[05d5ede9-64a5-4678-b8ae-cf4c595dc824]
+description = "aces can start a straight flush (A 2 3 4 5)"
+
+[ad655466-6d04-49e8-a50c-0043c3ac18ff]
+description = "aces cannot be in the middle of a straight flush (Q K A 2 3)"
+
[d0927f70-5aec-43db-aed8-1cbd1b6ee9ad]
-description = "both hands have straight flush, tie goes to highest-ranked card"
+description = "both hands have a straight flush, tie goes to highest-ranked card"
+
+[be620e09-0397-497b-ac37-d1d7a4464cfc]
+description = "even though an ace is usually high, a 5-high straight flush is the lowest-scoring straight flush"
diff --git a/exercises/practice/poker/poker_test.py b/exercises/practice/poker/poker_test.py
index 7951cb533a5..f436e48120b 100644
--- a/exercises/practice/poker/poker_test.py
+++ b/exercises/practice/poker/poker_test.py
@@ -115,6 +115,11 @@ def test_aces_can_start_a_straight_a_2_3_4_5(self):
best_hands(["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"]), ["4D AH 3S 2D 5C"]
)
+ def test_aces_cannot_be_in_the_middle_of_a_straight_q_k_a_2_3(self):
+ self.assertEqual(
+ best_hands(["2C 3D 7H 5H 2S", "QS KH AC 2D 3S"]), ["2C 3D 7H 5H 2S"]
+ )
+
def test_both_hands_with_a_straight_tie_goes_to_highest_ranked_card(self):
self.assertEqual(
best_hands(["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"]), ["5S 7H 8S 9D 6H"]
@@ -178,11 +183,33 @@ def test_straight_flush_beats_four_of_a_kind(self):
best_hands(["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"]), ["7S 8S 9S 6S 10S"]
)
+ def test_aces_can_end_a_straight_flush_10_j_q_k_a(self):
+ self.assertEqual(
+ best_hands(["KC AH AS AD AC", "10C JC QC KC AC"]), ["10C JC QC KC AC"]
+ )
+
+ def test_aces_can_start_a_straight_flush_a_2_3_4_5(self):
+ self.assertEqual(
+ best_hands(["KS AH AS AD AC", "4H AH 3H 2H 5H"]), ["4H AH 3H 2H 5H"]
+ )
+
+ def test_aces_cannot_be_in_the_middle_of_a_straight_flush_q_k_a_2_3(self):
+ self.assertEqual(
+ best_hands(["2C AC QC 10C KC", "QH KH AH 2H 3H"]), ["2C AC QC 10C KC"]
+ )
+
def test_both_hands_have_a_straight_flush_tie_goes_to_highest_ranked_card(self):
self.assertEqual(
best_hands(["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"]), ["5S 7S 8S 9S 6S"]
)
+ def test_even_though_an_ace_is_usually_high_a_5_high_straight_flush_is_the_lowest_scoring_straight_flush(
+ self,
+ ):
+ self.assertEqual(
+ best_hands(["2H 3H 4H 5H 6H", "4D AD 3D 2D 5D"]), ["2H 3H 4H 5H 6H"]
+ )
+
if __name__ == "__main__":
unittest.main()
From caa501c527798c6e4ee79ee6bc474ebfce0c9cab Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 2 Dec 2022 22:43:01 -0800
Subject: [PATCH 265/932] Updates to sgf-parsing.
---
.../practice/sgf-parsing/.meta/tests.toml | 54 +++++++++++++++++--
.../practice/sgf-parsing/sgf_parsing_test.py | 33 ++++++++++--
2 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/sgf-parsing/.meta/tests.toml b/exercises/practice/sgf-parsing/.meta/tests.toml
index c325ae966f9..71f5b83a64d 100644
--- a/exercises/practice/sgf-parsing/.meta/tests.toml
+++ b/exercises/practice/sgf-parsing/.meta/tests.toml
@@ -1,6 +1,13 @@
-# 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.
[2668d5dc-109f-4f71-b9d5-8d06b1d6f1cd]
description = "empty input"
@@ -38,5 +45,46 @@ description = "two child trees"
[724eeda6-00db-41b1-8aa9-4d5238ca0130]
description = "multiple property values"
+[28092c06-275f-4b9f-a6be-95663e69d4db]
+description = "within property values, whitespace characters such as tab are converted to spaces"
+include = false
+
+[deaecb9d-b6df-4658-aa92-dcd70f4d472a]
+description = "within property values, newli es remain as newlines"
+include = false
+
+[8e4c970e-42d7-440e-bfef-5d7a296868ef]
+description = "escaped closing bracket within property value becomes just a closing bracket"
+include = false
+
+[cf371fa8-ba4a-45ec-82fb-38668edcb15f]
+description = "escaped backslash in property value becomes just a backslash"
+include = false
+
+[dc13ca67-fac0-4b65-b3fe-c584d6a2c523]
+description = "opening bracket within property value doesn't need to be escaped"
+include = false
+
+[a780b97e-8dbb-474e-8f7e-4031902190e8]
+description = "semicolon in property value doesn't need to be escaped"
+
+[0b57a79e-8d89-49e5-82b6-2eaaa6b88ed7]
+description = "parentheses in property value don't need to be escaped"
+
+[c72a33af-9e04-4cc5-9890-1b92262813ac]
+description = "escaped tab in property value is converted to space"
+
+[3a1023d2-7484-4498-8d73-3666bb386e81]
+description = "escaped newline in property value is converted to nothing at all"
+
+[25abf1a4-5205-46f1-8c72-53273b94d009]
+description = "escaped t and n in property value are just letters, not whitespace"
+
+[08e4b8ba-bb07-4431-a3d9-b1f4cdea6dab]
+description = "mixing various kinds of whitespace and escaped characters in property value"
+reimplements = "11c36323-93fc-495d-bb23-c88ee5844b8c"
+include = false
+
[11c36323-93fc-495d-bb23-c88ee5844b8c]
description = "escaped property"
+include = false
diff --git a/exercises/practice/sgf-parsing/sgf_parsing_test.py b/exercises/practice/sgf-parsing/sgf_parsing_test.py
index ff4fac2f75f..9b01fb11d09 100644
--- a/exercises/practice/sgf-parsing/sgf_parsing_test.py
+++ b/exercises/practice/sgf-parsing/sgf_parsing_test.py
@@ -84,7 +84,34 @@ def test_multiple_property_values(self):
expected = SgfTree(properties={"A": ["b", "c", "d"]})
self.assertEqual(parse(input_string), expected)
- def test_escaped_property(self):
- input_string = "(;A[\\]b\nc\nd\t\te \n\\]])"
- expected = SgfTree(properties={"A": ["]b\nc\nd e \n]"]})
+ def test_semicolon_in_property_value_doesn_t_need_to_be_escaped(self):
+ input_string = "(;A[a;b][foo]B[bar];C[baz])"
+ expected = SgfTree(
+ properties={"A": ["a;b", "foo"], "B": ["bar"]},
+ children=[SgfTree({"C": ["baz"]})],
+ )
+ self.assertEqual(parse(input_string), expected)
+
+ def test_parentheses_in_property_value_don_t_need_to_be_escaped(self):
+ input_string = "(;A[x(y)z][foo]B[bar];C[baz])"
+ expected = SgfTree(
+ properties={"A": ["x(y)z", "foo"], "B": ["bar"]},
+ children=[SgfTree({"C": ["baz"]})],
+ )
+ self.assertEqual(parse(input_string), expected)
+
+ def test_escaped_tab_in_property_value_is_converted_to_space(self):
+ input_string = "(;A[hello\\ world])"
+ expected = SgfTree(properties={"A": ["hello world"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_escaped_newline_in_property_value_is_converted_to_nothing_at_all(self):
+ input_string = "(;A[hello\
+world])"
+ expected = SgfTree(properties={"A": ["helloworld"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_escaped_t_and_n_in_property_value_are_just_letters_not_whitespace(self):
+ input_string = "(;A[\t = t and \n = n])"
+ expected = SgfTree(properties={"A": ["t = t and n = n"]})
self.assertEqual(parse(input_string), expected)
From 8c23360c500b0fc491b453b6d5e217b8cea79d12 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 13:58:26 +0100
Subject: [PATCH 266/932] Fixed exemplar
---
exercises/practice/sgf-parsing/.meta/example.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/sgf-parsing/.meta/example.py b/exercises/practice/sgf-parsing/.meta/example.py
index cc3327c8ff0..f32b20b8531 100644
--- a/exercises/practice/sgf-parsing/.meta/example.py
+++ b/exercises/practice/sgf-parsing/.meta/example.py
@@ -47,7 +47,7 @@ def parse(input_string):
root = None
current = None
stack = list(input_string)
-
+
if input_string == '()':
raise ValueError('tree with no nodes')
@@ -64,6 +64,10 @@ def pop_until(delimiter):
try:
value = ''
while stack[0] != delimiter:
+ if stack[0] == "\n":
+ stack[0] = "n"
+ if stack[0] == "\t":
+ stack[0] = "t"
value += pop()
return value
except IndexError as error:
From ff2f4b3aabe3332c6cb9dc579e1ad66ea954734f Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 4 Dec 2022 23:26:45 -0800
Subject: [PATCH 267/932] Update example.py
Deleted unnecessary blank line 50.
---
exercises/practice/sgf-parsing/.meta/example.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/exercises/practice/sgf-parsing/.meta/example.py b/exercises/practice/sgf-parsing/.meta/example.py
index f32b20b8531..7711625518b 100644
--- a/exercises/practice/sgf-parsing/.meta/example.py
+++ b/exercises/practice/sgf-parsing/.meta/example.py
@@ -47,7 +47,6 @@ def parse(input_string):
root = None
current = None
stack = list(input_string)
-
if input_string == '()':
raise ValueError('tree with no nodes')
From 384e8e219089188c4329776448cc3ab153deb402 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:05:14 -0600
Subject: [PATCH 268/932] Create config.json
---
.../practice/pig-latin/.approaches/config.json | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/config.json
diff --git a/exercises/practice/pig-latin/.approaches/config.json b/exercises/practice/pig-latin/.approaches/config.json
new file mode 100644
index 00000000000..bd808f77fb8
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/config.json
@@ -0,0 +1,15 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "505e545c-d56c-45e3-8cc6-df3fdb54cc0c",
+ "slug": "sets-and-slices",
+ "title": "Sets and slices",
+ "blurb": "Use sets with slices for parsing.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 75a3a577e127b185afc995085d99f8bf0f512fe1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:35:54 -0600
Subject: [PATCH 269/932] Create introduction.md
---
.../pig-latin/.approaches/introduction.md | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/introduction.md
diff --git a/exercises/practice/pig-latin/.approaches/introduction.md b/exercises/practice/pig-latin/.approaches/introduction.md
new file mode 100644
index 00000000000..9965022d550
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/introduction.md
@@ -0,0 +1,42 @@
+# Introduction
+
+There are various ways to solve Pig Latin.
+One way is to use [regular expressions][regex] (also known as [regex][regex-ops]) for processing the input.
+Solutions using regex can be very succinct, but require familiarity with regex patterns, which are like another language.
+Another way is to use a series of conditional statements to test which of several rules the input matches.
+Another approach is to use [set][set]s for look-up and then [slice][slicing] the input to return the correct value.
+
+## General guidance
+
+At the time of writing only four rules need to be handled, but if they have similar output, they don't need to be handled completely separately.
+
+```python
+VOWELS = {"a", "e", "i", "o", "u"}
+VOWELS_Y = {"a", "e", "i", "o", "u", "y"}
+SPECIALS = {"xr", "yt"}
+
+
+def translate(text):
+ piggyfied = []
+
+ for word in text.split():
+ if word[0] in VOWELS or word[0:2] in SPECIALS:
+ piggyfied.append(word + "ay")
+ continue
+
+ for pos in range(1, len(word)):
+ if word[pos] in VOWELS_Y:
+ pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
+ piggyfied.append(word[pos:] + word[:pos] + "ay")
+ break
+ return " ".join(piggyfied)
+
+```
+
+For more information, check the [sets and slices approach][approach-sets-and-slices].
+
+[regex]: https://docs.python.org/3/howto/regex.html#regex-howto
+[regex-ops]: https://docs.python.org/3/library/re.html?regex
+[set]: https://docs.python.org/3/library/stdtypes.html?#set
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[approach-sets-and-slices]: https://exercism.org/tracks/python/exercises/pig-latin/approaches/sets-and-slices
From 75b17840c35aa26e80d3656927435372625b6e66 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:40:23 -0600
Subject: [PATCH 270/932] Update introduction.md
---
exercises/practice/pig-latin/.approaches/introduction.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/pig-latin/.approaches/introduction.md b/exercises/practice/pig-latin/.approaches/introduction.md
index 9965022d550..09cd0ab5d80 100644
--- a/exercises/practice/pig-latin/.approaches/introduction.md
+++ b/exercises/practice/pig-latin/.approaches/introduction.md
@@ -29,6 +29,7 @@ def translate(text):
pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
piggyfied.append(word[pos:] + word[:pos] + "ay")
break
+
return " ".join(piggyfied)
```
From d66103336cac5ea5e0525b769a15dabd6cde5664 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:40:46 -0600
Subject: [PATCH 271/932] Create snippet.txt
---
.../pig-latin/.approaches/sets-and-slices/snippet.txt | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt b/exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt
new file mode 100644
index 00000000000..e23cee9c2eb
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt
@@ -0,0 +1,7 @@
+for pos in range(1, len(word)):
+ if word[pos] in VOWELS_Y:
+ pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
+ piggyfied.append(word[pos:] + word[:pos] + "ay")
+ break
+
+return " ".join(piggyfied)
From c8525eef7b297d56882df3abeac5159c728e57b5 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:43:10 -0600
Subject: [PATCH 272/932] Update introduction.md
---
exercises/practice/pig-latin/.approaches/introduction.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/exercises/practice/pig-latin/.approaches/introduction.md b/exercises/practice/pig-latin/.approaches/introduction.md
index 09cd0ab5d80..0f8807acce6 100644
--- a/exercises/practice/pig-latin/.approaches/introduction.md
+++ b/exercises/practice/pig-latin/.approaches/introduction.md
@@ -10,6 +10,8 @@ Another approach is to use [set][set]s for look-up and then [slice][slicing] the
At the time of writing only four rules need to be handled, but if they have similar output, they don't need to be handled completely separately.
+## Approach: Sets and slices
+
```python
VOWELS = {"a", "e", "i", "o", "u"}
VOWELS_Y = {"a", "e", "i", "o", "u", "y"}
From 219078c9596e931584f5b4e8bae760efed93ec9b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 09:33:22 -0600
Subject: [PATCH 273/932] Create content.md
Saving work...
---
.../.approaches/sets-and-slices/content.md | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
new file mode 100644
index 00000000000..88454415cbc
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -0,0 +1,69 @@
+# Sets and slices
+
+```python
+VOWELS = {"a", "e", "i", "o", "u"}
+VOWELS_Y = {"a", "e", "i", "o", "u", "y"}
+SPECIALS = {"xr", "yt"}
+
+
+def translate(text):
+ piggyfied = []
+
+ for word in text.split():
+ if word[0] in VOWELS or word[0:2] in SPECIALS:
+ piggyfied.append(word + "ay")
+ continue
+
+ for pos in range(1, len(word)):
+ if word[pos] in VOWELS_Y:
+ pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
+ piggyfied.append(word[pos:] + word[:pos] + "ay")
+ break
+ return " ".join(piggyfied)
+
+```
+
+This approach begins by defining [sets][set] for looking up matching characters from the input.
+Python doesn't _enforce_ having real constant values,
+but the sets are defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+
+The `translate` function begins by defining the list which will hold the parsed value(s).
+
+The input is [`split()`][split] into a list of its words, which is then iterated.
+
+[String indexing][string-indexing] is used to check if the first letter of the word is in the set of vowels.
+If so, the logical [or][logical-or] operator "short-circuits" and the word plus "ay" is appended to the list.
+If the first letter is not a vowel, then a [slice][slicing] of the first two letters is used to check if they match any of the special beginning characters.
+If the letters match, the word plus "ay" will be appended to the list.
+
+If the beginning of the word has not yet matched a rule, then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
+
+```exercism/note
+When a [range](https://docs.python.org/3/library/stdtypes.html?#range) is provided two arguments,
+it generates values from the `start` argument up to but not including the `stop` argument.
+This can be referred to as start inclusive, stop exclusive.
+```
+ Each character is iterated until finding a vowel (at this point, the letter `y` is now considered a vowel.)
+If the vowel is `u`, the previous consonant is checked to be `q`.
+If so, the position is advanced to be one position after the found `u`.
+
+A slice is then taken from the position until the end of the word,
+plus a slice taken from the beginning of the word and stopped before the position, plus `ay`.
+That concatenated string is appended to the list.
+
+Once all of the words in the input have been iterated,
+the [join][join] method is called on a space character to connect all of the words in the list back into a single string.
+Since the space is only used as a separator _between_ elements of the list, if the list has only one element,
+the space will not be added to the beginning or end of the output string.
+
+[set]: https://docs.python.org/3/library/stdtypes.html?#set
+[const]: https://realpython.com/python-constants/
+[split]: https://docs.python.org/3/library/stdtypes.html?#str.split
+[string-indexing]: https://realpython.com/lessons/string-indexing/
+[logical-or]: https://realpython.com/python-or-operator/
+[ranging]: https://www.w3schools.com/python/gloss_python_for_range.asp
+[range]: https://docs.python.org/3/library/stdtypes.html?#range
+[len]: https://docs.python.org/3/library/functions.html?#len
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[join]: https://docs.python.org/3/library/stdtypes.html?#str.join
From ad2c51f433e15b235b7fc52c5f588d6af7993294 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 10:29:29 -0600
Subject: [PATCH 274/932] Update content.md
---
.../.approaches/sets-and-slices/content.md | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
index 88454415cbc..5ee7719700a 100644
--- a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -36,21 +36,26 @@ The input is [`split()`][split] into a list of its words, which is then iterated
If so, the logical [or][logical-or] operator "short-circuits" and the word plus "ay" is appended to the list.
If the first letter is not a vowel, then a [slice][slicing] of the first two letters is used to check if they match any of the special beginning characters.
If the letters match, the word plus "ay" will be appended to the list.
+If the beginning of the word matches either condition, the loop [continue][continue]s to the next word.
-If the beginning of the word has not yet matched a rule, then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
+If the beginning of the word did not match either condition,
+then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
```exercism/note
When a [range](https://docs.python.org/3/library/stdtypes.html?#range) is provided two arguments,
-it generates values from the `start` argument up to but not including the `stop` argument.
-This can be referred to as start inclusive, stop exclusive.
+it generates values from the `start` argument up to _but not including_ the `stop` argument.
+This behavior can be referred to as start inclusive, stop exclusive.
```
- Each character is iterated until finding a vowel (at this point, the letter `y` is now considered a vowel.)
+
+The inner loop iterating characters is nested within the outer loop that iterates the words.
+Each character is iterated until finding a vowel (at this point, the letter `y` is now considered a vowel.)
If the vowel is `u`, the previous consonant is checked to be `q`.
If so, the position is advanced to be one position after the found `u`.
A slice is then taken from the position until the end of the word,
plus a slice taken from the beginning of the word and stopped before the position, plus `ay`.
-That concatenated string is appended to the list.
+That concatenated string is appended to the list, [break][break] is called to exit the inner loop,
+and execution returns to the outer loop.
Once all of the words in the input have been iterated,
the [join][join] method is called on a space character to connect all of the words in the list back into a single string.
@@ -62,8 +67,10 @@ the space will not be added to the beginning or end of the output string.
[split]: https://docs.python.org/3/library/stdtypes.html?#str.split
[string-indexing]: https://realpython.com/lessons/string-indexing/
[logical-or]: https://realpython.com/python-or-operator/
+[continue]: https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement
[ranging]: https://www.w3schools.com/python/gloss_python_for_range.asp
[range]: https://docs.python.org/3/library/stdtypes.html?#range
[len]: https://docs.python.org/3/library/functions.html?#len
[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[break]: https://docs.python.org/3/reference/simple_stmts.html#the-break-statement
[join]: https://docs.python.org/3/library/stdtypes.html?#str.join
From 7a0e4346c7166f81bfd51422d09c8b39de06e6ce Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 10:33:07 -0600
Subject: [PATCH 275/932] Update content.md
---
.../practice/pig-latin/.approaches/sets-and-slices/content.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
index 5ee7719700a..0f7ab8c28f3 100644
--- a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -19,6 +19,7 @@ def translate(text):
pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
piggyfied.append(word[pos:] + word[:pos] + "ay")
break
+
return " ".join(piggyfied)
```
@@ -53,7 +54,7 @@ If the vowel is `u`, the previous consonant is checked to be `q`.
If so, the position is advanced to be one position after the found `u`.
A slice is then taken from the position until the end of the word,
-plus a slice taken from the beginning of the word and stopped before the position, plus `ay`.
+plus a slice taken from the beginning of the word and stopped just before the position, plus `ay`.
That concatenated string is appended to the list, [break][break] is called to exit the inner loop,
and execution returns to the outer loop.
From 38b9e9ec679450f2b68fc9ea71a4c4d9cdff7c0e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 3 Dec 2022 08:01:04 -0600
Subject: [PATCH 276/932] Update content.md
---
.../practice/pig-latin/.approaches/sets-and-slices/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
index 0f7ab8c28f3..54d5a6e809c 100644
--- a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -29,7 +29,7 @@ Python doesn't _enforce_ having real constant values,
but the sets are defined with all uppercase letters, which is the naming convention for a Python [constant][const].
It indicates that the value is not intended to be changed.
-The `translate` function begins by defining the list which will hold the parsed value(s).
+The `translate()` function begins by defining the list which will hold the parsed value(s).
The input is [`split()`][split] into a list of its words, which is then iterated.
@@ -40,7 +40,7 @@ If the letters match, the word plus "ay" will be appended to the list.
If the beginning of the word matches either condition, the loop [continue][continue]s to the next word.
If the beginning of the word did not match either condition,
-then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
+that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
```exercism/note
When a [range](https://docs.python.org/3/library/stdtypes.html?#range) is provided two arguments,
From bc9667613470ed9dee41123e178745234e215451 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 18:38:25 +0100
Subject: [PATCH 277/932] Update track_exercises_overview.md
---
reference/track_exercises_overview.md | 437 +++++++++++++-------------
1 file changed, 224 insertions(+), 213 deletions(-)
diff --git a/reference/track_exercises_overview.md b/reference/track_exercises_overview.md
index 383e1e1ebb9..8b8fe4f62e8 100644
--- a/reference/track_exercises_overview.md
+++ b/reference/track_exercises_overview.md
@@ -5,180 +5,192 @@
-
## Implemented Practice Exercises
Practice Exercises with Difficulty, Solutions, and Mentor Notes
+| Exercise | Difficulty | Solutions | prereqs | Practices | Mentor Notes | Jinja? |
+| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------ |
+| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | | ✅ |
+| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) | ✅ |
+| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | | ✅ |
+| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | | ✅ |
+| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) | ✅ |
+| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | | ✅ |
+| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | | ✅ |
+| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | | ✅ |
+| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | | ✅ |
+| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | | ❌ |
+| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | | ✅ |
+| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) | ✅ |
+| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) | ✅ |
+| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | | ✅ |
+| [Bottle Song](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.meta/example.py)┋[most⭐](https://exercism.org/tracks/python/exercises/bottle-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json/#LC1012) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC1012) | | ✅ |
+| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | | ✅ |
+| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | | ✅ |
+| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | | ✅ |
+| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) | ✅ |
+| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | | ✅ |
+| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | | ✅ |
+| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | | ✅ |
+| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | | ✅ |
+| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | | ✅ |
+| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | | ✅ |
+| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | | ✅ |
+| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | | ✅ |
+| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | | ✅ |
+| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | | ✅ |
+| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | | ❌ |
+| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | | ✅ |
+| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | | ✅ |
+| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | | ✅ |
+| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | | ✅ |
+| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | | ✅ |
+| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | | ✅ |
+| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) | ✅ |
+| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | | ✅ |
+| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | | ✅ |
+| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) | ✅ |
+| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | | ❌ |
+| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) | ✅ |
+| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | | ✅ |
+| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | | ✅ |
+| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) | ✅ |
+| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) | ✅ |
+| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | | ✅ |
+| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | | ✅ |
+| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) | ✅ |
+| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | | ❌ |
+| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | | ❌ |
+| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | | ✅ |
+| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) | ✅ |
+| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) | ✅ |
+| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) | ✅ |
+| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) | ✅ |
+| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | | ✅ |
+| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | | ✅ |
+| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | | ✅ |
+| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | | ✅ |
+| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | | ❌ |
+| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | | ✅ |
+| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | | ✅ |
+| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | | ✅ |
+| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | | ✅ |
+| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | | ✅ |
+| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | | ✅ |
+| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | | ✅ |
+| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | | ✅ |
+| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | | ✅ |
+| [Proverb](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/proverb/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC661) | [config.json](https://github.com/exercism/python/blob/main/config.json#L661) | | ✅ |
+| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | | ✅ |
+| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | | ✅ |
+| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | | ✅ |
+| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) | ✅ |
+| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | | ✅ |
+| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | | ❌ |
+| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | | ✅ |
+| [Resistor Color Trio](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-trio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC631) | [config.json](https://github.com/exercism/python/blob/main/config.json#L631) | | ✅ |
+| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | | ✅ |
+| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | | ✅ |
+| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | | ✅ |
+| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) | ✅ |
+| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | | ✅ |
+| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | | ❌ |
+| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | | ✅ |
+| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | | ✅ |
+| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | | ✅ |
+| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | | ✅ |
+| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | | ✅ |
+| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | | ✅ |
+| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | | ✅ |
+| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | | ✅ |
+| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) | ✅ |
+| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | | ✅ |
+| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | | ✅ |
+| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | | ✅ |
+| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | | ✅ |
+| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | | ✅ |
+| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | | ❌ |
+| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | | ✅ |
+| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | | ✅ |
+| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | | ✅ |
+| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) | ✅ |
+| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | | ✅ |
+| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | | ✅ |
+| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | | ❌ |
+| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | | ✅ |
+| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) | ✅ |
+| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | | ✅ |
+| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) | ✅ |
+| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | | ✅ |
+| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) | ✅ |
+| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | | ✅ |
+| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | | ✅ |
+| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | | ✅ |
+| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | | ✅ |
+| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | | ✅ |
+
+
-| Exercise | Difficulty | Solutions | Prereqs | Practices | Mentor
Notes |
-|--------------------------------------------------------------------------------------------------------------------------------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
-| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | |
-| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) |
-| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | |
-| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | |
-| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) |
-| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | |
-| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | |
-| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | |
-| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | |
-| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | |
-| [Beer Song](https://github.com/exercism/python/blob/main/exercises/practice/beer-song/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/beer-song/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/beer-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L905) | NONE | |
-| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | |
-| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) |
-| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) |
-| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | |
-| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | |
-| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | |
-| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | |
-| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) |
-| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | |
-| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | |
-| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | |
-| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | |
-| [Custom Set](https://github.com/exercism/python/blob/main/exercises/practice/custom-set/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/custom-set/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/custom-set/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1888) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1883) | |
-| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | |
-| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | |
-| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | |
-| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | |
-| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | |
-| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | |
-| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | |
-| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | |
-| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | |
-| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | |
-| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | |
-| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | |
-| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | |
-| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) |
-| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | |
-| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | |
-| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) |
-| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | |
-| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) |
-| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | |
-| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | |
-| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) |
-| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) |
-| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | |
-| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | |
-| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) |
-| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | |
-| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | |
-| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | |
-| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) |
-| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) |
-| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) |
-| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) |
-| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | |
-| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | |
-| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | |
-| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | |
-| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | |
-| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | |
-| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | |
-| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | |
-| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | |
-| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | |
-| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | |
-| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | |
-| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | |
-| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | |
-| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | |
-| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | |
-| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | |
-| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) |
-| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | |
-| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | |
-| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | |
-| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | |
-| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | |
-| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | |
-| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) |
-| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | |
-| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | |
-| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | |
-| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | |
-| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | |
-| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | |
-| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | |
-| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | |
-| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | |
-| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | |
-| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) |
-| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | |
-| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | |
-| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | |
-| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | |
-| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | |
-| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | |
-| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | |
-| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | |
-| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | |
-| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) |
-| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | |
-| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | |
-| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | |
-| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | |
-| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) |
-| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | |
-| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) |
-| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true)| [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | |
-| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) |
-| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | |
-| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | |
-| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | |
-| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | |
-| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | |
+### Deprecated
+
+| Exercise | Difficulty |
+| -------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
+| [Accumulate](https://github.com/exercism/python/blob/main/exercises/practice/accumulate/.docs/instructions.md) | 🔹🔹 |
+| [Beer Song](https://github.com/exercism/python/blob/main/exercises/practice/beer-song/.docs/instructions.md) | 🔹🔹🔹 |
+| [Binary](https://github.com/exercism/python/blob/main/exercises/practice/binary/.docs/instructions.md) | 🔹🔹🔹 |
+| [Error Handling](https://github.com/exercism/python/blob/main/exercises/practice/error-handling/.docs/instructions.md) | 🔹🔹🔹 |
+| [Hexadecimal](https://github.com/exercism/python/blob/main/exercises/practice/hexadecimal/.docs/instructions.md) | 🔹🔹🔹 |
+| [Nucleotide Count](https://github.com/exercism/python/blob/main/exercises/practice/nucleotide-count/.docs/instructions.md) | 🔹🔹 |
+| [Parallel Letter Frequency](https://github.com/exercism/python/blob/main/exercises/practice/parallel-letter-frequency/.docs/instructions.md) | 🔹🔹🔹 |
+| [Pascal's Triangle](https://github.com/exercism/python/blob/main/exercises/practice/pascals-triangle/.docs/instructions.md) | 🔹🔹🔹 |
+| [Point Mutations](https://github.com/exercism/python/blob/main/exercises/practice/point-mutations/.docs/instructions.md) | 🔹🔹🔹 |
+| [Trinary](https://github.com/exercism/python/blob/main/exercises/practice/trinary/.docs/instructions.md) | 🔹🔹🔹🔹 |
+| [Custom Set](https://github.com/exercism/python/blob/main/exercises/practice/custom-set/.docs/instructions.md) | 🔹🔹🔹🔹🔹 |
-
-
## Concepts Without Planned Exercises
No Exercises Planned
-
-| Status | Concept | About&Intro | Exercise | Design Doc or Issue |
-|:---------------------------------------------------------------------------------------------------: |------------------------------------------------------------------------------------------------------------------------------ |:---------------------------------------------------------------------------------------------------: |------------------------------------------------------------------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------ |
-| ~~ | \*general--Composition | ~~ | NA | NA |
-| ~~ | \*general--Data Structures] | ~~ | NA | NA |
-| ~~ | \*general--Encapsulation | ~~ | NA | NA |
-| ~~ | \*general--Interfaces] | ~~ | NA | NA |
-| ~~ | \*general--Lookup efficiency] | ~~ | NA | NA |
-| ~~ | \*general--Mutability in Python] | ~~ | NA | NA |
-| ~~ | \*general--Mutation | ~~ | NA | NA |
-| ~~ | \*general--Polymorphism | ~~ | NA | NA |
-| ~~ | \*general--Recursive data structures | ~~ | NA | NA |
-| ~~ | \*general--Scope | ~~ | NA | NA |
-| ~~ | \*general--Standard Library | ~~ | NA | NA |
-| ~~ | \*general--State | ~~ | NA | NA |
-| ~~ | \*no stand-alone--del | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Duck Typing | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Dynamic Typing | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Expressions | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Immutability in Python | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Operator precedence | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Operators] | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Order of Evaluation | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--type | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--type conversion | ~~ | Multiple | Multiple |
+| Status | Concept | About&Intro | Exercise | Design Doc or Issue |
+| :----: | ---------------------------------------- | :---------: | -------- | ------------------- |
+| ~~ | \*general--Composition | ~~ | NA | NA |
+| ~~ | \*general--Data Structures] | ~~ | NA | NA |
+| ~~ | \*general--Encapsulation | ~~ | NA | NA |
+| ~~ | \*general--Interfaces] | ~~ | NA | NA |
+| ~~ | \*general--Lookup efficiency] | ~~ | NA | NA |
+| ~~ | \*general--Mutability in Python] | ~~ | NA | NA |
+| ~~ | \*general--Mutation | ~~ | NA | NA |
+| ~~ | \*general--Polymorphism | ~~ | NA | NA |
+| ~~ | \*general--Recursive data structures | ~~ | NA | NA |
+| ~~ | \*general--Scope | ~~ | NA | NA |
+| ~~ | \*general--Standard Library | ~~ | NA | NA |
+| ~~ | \*general--State | ~~ | NA | NA |
+| ~~ | \*no stand-alone--del | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Duck Typing | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Dynamic Typing | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Expressions | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Immutability in Python | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Operator precedence | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Operators] | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Order of Evaluation | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--type | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--type conversion | ~~ | Multiple | Multiple |
-
## Implemented & Planned Concept Exercises
-
= live on exercism.org
= drafted but not live
= planned or in progress
@@ -186,73 +198,72 @@
-| Status | Concept | About&Intro | Exercise | Design Doc or Issue |Stub
Docstring Level|
-|:----------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|:---|
-|
| [basics](https://github.com/exercism/python/blob/main/concepts/basics) |
| [Guidos Gorgeous Lasagna](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/guidos-gorgeous-lasagna) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/guidos-gorgeous-lasagna/.meta) | Full |
-|
| [bools](https://github.com/exercism/python/blob/main/concepts/bools) |
| [Ghost Gobble Arcade Game](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/ghost-gobble-arcade-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ghost-gobble-arcade-game/.meta) | Full |
-|
| [numbers](https://github.com/exercism/python/blob/main/concepts/numbers) |
| [Currency Exchange](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange/.meta) | Full |
-|
| [complex-numbers](https://github.com/exercism/python/blob/main/concepts/complex-numbers) |
| ~ | [#2208](https://github.com/exercism/v3/issues/2208) | TBD |
-|
| [conditionals](https://github.com/exercism/python/blob/main/concepts/conditionals) |
| [Meltdown Mitigation ](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation/.meta) | Full |
-|
| [comparisons](https://github.com/exercism/python/blob/main/concepts/comparisons) |
| [Black Jack](https://github.com/exercism/python/tree/main/exercises/concept/black-jack) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/black-jack/.meta) | Full |
-|
| [strings](https://github.com/exercism/python/blob/main/concepts/strings) |
| [Litte Sister's Vocab](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab/.meta) | Full |
-|
| [string-methods](https://github.com/exercism/python/blob/main/concepts/string-methods) |
| [Litte Sister's Essay](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay/.meta) | Full |
-|
| [string-formatting](https://github.com/exercism/python/blob/main/concepts/string-formatting) |
| [Pretty Leaflet ](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet/.meta) | Full |
-|
| [lists](https://github.com/exercism/python/blob/main/concepts/lists) |
| [Card Games](https://github.com/exercism/python/tree/main/exercises/concept/card-games) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/card-games/.meta) | Full |
-|
| [list-methods](https://github.com/exercism/python/blob/main/concepts/list-methods) |
| [Chaitanas Colossal Coaster](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster/.meta) | Full |
-|
| [loops](https://github.com/exercism/python/blob/main/concepts/loops) |
| [Making the Grade](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade/.meta) | Full |
-|
| [tuples](https://github.com/exercism/python/blob/main/concepts/tuples) |
| [Tisbury Treasure Hunt](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt/.meta) | Full |
-|
| [sequences](https://github.com/exercism/python/blob/main/concepts/sequences) |
| ~ | [#2290](https://github.com/exercism/python/issues/2290) | TBD |
-|
| [dicts](https://github.com/exercism/python/blob/main/concepts/dicts) |
| [Inventory Management](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | Full |
-|
| [dict-methods](https://github.com/exercism/python/blob/main/concepts/dict-methods) |
| ~ | [#2348](https://github.com/exercism/python/issues/2348) | |
-|
| [sets](https://github.com/exercism/python/blob/main/concepts/sets) |
| [Cater Waiter ](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter/.meta) | Full |
-|
| [list-comprehensions](https://github.com/exercism/python/blob/main/concepts/list-comprehensions) |
| ~ | [#2295](https://github.com/exercism/python/issues/2295) | |
-|
| [other-comprehensions](https://github.com/exercism/python/blob/main/concepts/other-comprehensions) |
| ~ | [#2294](https://github.com/exercism/python/issues/2294) | |
-|
| [classes](https://github.com/exercism/python/blob/main/concepts/classes) |
| [Ellen's Alien Game](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game/.meta) | Minimal |
-|
| [generators](https://github.com/exercism/python/blob/main/concepts/generators) |
| Plane Tickets | [PR#2729](https://github.com/exercism/python/pull/2729)/[#2293](https://github.com/exercism/python/issues/2293) | Minimal |
-|
| [generator-expressions](https://github.com/exercism/python/blob/main/concepts/generator-expressions) |
| ~ | [#2292](https://github.com/exercism/python/issues/2292) | |
-|
| [iterators](https://github.com/exercism/python/blob/main/concepts/iterators) |
| ~ | [#2367](https://github.com/exercism/python/issues/2367) | TBD |
-|
| [functions](https://github.com/exercism/python/blob/main/concepts/functions) |
| ~ | [#2353](https://github.com/exercism/python/issues/2353) | |
-|
| [unpacking-and-multiple-assignment](https://github.com/exercism/python/blob/main/concepts/unpacking-and-multiple-assignment) |
| ~ | [#2360](https://github.com/exercism/python/issues/2360) | |
-|
| [raising-and-handling-errors](https://github.com/exercism/python/blob/main/concepts/raising-and-handling-errors) |
| ~ | TBD | |
-|
| [itertools](https://github.com/exercism/python/blob/main/concepts/itertools) |
| ~ | [#2368](https://github.com/exercism/python/issues/2368) | |
-|
| [with-statement](https://github.com/exercism/python/blob/main/concepts/with-statement) |
| ~ | [#2369](https://github.com/exercism/python/issues/2369) | |
-|
| [enums](https://github.com/exercism/python/blob/main/concepts/enums) |
| [Log Levels](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | Minimal |
-|
| [none](https://github.com/exercism/python/blob/main/concepts/none) |
| [Restaurant Rozalynn](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn/.meta) | Minimal |
-|
| [decorators](https://github.com/exercism/python/blob/main/concepts/decorators) |
| ~ | [#2356](https://github.com/exercism/python/issues/2356) | |
-|
| [rich-comparisons](https://github.com/exercism/python/blob/main/concepts/rich-comparisons) |
| ~ | [#2287](https://github.com/exercism/python/issues/2287) | |
-|
| [function-arguments](https://github.com/exercism/python/blob/main/concepts/function-arguments) |
| ~ | [#2354](https://github.com/exercism/python/issues/2354) | |
-|
| [class-customization](https://github.com/exercism/python/blob/main/concepts/class-customization) |
| ~ | [#2350](https://github.com/exercism/python/issues/2350) | |
-|
| [class-inheritance](https://github.com/exercism/python/blob/main/concepts/class-inheritance) |
| ~ | [#2351](https://github.com/exercism/python/issues/2351) | |
-|
| [user-defined-errors](https://github.com/exercism/python/blob/main/concepts/user-defined-errors) |
| ~ | TBD | |
-|
| [context-manager-customization](https://github.com/exercism/python/blob/main/concepts/context-manager-customization) |
| ~ | [#2370](https://github.com/exercism/python/issues/2370) | |
-|
| [higher-order-functions](https://github.com/exercism/python/blob/main/concepts/higher-order-functions) |
| ~ | [#2355](https://github.com/exercism/python/issues/2355) | |
-|
| [functional-tools](https://github.com/exercism/python/blob/main/concepts/functional-tools) |
| ~ | [#2359](https://github.com/exercism/python/issues/2359) | |
-|
| [functools](https://github.com/exercism/python/blob/main/concepts/functools) |
| ~ | [#2366](https://github.com/exercism/python/issues/2366) | |
-|
| [anonymous-functions](https://github.com/exercism/python/blob/main/concepts) |
| ~ | [#2357](https://github.com/exercism/python/issues/2357) | |
-|
| [descriptors](https://github.com/exercism/python/blob/main/concepts/descriptors) |
| ~ | [#2365](https://github.com/exercism/python/issues/2365) | |
-|
| [aliasing](https://github.com/exercism/python/blob/main/concepts) |
| ~ | TBD | |
-|
| [binary data](https://github.com/exercism/python/blob/main/concepts/binary-data) |
| ~ | TBD | |
-|
| [bitflags](https://github.com/exercism/python/blob/main/concepts/bitflags) |
| ~ | TBD | |
-|
| [bitwise-operators](https://github.com/exercism/python/blob/main/concepts/bitwise-operators) |
| ~ | TBD | |
-|
| [bytes](https://github.com/exercism/python/blob/main/concepts/bytes) |
| ~ | TBD | |
-|
| [class-composition](https://github.com/exercism/python/blob/main/concepts/class-composition) |
| ~ | [#2352](https://github.com/exercism/python/issues/2352) | |
-|
| [class-interfaces](https://github.com/exercism/python/blob/main/concepts/class-interfaces) |
| ~ | TBD | |
-|
| [collections](https://github.com/exercism/python/blob/main/concepts/collections) |
| ~ | TBD | |
-|
| [dataclasses-and-namedtuples](https://github.com/exercism/python/blob/main/concepts/dataclasses-and-namedtuples) |
| ~ | [#2361](https://github.com/exercism/python/issues/2361) | |
-|
| [import](https://github.com/exercism/python/blob/main/concepts/import) |
| ~ | ON HOLD | |
-|
| [memoryview](https://github.com/exercism/python/blob/main/concepts/memoryview) |
| ~ | TBD | |
-|
| [operator-overloading](https://github.com/exercism/python/blob/main/concepts/operator-overloading) |
| ~ | TBD | |
-|
| [regular-expressions](https://github.com/exercism/python/blob/main/concepts/regular-expressions) |
| ~ | TBD | |
-|
| [string-methods-splitting](https://github.com/exercism/python/blob/main/concepts/string-methods-splitting) |
| ~ | TBD | |
-|
| [testing](https://github.com/exercism/python/blob/main/concepts/testing) |
| ~ | TBD | |
-|
| [text-processing](https://github.com/exercism/python/blob/main/concepts/text-processing) |
| ~ | TBD | |
-|
| [type-hinting](https://github.com/exercism/python/blob/main/concepts/type-hinting) |
| ~ | TBD | |
-|
| [unicode-regular-expressions](https://github.com/exercism/python/blob/main/concepts/unicode-regular-expressions) |
| ~ | TBD | |
-|
| [walrus-operator](https://github.com/exercism/python/blob/main/concepts/walrus-operator) |
| ~ | TBD | |
+| Status | Concept | About&Intro | Exercise | Design Doc or Issue | Stub
Docstring Level |
+| :--------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- | :---------------------- |
+|
| [basics](https://github.com/exercism/python/blob/main/concepts/basics) |
| [Guidos Gorgeous Lasagna](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/guidos-gorgeous-lasagna) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/guidos-gorgeous-lasagna/.meta) | Full |
+|
| [bools](https://github.com/exercism/python/blob/main/concepts/bools) |
| [Ghost Gobble Arcade Game](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/ghost-gobble-arcade-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ghost-gobble-arcade-game/.meta) | Full |
+|
| [numbers](https://github.com/exercism/python/blob/main/concepts/numbers) |
| [Currency Exchange](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange/.meta) | Full |
+|
| [complex-numbers](https://github.com/exercism/python/blob/main/concepts/complex-numbers) |
| ~ | [#2208](https://github.com/exercism/v3/issues/2208) | TBD |
+|
| [conditionals](https://github.com/exercism/python/blob/main/concepts/conditionals) |
| [Meltdown Mitigation ](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation/.meta) | Full |
+|
| [comparisons](https://github.com/exercism/python/blob/main/concepts/comparisons) |
| [Black Jack](https://github.com/exercism/python/tree/main/exercises/concept/black-jack) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/black-jack/.meta) | Full |
+|
| [strings](https://github.com/exercism/python/blob/main/concepts/strings) |
| [Litte Sister's Vocab](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab/.meta) | Full |
+|
| [string-methods](https://github.com/exercism/python/blob/main/concepts/string-methods) |
| [Litte Sister's Essay](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay/.meta) | Full |
+|
| [string-formatting](https://github.com/exercism/python/blob/main/concepts/string-formatting) |
| [Pretty Leaflet ](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet/.meta) | Full |
+|
| [lists](https://github.com/exercism/python/blob/main/concepts/lists) |
| [Card Games](https://github.com/exercism/python/tree/main/exercises/concept/card-games) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/card-games/.meta) | Full |
+|
| [list-methods](https://github.com/exercism/python/blob/main/concepts/list-methods) |
| [Chaitanas Colossal Coaster](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster/.meta) | Full |
+|
| [loops](https://github.com/exercism/python/blob/main/concepts/loops) |
| [Making the Grade](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade/.meta) | Full |
+|
| [tuples](https://github.com/exercism/python/blob/main/concepts/tuples) |
| [Tisbury Treasure Hunt](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt/.meta) | Full |
+|
| [sequences](https://github.com/exercism/python/blob/main/concepts/sequences) |
| ~ | [#2290](https://github.com/exercism/python/issues/2290) | TBD |
+|
| [dicts](https://github.com/exercism/python/blob/main/concepts/dicts) |
| [Inventory Management](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | Full |
+|
| [dict-methods](https://github.com/exercism/python/blob/main/concepts/dict-methods) |
| ~ | [#2348](https://github.com/exercism/python/issues/2348) | |
+|
| [sets](https://github.com/exercism/python/blob/main/concepts/sets) |
| [Cater Waiter ](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter/.meta) | Full |
+|
| [list-comprehensions](https://github.com/exercism/python/blob/main/concepts/list-comprehensions) |
| ~ | [#2295](https://github.com/exercism/python/issues/2295) | |
+|
| [other-comprehensions](https://github.com/exercism/python/blob/main/concepts/other-comprehensions) |
| ~ | [#2294](https://github.com/exercism/python/issues/2294) | |
+|
| [classes](https://github.com/exercism/python/blob/main/concepts/classes) |
| [Ellen's Alien Game](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game/.meta) | Minimal |
+|
| [generators](https://github.com/exercism/python/blob/main/concepts/generators) |
| Plane Tickets | [PR#2729](https://github.com/exercism/python/pull/2729)/[#2293](https://github.com/exercism/python/issues/2293) | Minimal |
+|
| [generator-expressions](https://github.com/exercism/python/blob/main/concepts/generator-expressions) |
| ~ | [#2292](https://github.com/exercism/python/issues/2292) | |
+|
| [iterators](https://github.com/exercism/python/blob/main/concepts/iterators) |
| ~ | [#2367](https://github.com/exercism/python/issues/2367) | TBD |
+|
| [functions](https://github.com/exercism/python/blob/main/concepts/functions) |
| ~ | [#2353](https://github.com/exercism/python/issues/2353) | |
+|
| [unpacking-and-multiple-assignment](https://github.com/exercism/python/blob/main/concepts/unpacking-and-multiple-assignment) |
| ~ | [#2360](https://github.com/exercism/python/issues/2360) | |
+|
| [raising-and-handling-errors](https://github.com/exercism/python/blob/main/concepts/raising-and-handling-errors) |
| ~ | TBD | |
+|
| [itertools](https://github.com/exercism/python/blob/main/concepts/itertools) |
| ~ | [#2368](https://github.com/exercism/python/issues/2368) | |
+|
| [with-statement](https://github.com/exercism/python/blob/main/concepts/with-statement) |
| ~ | [#2369](https://github.com/exercism/python/issues/2369) | |
+|
| [enums](https://github.com/exercism/python/blob/main/concepts/enums) |
| [Log Levels](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | Minimal |
+|
| [none](https://github.com/exercism/python/blob/main/concepts/none) |
| [Restaurant Rozalynn](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn/.meta) | Minimal |
+|
| [decorators](https://github.com/exercism/python/blob/main/concepts/decorators) |
| ~ | [#2356](https://github.com/exercism/python/issues/2356) | |
+|
| [rich-comparisons](https://github.com/exercism/python/blob/main/concepts/rich-comparisons) |
| ~ | [#2287](https://github.com/exercism/python/issues/2287) | |
+|
| [function-arguments](https://github.com/exercism/python/blob/main/concepts/function-arguments) |
| ~ | [#2354](https://github.com/exercism/python/issues/2354) | |
+|
| [class-customization](https://github.com/exercism/python/blob/main/concepts/class-customization) |
| ~ | [#2350](https://github.com/exercism/python/issues/2350) | |
+|
| [class-inheritance](https://github.com/exercism/python/blob/main/concepts/class-inheritance) |
| ~ | [#2351](https://github.com/exercism/python/issues/2351) | |
+|
| [user-defined-errors](https://github.com/exercism/python/blob/main/concepts/user-defined-errors) |
| ~ | TBD | |
+|
| [context-manager-customization](https://github.com/exercism/python/blob/main/concepts/context-manager-customization) |
| ~ | [#2370](https://github.com/exercism/python/issues/2370) | |
+|
| [higher-order-functions](https://github.com/exercism/python/blob/main/concepts/higher-order-functions) |
| ~ | [#2355](https://github.com/exercism/python/issues/2355) | |
+|
| [functional-tools](https://github.com/exercism/python/blob/main/concepts/functional-tools) |
| ~ | [#2359](https://github.com/exercism/python/issues/2359) | |
+|
| [functools](https://github.com/exercism/python/blob/main/concepts/functools) |
| ~ | [#2366](https://github.com/exercism/python/issues/2366) | |
+|
| [anonymous-functions](https://github.com/exercism/python/blob/main/concepts) |
| ~ | [#2357](https://github.com/exercism/python/issues/2357) | |
+|
| [descriptors](https://github.com/exercism/python/blob/main/concepts/descriptors) |
| ~ | [#2365](https://github.com/exercism/python/issues/2365) | |
+|
| [aliasing](https://github.com/exercism/python/blob/main/concepts) |
| ~ | TBD | |
+|
| [binary data](https://github.com/exercism/python/blob/main/concepts/binary-data) |
| ~ | TBD | |
+|
| [bitflags](https://github.com/exercism/python/blob/main/concepts/bitflags) |
| ~ | TBD | |
+|
| [bitwise-operators](https://github.com/exercism/python/blob/main/concepts/bitwise-operators) |
| ~ | TBD | |
+|
| [bytes](https://github.com/exercism/python/blob/main/concepts/bytes) |
| ~ | TBD | |
+|
| [class-composition](https://github.com/exercism/python/blob/main/concepts/class-composition) |
| ~ | [#2352](https://github.com/exercism/python/issues/2352) | |
+|
| [class-interfaces](https://github.com/exercism/python/blob/main/concepts/class-interfaces) |
| ~ | TBD | |
+|
| [collections](https://github.com/exercism/python/blob/main/concepts/collections) |
| ~ | TBD | |
+|
| [dataclasses-and-namedtuples](https://github.com/exercism/python/blob/main/concepts/dataclasses-and-namedtuples) |
| ~ | [#2361](https://github.com/exercism/python/issues/2361) | |
+|
| [import](https://github.com/exercism/python/blob/main/concepts/import) |
| ~ | ON HOLD | |
+|
| [memoryview](https://github.com/exercism/python/blob/main/concepts/memoryview) |
| ~ | TBD | |
+|
| [operator-overloading](https://github.com/exercism/python/blob/main/concepts/operator-overloading) |
| ~ | TBD | |
+|
| [regular-expressions](https://github.com/exercism/python/blob/main/concepts/regular-expressions) |
| ~ | TBD | |
+|
| [string-methods-splitting](https://github.com/exercism/python/blob/main/concepts/string-methods-splitting) |
| ~ | TBD | |
+|
| [testing](https://github.com/exercism/python/blob/main/concepts/testing) |
| ~ | TBD | |
+|
| [text-processing](https://github.com/exercism/python/blob/main/concepts/text-processing) |
| ~ | TBD | |
+|
| [type-hinting](https://github.com/exercism/python/blob/main/concepts/type-hinting) |
| ~ | TBD | |
+|
| [unicode-regular-expressions](https://github.com/exercism/python/blob/main/concepts/unicode-regular-expressions) |
| ~ | TBD | |
+|
| [walrus-operator](https://github.com/exercism/python/blob/main/concepts/walrus-operator) |
| ~ | TBD | |
-
## Concept Exercise Tree
```mermaid
From 1168afc639646525e3214fdf92233320250b9221 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 19:37:16 +0100
Subject: [PATCH 278/932] Update track_exercises_overview.md
---
reference/track_exercises_overview.md | 240 +++++++++++++-------------
1 file changed, 120 insertions(+), 120 deletions(-)
diff --git a/reference/track_exercises_overview.md b/reference/track_exercises_overview.md
index 8b8fe4f62e8..f7a04168033 100644
--- a/reference/track_exercises_overview.md
+++ b/reference/track_exercises_overview.md
@@ -11,126 +11,126 @@
Practice Exercises with Difficulty, Solutions, and Mentor Notes
-| Exercise | Difficulty | Solutions | prereqs | Practices | Mentor Notes | Jinja? |
-| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------ |
-| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | | ✅ |
-| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) | ✅ |
-| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | | ✅ |
-| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | | ✅ |
-| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) | ✅ |
-| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | | ✅ |
-| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | | ✅ |
-| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | | ✅ |
-| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | | ✅ |
-| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | | ❌ |
-| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | | ✅ |
-| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) | ✅ |
-| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) | ✅ |
-| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | | ✅ |
-| [Bottle Song](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.meta/example.py)┋[most⭐](https://exercism.org/tracks/python/exercises/bottle-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json/#LC1012) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC1012) | | ✅ |
-| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | | ✅ |
-| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | | ✅ |
-| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | | ✅ |
-| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) | ✅ |
-| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | | ✅ |
-| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | | ✅ |
-| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | | ✅ |
-| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | | ✅ |
-| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | | ✅ |
-| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | | ✅ |
-| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | | ✅ |
-| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | | ✅ |
-| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | | ✅ |
-| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | | ✅ |
-| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | | ❌ |
-| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | | ✅ |
-| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | | ✅ |
-| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | | ✅ |
-| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | | ✅ |
-| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | | ✅ |
-| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | | ✅ |
-| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) | ✅ |
-| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | | ✅ |
-| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | | ✅ |
-| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) | ✅ |
-| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | | ❌ |
-| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) | ✅ |
-| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | | ✅ |
-| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | | ✅ |
-| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) | ✅ |
-| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) | ✅ |
-| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | | ✅ |
-| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | | ✅ |
-| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) | ✅ |
-| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | | ❌ |
-| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | | ❌ |
-| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | | ✅ |
-| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) | ✅ |
-| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) | ✅ |
-| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) | ✅ |
-| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) | ✅ |
-| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | | ✅ |
-| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | | ✅ |
-| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | | ✅ |
-| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | | ✅ |
-| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | | ❌ |
-| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | | ✅ |
-| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | | ✅ |
-| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | | ✅ |
-| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | | ✅ |
-| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | | ✅ |
-| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | | ✅ |
-| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | | ✅ |
-| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | | ✅ |
-| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | | ✅ |
-| [Proverb](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/proverb/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC661) | [config.json](https://github.com/exercism/python/blob/main/config.json#L661) | | ✅ |
-| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | | ✅ |
-| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | | ✅ |
-| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | | ✅ |
-| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) | ✅ |
-| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | | ✅ |
-| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | | ❌ |
-| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | | ✅ |
-| [Resistor Color Trio](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-trio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC631) | [config.json](https://github.com/exercism/python/blob/main/config.json#L631) | | ✅ |
-| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | | ✅ |
-| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | | ✅ |
-| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | | ✅ |
-| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) | ✅ |
-| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | | ✅ |
-| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | | ❌ |
-| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | | ✅ |
-| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | | ✅ |
-| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | | ✅ |
-| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | | ✅ |
-| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | | ✅ |
-| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | | ✅ |
-| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | | ✅ |
-| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | | ✅ |
-| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) | ✅ |
-| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | | ✅ |
-| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | | ✅ |
-| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | | ✅ |
-| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | | ✅ |
-| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | | ✅ |
-| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | | ❌ |
-| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | | ✅ |
-| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | | ✅ |
-| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | | ✅ |
-| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) | ✅ |
-| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | | ✅ |
-| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | | ✅ |
-| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | | ❌ |
-| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | | ✅ |
-| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) | ✅ |
-| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | | ✅ |
-| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) | ✅ |
-| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | | ✅ |
-| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) | ✅ |
-| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | | ✅ |
-| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | | ✅ |
-| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | | ✅ |
-| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | | ✅ |
-| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | | ✅ |
+| Exercise | Difficulty | Solutions | Prereqs | Practices | Mentor Notes | Jinja? | Approaches? |
+| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ----------- |
+| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | | ✅ | ❌ |
+| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) | ✅ | ❌ |
+| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | | ✅ | ❌ |
+| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | | ✅ | ❌ |
+| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) | ✅ | ❌ |
+| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | | ✅ | ❌ |
+| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | | ✅ | ❌ |
+| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | | ✅ | ❌ |
+| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | | ✅ | ❌ |
+| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | | ❌ | ❌ |
+| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | | ✅ | ❌ |
+| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) | ✅ | ❌ |
+| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) | ✅ | ✅ |
+| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | | ✅ | ❌ |
+| [Bottle Song](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.meta/example.py)┋[most⭐](https://exercism.org/tracks/python/exercises/bottle-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json/#LC1012) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC1012) | | ✅ | ❌ |
+| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | | ✅ | ❌ |
+| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | | ✅ | ❌ |
+| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | | ✅ | ❌ |
+| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) | ✅ | ❌ |
+| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | | ✅ | ❌ |
+| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | | ✅ | ❌ |
+| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | | ✅ | ❌ |
+| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | | ✅ | ❌ |
+| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | | ✅ | ❌ |
+| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | | ✅ | ❌ |
+| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | | ✅ | ❌ |
+| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | | ✅ | ❌ |
+| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | | ✅ | ❌ |
+| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | | ✅ | ❌ |
+| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | | ❌ | ❌ |
+| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | | ✅ | ❌ |
+| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | | ✅ | ❌ |
+| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | | ✅ | ❌ |
+| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | | ✅ | ❌ |
+| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | | ✅ | ❌ |
+| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | | ✅ | ❌ |
+| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) | ✅ | ❌ |
+| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | | ✅ | ✅ |
+| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | | ✅ | ❌ |
+| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) | ✅ | ❌ |
+| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | | ❌ | ❌ |
+| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) | ✅ | ❌ |
+| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | | ✅ | ❌ |
+| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | | ✅ | ❌ |
+| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) | ✅ | ✅ |
+| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) | ✅ | ❌ |
+| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | | ✅ | ❌ |
+| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | | ✅ | ❌ |
+| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) | ✅ | ✅ |
+| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | | ❌ | ❌ |
+| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | | ❌ | ❌ |
+| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | | ✅ | ❌ |
+| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) | ✅ | ❌ |
+| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) | ✅ | ❌ |
+| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) | ✅ | ❌ |
+| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) | ✅ | ❌ |
+| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | | ✅ | ❌ |
+| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | | ✅ | ❌ |
+| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | | ✅ | ❌ |
+| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | | ✅ | ❌ |
+| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | | ❌ | ❌ |
+| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | | ✅ | ❌ |
+| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | | ✅ | ✅ |
+| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | | ✅ | ❌ |
+| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | | ✅ | ❌ |
+| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | | ✅ | ✅ |
+| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | | ✅ | ❌ |
+| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | | ✅ | ❌ |
+| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | | ✅ | ❌ |
+| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | | ✅ | ❌ |
+| [Proverb](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/proverb/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC661) | [config.json](https://github.com/exercism/python/blob/main/config.json#L661) | | ✅ | ❌ |
+| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | | ✅ | ❌ |
+| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | | ✅ | ❌ |
+| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | | ✅ | ❌ |
+| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) | ✅ | ❌ |
+| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | | ✅ | ❌ |
+| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | | ❌ | ❌ |
+| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | | ✅ | ❌ |
+| [Resistor Color Trio](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-trio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC631) | [config.json](https://github.com/exercism/python/blob/main/config.json#L631) | | ✅ | ❌ |
+| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | | ✅ | ❌ |
+| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | | ✅ | ❌ |
+| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | | ✅ | ❌ |
+| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) | ✅ | ❌ |
+| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | | ✅ | ✅ |
+| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | | ❌ | ❌ |
+| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | | ✅ | ❌ |
+| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | | ✅ | ❌ |
+| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | | ✅ | ❌ |
+| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | | ✅ | ❌ |
+| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | | ✅ | ❌ |
+| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | | ✅ | ❌ |
+| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | | ✅ | ❌ |
+| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | | ✅ | ❌ |
+| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) | ✅ | ❌ |
+| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | | ✅ | ❌ |
+| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | | ✅ | ❌ |
+| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | | ✅ | ❌ |
+| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | | ✅ | ❌ |
+| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | | ✅ | ❌ |
+| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | | ❌ | ❌ |
+| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | | ✅ | ❌ |
+| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | | ✅ | ❌ |
+| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | | ✅ | ❌ |
+| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) | ✅ | ❌ |
+| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | | ✅ | ❌ |
+| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | | ✅ | ❌ |
+| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | | ❌ | ❌ |
+| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | | ✅ | ❌ |
+| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) | ✅ | ❌ |
+| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | | ✅ | ❌ |
+| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) | ✅ | ❌ |
+| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | | ✅ | ❌ |
+| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) | ✅ | ❌ |
+| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | | ✅ | ❌ |
+| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | | ✅ | ✅ |
+| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | | ✅ | ❌ |
+| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | | ✅ | ❌ |
+| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | | ✅ | ❌ |
From eb69066cde65f84b8b18055023fd87f53ac47c7c Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 20:51:36 +0100
Subject: [PATCH 279/932] Added killer-sudoku-helper
---
config.json | 16 +++++
.../.docs/instructions.md | 63 +++++++++++++++++++
.../killer-sudoku-helper/.meta/config.json | 21 +++++++
.../killer-sudoku-helper/.meta/example.py | 10 +++
.../killer-sudoku-helper/.meta/template.j2 | 19 ++++++
.../killer-sudoku-helper/.meta/tests.toml | 49 +++++++++++++++
.../killer_sudoku_helper.py | 13 ++++
.../killer_sudoku_helper_test.py | 48 ++++++++++++++
8 files changed, 239 insertions(+)
create mode 100644 exercises/practice/killer-sudoku-helper/.docs/instructions.md
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/config.json
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/example.py
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/template.j2
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/tests.toml
create mode 100644 exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
create mode 100644 exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py
diff --git a/config.json b/config.json
index b2a3c8ebc38..748c14b1052 100644
--- a/config.json
+++ b/config.json
@@ -1005,6 +1005,22 @@
],
"difficulty": 3
},
+ {
+ "slug": "killer-sudoku-helper",
+ "name": "Killer Sudoku Helper",
+ "uuid": "7b16fc93-791b-42a9-8aae-1f78fef2f2f3",
+ "practices": ["list-comprehensions"],
+ "prerequisites": [
+ "conditionals",
+ "lists",
+ "list-methods",
+ "loops",
+ "numbers",
+ "strings",
+ "string-methods"
+ ],
+ "difficulty": 3
+ },
{
"slug": "bottle-song",
"name": "Bottle Song",
diff --git a/exercises/practice/killer-sudoku-helper/.docs/instructions.md b/exercises/practice/killer-sudoku-helper/.docs/instructions.md
new file mode 100644
index 00000000000..93469a09cc0
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.docs/instructions.md
@@ -0,0 +1,63 @@
+# Instructions
+
+A friend of yours is learning how to solve Killer Sudokus (rules below) but struggling to figure out which digits can go in a cage.
+They ask you to help them out by writing a small program that lists all valid combinations for a given cage, and any constraints that affect the cage.
+
+To make the output of your program easy to read, the combinations it returns must be sorted.
+
+## Killer Sudoku Rules
+
+- [Standard Sudoku rules][sudoku-rules] apply.
+- The digits in a cage, usually marked by a dotted line, add up to the small number given in the corner of the cage.
+- A digit may only occur once in a cage.
+
+For a more detailed explanation, check out [this guide][killer-guide].
+
+## Example 1: Cage with only 1 possible combination
+
+In a 3-digit cage with a sum of 7, there is only one valid combination: 124.
+
+- 1 + 2 + 4 = 7
+- Any other combination that adds up to 7, e.g. 232, would violate the rule of not repeating digits within a cage.
+
+![Sudoku grid, with three killer cages that are marked as grouped together. The first killer cage is in the 3×3 box in the top left corner of the grid. The middle column of that box forms the cage, with the followings cells from top to bottom: first cell contains a 1 and a pencil mark of 7, indicating a cage sum of 7, second cell contains a 2, third cell contains a 5. The numbers are highlighted in red to indicate a mistake. The second killer cage is in the central 3×3 box of the grid. The middle column of that box forms the cage, with the followings cells from top to bottom: first cell contains a 1 and a pencil mark of 7, indicating a cage sum of 7, second cell contains a 2, third cell contains a 4. None of the numbers in this cage are highlighted and therefore don't contain any mistakes. The third killer cage follows the outside corner of the central 3×3 box of the grid. It is made up of the following three cells: the top left cell of the cage contains a 2, highlighted in red, and a cage sum of 7. The top right cell of the cage contains a 3. The bottom right cell of the cage contains a 2, highlighted in red. All other cells are empty.][one-solution-img]
+
+## Example 2: Cage with several combinations
+
+In a 2-digit cage with a sum 10, there are 4 possible combinations:
+
+- 19
+- 28
+- 37
+- 46
+
+![Sudoku grid, all squares empty except for the middle column, column 5, which has 8 rows filled. Each continguous two rows form a killer cage and are marked as grouped together. From top to bottom: first group is a cell with value 1 and a pencil mark indicating a cage sum of 10, cell with value 9. Second group is a cell with value 2 and a pencil mark of 10, cell with value 8. Third group is a cell with value 3 and a pencil mark of 10, cell with value 7. Fourth group is a cell with value 4 and a pencil mark of 10, cell with value 6. The last cell in the column is empty.][four-solutions-img]
+
+## Example 3: Cage with several combinations that is restricted
+
+In a 2-digit cage with a sum 10, where the column already contains a 1 and a 4, there are 2 possible combinations:
+
+- 28
+- 37
+
+19 and 46 are not possible due to the 1 and 4 in the column according to standard Sudoku rules.
+
+![Sudoku grid, all squares empty except for the middle column, column 5, which has 8 rows filled. The first row contains a 4, the second is empty, and the third contains a 1. The 1 is highlighted in red to indicate a mistake. The last 6 rows in the column form killer cages of two cells each. From top to bottom: first group is a cell with value 2 and a pencil mark indicating a cage sum of 10, cell with value 8. Second group is a cell with value 3 and a pencil mark of 10, cell with value 7. Third group is a cell with value 1, highlighted in red, and a pencil mark of 10, cell with value 9.][not-possible-img]
+
+## Trying it yourself
+
+If you want to give an approachable Killer Sudoku a go, you can try out [this puzzle][clover-puzzle] by Clover, featured by [Mark Goodliffe on Cracking The Cryptic on the 21st of June 2021][goodliffe-video].
+
+You can also find Killer Sudokus in varying difficulty in numerous newspapers, as well as Sudoku apps, books and websites.
+
+## Credit
+
+The screenshots above have been generated using [F-Puzzles.com](https://www.f-puzzles.com/), a Puzzle Setting Tool by Eric Fox.
+
+[sudoku-rules]: https://masteringsudoku.com/sudoku-rules-beginners/
+[killer-guide]: https://masteringsudoku.com/killer-sudoku/
+[one-solution-img]: https://media.githubusercontent.com/media/exercism/v3-files/main/julia/killer-sudoku-helper/example1.png
+[four-solutions-img]: https://media.githubusercontent.com/media/exercism/v3-files/main/julia/killer-sudoku-helper/example2.png
+[not-possible-img]: https://media.githubusercontent.com/media/exercism/v3-files/main/julia/killer-sudoku-helper/example3.png
+[clover-puzzle]: https://app.crackingthecryptic.com/sudoku/HqTBn3Pr6R
+[goodliffe-video]: https://youtu.be/c_NjEbFEeW0?t=1180
diff --git a/exercises/practice/killer-sudoku-helper/.meta/config.json b/exercises/practice/killer-sudoku-helper/.meta/config.json
new file mode 100644
index 00000000000..eb5a6f84bfc
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/config.json
@@ -0,0 +1,21 @@
+{
+ "authors": [
+ "meatball133",
+ "Bethanyg"
+ ],
+ "contributors": [],
+ "files": {
+ "solution": [
+ "killer_sudoku_helper.py"
+ ],
+ "test": [
+ "killer_sudoku_helper_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "blurb": "Write a tool that makes it easier to solve Killer Sudokus",
+ "source": "Created by Sascha Mann, Jeremy Walker, and BethanyG for the Julia track on Exercism.",
+ "source_url": "https://github.com/exercism/julia/pull/413"
+}
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
new file mode 100644
index 00000000000..ba42d25f7d1
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -0,0 +1,10 @@
+import itertools
+
+def combinations(target, size, exclude):
+ possible = [i for i in range(1, target) if i not in exclude]
+ result = [seq for i in range(len(possible), 0, -1)
+ for seq in itertools.combinations(possible, i)
+ if sum(seq) == target]
+
+
+ return result
diff --git a/exercises/practice/killer-sudoku-helper/.meta/template.j2 b/exercises/practice/killer-sudoku-helper/.meta/template.j2
new file mode 100644
index 00000000000..c107933784f
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/template.j2
@@ -0,0 +1,19 @@
+{%- 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"]["cage"]["sum"] }},
+ {{ case["input"]["cage"]["size"] }},
+ {{ case["input"]["cage"]["exclude"] }}),
+ {{ case["expected"] }})
+{%- endmacro %}
+{{ macros.header()}}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases[0]["cases"] -%}
+ {{ test_case(case) }}
+ {% endfor %}
+ {% for case in cases[1:] -%}
+ {{ test_case(case) }}
+ {% endfor %}
diff --git a/exercises/practice/killer-sudoku-helper/.meta/tests.toml b/exercises/practice/killer-sudoku-helper/.meta/tests.toml
new file mode 100644
index 00000000000..19c23e8a925
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/tests.toml
@@ -0,0 +1,49 @@
+# 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.
+
+[2aaa8f13-11b5-4054-b95c-a906e4d79fb6]
+description = "Trivial 1-digit cages -> 1"
+
+[4645da19-9fdd-4087-a910-a6ed66823563]
+description = "Trivial 1-digit cages -> 2"
+
+[07cfc704-f8aa-41b2-8f9a-cbefb674cb48]
+description = "Trivial 1-digit cages -> 3"
+
+[22b8b2ba-c4fd-40b3-b1bf-40aa5e7b5f24]
+description = "Trivial 1-digit cages -> 4"
+
+[b75d16e2-ff9b-464d-8578-71f73094cea7]
+description = "Trivial 1-digit cages -> 5"
+
+[bcbf5afc-4c89-4ff6-9357-07ab4d42788f]
+description = "Trivial 1-digit cages -> 6"
+
+[511b3bf8-186f-4e35-844f-c804d86f4a7a]
+description = "Trivial 1-digit cages -> 7"
+
+[bd09a60d-3aca-43bd-b6aa-6ccad01bedda]
+description = "Trivial 1-digit cages -> 8"
+
+[9b539f27-44ea-4ff8-bd3d-c7e136bee677]
+description = "Trivial 1-digit cages -> 9"
+
+[0a8b2078-b3a4-4dbd-be0d-b180f503d5c3]
+description = "Cage with sum 45 contains all digits 1:9"
+
+[2635d7c9-c716-4da1-84f1-c96e03900142]
+description = "Cage with only 1 possible combination"
+
+[a5bde743-e3a2-4a0c-8aac-e64fceea4228]
+description = "Cage with several combinations"
+
+[dfbf411c-737d-465a-a873-ca556360c274]
+description = "Cage with several combinations that is restricted"
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
new file mode 100644
index 00000000000..f5103b776ec
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -0,0 +1,13 @@
+import itertools
+
+def combinations(target, size, exclude):
+ result = []
+ possible = [i for i in range(1, target) if i not in exclude]
+ if size == 1:
+ return [[target]]
+ else:
+ for i in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, i):
+ if sum(seq) == target and len(seq) == size:
+ result.append(list(seq))
+ return result
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py
new file mode 100644
index 00000000000..b5732054653
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py
@@ -0,0 +1,48 @@
+import unittest
+
+from killer_sudoku_helper import (
+ combinations,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class KillerSudokuHelperTest(unittest.TestCase):
+ def test_1(self):
+ self.assertEqual(combinations(1, 1, []), [[1]])
+
+ def test_2(self):
+ self.assertEqual(combinations(2, 1, []), [[2]])
+
+ def test_3(self):
+ self.assertEqual(combinations(3, 1, []), [[3]])
+
+ def test_4(self):
+ self.assertEqual(combinations(4, 1, []), [[4]])
+
+ def test_5(self):
+ self.assertEqual(combinations(5, 1, []), [[5]])
+
+ def test_6(self):
+ self.assertEqual(combinations(6, 1, []), [[6]])
+
+ def test_7(self):
+ self.assertEqual(combinations(7, 1, []), [[7]])
+
+ def test_8(self):
+ self.assertEqual(combinations(8, 1, []), [[8]])
+
+ def test_9(self):
+ self.assertEqual(combinations(9, 1, []), [[9]])
+
+ def test_cage_with_sum_45_contains_all_digits_1_9(self):
+ self.assertEqual(combinations(45, 9, []), [[1, 2, 3, 4, 5, 6, 7, 8, 9]])
+
+ def test_cage_with_only_1_possible_combination(self):
+ self.assertEqual(combinations(7, 3, []), [[1, 2, 4]])
+
+ def test_cage_with_several_combinations(self):
+ self.assertEqual(combinations(10, 2, []), [[1, 9], [2, 8], [3, 7], [4, 6]])
+
+ def test_cage_with_several_combinations_that_is_restricted(self):
+ self.assertEqual(combinations(10, 2, [1, 4]), [[2, 8], [3, 7]])
From 14545d736f29c204fe858850d8ebaec7e0857061 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 20:54:19 +0100
Subject: [PATCH 280/932] fixes
---
.../practice/killer-sudoku-helper/.meta/example.py | 13 ++++++++-----
.../killer-sudoku-helper/killer_sudoku_helper.py | 13 +------------
2 files changed, 9 insertions(+), 17 deletions(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index ba42d25f7d1..f5103b776ec 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -1,10 +1,13 @@
import itertools
def combinations(target, size, exclude):
+ result = []
possible = [i for i in range(1, target) if i not in exclude]
- result = [seq for i in range(len(possible), 0, -1)
- for seq in itertools.combinations(possible, i)
- if sum(seq) == target]
-
-
+ if size == 1:
+ return [[target]]
+ else:
+ for i in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, i):
+ if sum(seq) == target and len(seq) == size:
+ result.append(list(seq))
return result
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
index f5103b776ec..03632d749cf 100644
--- a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -1,13 +1,2 @@
-import itertools
-
def combinations(target, size, exclude):
- result = []
- possible = [i for i in range(1, target) if i not in exclude]
- if size == 1:
- return [[target]]
- else:
- for i in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, i):
- if sum(seq) == target and len(seq) == size:
- result.append(list(seq))
- return result
+ pass
From 2c102dcef1e0a1325d188c08b0f1db827e135075 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 21:26:06 +0100
Subject: [PATCH 281/932] Fix
---
.../killer-sudoku-helper/killer_sudoku_helper.py | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
index 03632d749cf..ba6e873ab27 100644
--- a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -1,2 +1,13 @@
+import itertools
+
def combinations(target, size, exclude):
- pass
+ result = []
+ if size == 1:
+ return [[target]]
+ else:
+ possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
+ for index in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, index):
+ if sum(seq) == target and len(seq) == size:
+ result.append(list(seq))
+ return result
From dcfe63766ccb15f15bfba09d47b964cbbda9258e Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 21:28:16 +0100
Subject: [PATCH 282/932] Fixes
---
.../practice/killer-sudoku-helper/.meta/example.py | 6 +++---
.../killer-sudoku-helper/killer_sudoku_helper.py | 13 +------------
2 files changed, 4 insertions(+), 15 deletions(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index f5103b776ec..ba6e873ab27 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -2,12 +2,12 @@
def combinations(target, size, exclude):
result = []
- possible = [i for i in range(1, target) if i not in exclude]
if size == 1:
return [[target]]
else:
- for i in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, i):
+ possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
+ for index in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, index):
if sum(seq) == target and len(seq) == size:
result.append(list(seq))
return result
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
index ba6e873ab27..03632d749cf 100644
--- a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -1,13 +1,2 @@
-import itertools
-
def combinations(target, size, exclude):
- result = []
- if size == 1:
- return [[target]]
- else:
- possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
- for index in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, index):
- if sum(seq) == target and len(seq) == size:
- result.append(list(seq))
- return result
+ pass
From 3e05a50b2763039cff72e1b938c8c9908d1290f4 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 21:32:42 +0100
Subject: [PATCH 283/932] Changed to 4 as difficutly
---
config.json | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/config.json b/config.json
index 748c14b1052..f267cbd83ac 100644
--- a/config.json
+++ b/config.json
@@ -1005,22 +1005,6 @@
],
"difficulty": 3
},
- {
- "slug": "killer-sudoku-helper",
- "name": "Killer Sudoku Helper",
- "uuid": "7b16fc93-791b-42a9-8aae-1f78fef2f2f3",
- "practices": ["list-comprehensions"],
- "prerequisites": [
- "conditionals",
- "lists",
- "list-methods",
- "loops",
- "numbers",
- "strings",
- "string-methods"
- ],
- "difficulty": 3
- },
{
"slug": "bottle-song",
"name": "Bottle Song",
@@ -1359,6 +1343,22 @@
],
"difficulty": 4
},
+ {
+ "slug": "killer-sudoku-helper",
+ "name": "Killer Sudoku Helper",
+ "uuid": "7b16fc93-791b-42a9-8aae-1f78fef2f2f3",
+ "practices": ["list-comprehensions"],
+ "prerequisites": [
+ "conditionals",
+ "lists",
+ "list-methods",
+ "loops",
+ "numbers",
+ "strings",
+ "string-methods"
+ ],
+ "difficulty": 4
+ },
{
"slug": "tournament",
"name": "Tournament",
From 30d481c50d816f1bc79ce87f08ea6c8e06130cd6 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 21:36:35 +0100
Subject: [PATCH 284/932] Update
exercises/practice/killer-sudoku-helper/.meta/example.py
Co-authored-by: BethanyG
---
exercises/practice/killer-sudoku-helper/.meta/example.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index ba6e873ab27..3da5f2ccfd2 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -2,12 +2,15 @@
def combinations(target, size, exclude):
result = []
+ possible = [index for index in
+ range(1, int((target ** 2 /size) ** 0.6))
+ if index not in exclude]
+
if size == 1:
return [[target]]
else:
- possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
for index in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, index):
+ for seq in itertools.combinations(possible, i):
if sum(seq) == target and len(seq) == size:
result.append(list(seq))
return result
From 87d6924da0fa9d9cf4e618e1e9bc8d9becafbac8 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 21:38:49 +0100
Subject: [PATCH 285/932] Update
exercises/practice/killer-sudoku-helper/.meta/example.py
Co-authored-by: BethanyG
---
exercises/practice/killer-sudoku-helper/.meta/example.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index 3da5f2ccfd2..ac3a7f87962 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -10,7 +10,7 @@ def combinations(target, size, exclude):
return [[target]]
else:
for index in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, i):
+ for seq in itertools.combinations(possible, index):
if sum(seq) == target and len(seq) == size:
result.append(list(seq))
return result
From 0d28a142d3f54be564991aaa2e64c287e3955b3c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:09:30 -0600
Subject: [PATCH 286/932] Create snippet.md
---
.../rna-transcription/.articles/performance/snippet.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/performance/snippet.md
diff --git a/exercises/practice/rna-transcription/.articles/performance/snippet.md b/exercises/practice/rna-transcription/.articles/performance/snippet.md
new file mode 100644
index 00000000000..f51d300d519
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/performance/snippet.md
@@ -0,0 +1,4 @@
+```
+translate maketrans: 2.502872000914067e-07
+dictionary join: 1.0920033999718725e-06
+```
From caa61dbdaf9f6b4b2b55f2c75a3b03423d896cd1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:12:00 -0600
Subject: [PATCH 287/932] Create config.json
---
.../practice/rna-transcription/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/config.json
diff --git a/exercises/practice/rna-transcription/.articles/config.json b/exercises/practice/rna-transcription/.articles/config.json
new file mode 100644
index 00000000000..0b28ebcbaa7
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "bc3b17f7-a748-4cb3-b44d-e7049e321bc3",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach for RNA Transcription.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 5ce38dbfcc1fbc2a9121f42a75d1f461c2b70224 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:12:47 -0600
Subject: [PATCH 288/932] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py b/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..3980aa1748a
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
@@ -0,0 +1,25 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""to_rna("ACGTGGTCTTAA")""",
+ """
+LOOKUP = str.maketrans("GCTA","CGAU")
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
+
+""", number=loops) / loops
+
+print(f"translate maketrans: {val}")
+
+val = timeit.timeit("""to_rna("ACGTGGTCTTAA")""",
+ """
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
+
+""", number=loops) / loops
+
+print(f"dictionary join: {val}")
From 13b0fbf24c3057fb77103d142588f95db519c2d2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:16:38 -0600
Subject: [PATCH 289/932] Create config.json
---
.../rna-transcription/.approaches/config.json | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/config.json
diff --git a/exercises/practice/rna-transcription/.approaches/config.json b/exercises/practice/rna-transcription/.approaches/config.json
new file mode 100644
index 00000000000..9ab41145480
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/config.json
@@ -0,0 +1,22 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "9c3d3762-8473-49f2-8004-6d611c958c38",
+ "slug": "translate-maketrans",
+ "title": "translate maketrans",
+ "blurb": "Use translate with maketrans to return the value.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "fbc6be87-dec4-4c4b-84cf-fcc1ed2d6d41",
+ "slug": "dictionary-join",
+ "title": "dictionary join",
+ "blurb": "Use a dictionary look-up with join to return the value.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 2e5efcf01977718a4364edf542a844e02326ecc9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:40:41 -0600
Subject: [PATCH 290/932] Create introduction.md
---
.../.approaches/introduction.md | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/introduction.md
diff --git a/exercises/practice/rna-transcription/.approaches/introduction.md b/exercises/practice/rna-transcription/.approaches/introduction.md
new file mode 100644
index 00000000000..fa580afdb4d
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/introduction.md
@@ -0,0 +1,45 @@
+# Introduction
+
+There are at least two idiomatic approaches to solve RNA Transcription.
+One approach is to use `translate` with `maketrans`.
+Another approach is to do a dictionary lookup on each character and join the results.
+
+## General guidance
+
+Whichever approach is used needs to return the RNA complement for each DNA value.
+`translate` with `maketrans` transcribes using the [ASCII][ASCII] values of the characters.
+Using a dictionary with `join` transcribes using the string values of the characters.
+
+## Approach: `translate` with `maketrans`
+
+```python
+LOOKUP = str.maketrans("GCTA", "CGAU")
+
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
+
+```
+
+For more information, check the [`translate` with `maketrans` approach][approach-translate-maketrans].
+
+## Approach: dictionary look-up with `join`
+
+```python
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
+
+```
+
+For more information, check the [dictionary look-up with `join` approach][approach-dictionary-join].
+
+## Which approach to use?
+
+The `translate` with `maketrans` approach benchmarked over four times faster than the dictionary look-up with `join` approach.
+
+[ASCII]: https://www.asciitable.com/
+[approach-translate-maketrans]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/translate-maketrans
+[approach-dictionary-join]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/dictionary-join
From a4f312abf41f12dc8d8c0b6bf65656e06b3bb97b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:41:46 -0600
Subject: [PATCH 291/932] Create snippet.txt
---
.../.approaches/translate-maketrans/snippet.txt | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt
diff --git a/exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt b/exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt
new file mode 100644
index 00000000000..2d00b83be6b
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt
@@ -0,0 +1,5 @@
+LOOKUP = str.maketrans("GCTA", "CGAU")
+
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
From bf4515ed172100d72aa34b11422372c24db8a644 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:42:27 -0600
Subject: [PATCH 292/932] Create snippet.txt
---
.../.approaches/dictionary-join/snippet.txt | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt
diff --git a/exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt b/exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt
new file mode 100644
index 00000000000..558bf981408
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt
@@ -0,0 +1,5 @@
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
From eae8944e382dcf97a56a7646d3e37275c3c0bd99 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 07:04:00 -0600
Subject: [PATCH 293/932] Create content.md
---
.../translate-maketrans/content.md | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md
diff --git a/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md
new file mode 100644
index 00000000000..fcd55730d91
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md
@@ -0,0 +1,34 @@
+# `translate()` with `maketrans()`
+
+```python
+LOOKUP = str.maketrans("GCTA", "CGAU")
+
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
+
+```
+
+This approach starts by defining a [dictionary][dictionaries] (also called a translation table in this context) by calling the [`maketrans()`][maketrans] method.
+
+Python doesn't _enforce_ having real constant values,
+but the `LOOKUP` translation table is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+
+The translation table that is created uses the [ASCII][ASCII] values (also called the ordinal values) for each letter in the two strings.
+The ASCII value for "G" in the first string is the key for the ASCII value of "C" in the second string, and so on.
+
+In the `to_rna()` function, the [`translate()`][translate] method is called on the input,
+and is passed the translation table.
+The output of `translate()` is a string where all of the input DNA characters have been replaced by their RNA complement in the translation table.
+
+
+```exercism/note
+As of this writing, no invalid DNA characters are in the argument to `to_rna()`, so there is no error handling required for invalid input.
+```
+
+[dictionaries]: https://docs.python.org/3/tutorial/datastructures.html?#dictionaries
+[maketrans]: https://docs.python.org/3/library/stdtypes.html?#str.maketrans
+[const]: https://realpython.com/python-constants/
+[translate]: https://docs.python.org/3/library/stdtypes.html?#str.translate
+[ASCII]: https://www.asciitable.com/
From 80d267beb61167391853affda6fc21e51ac2643d Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 07:07:08 -0600
Subject: [PATCH 294/932] Update introduction.md
---
.../.approaches/introduction.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/rna-transcription/.approaches/introduction.md b/exercises/practice/rna-transcription/.approaches/introduction.md
index fa580afdb4d..ca2d74a1090 100644
--- a/exercises/practice/rna-transcription/.approaches/introduction.md
+++ b/exercises/practice/rna-transcription/.approaches/introduction.md
@@ -1,16 +1,16 @@
# Introduction
There are at least two idiomatic approaches to solve RNA Transcription.
-One approach is to use `translate` with `maketrans`.
+One approach is to use `translate()` with `maketrans()`.
Another approach is to do a dictionary lookup on each character and join the results.
## General guidance
Whichever approach is used needs to return the RNA complement for each DNA value.
-`translate` with `maketrans` transcribes using the [ASCII][ASCII] values of the characters.
-Using a dictionary with `join` transcribes using the string values of the characters.
+The `translate()` method with `maketrans()` transcribes using the [ASCII][ASCII] values of the characters.
+Using a dictionary look-up with `join()` transcribes using the string values of the characters.
-## Approach: `translate` with `maketrans`
+## Approach: `translate()` with `maketrans()`
```python
LOOKUP = str.maketrans("GCTA", "CGAU")
@@ -21,9 +21,9 @@ def to_rna(dna_strand):
```
-For more information, check the [`translate` with `maketrans` approach][approach-translate-maketrans].
+For more information, check the [`translate()` with `maketrans()` approach][approach-translate-maketrans].
-## Approach: dictionary look-up with `join`
+## Approach: dictionary look-up with `join()`
```python
LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
@@ -34,11 +34,11 @@ def to_rna(dna_strand):
```
-For more information, check the [dictionary look-up with `join` approach][approach-dictionary-join].
+For more information, check the [dictionary look-up with `join()` approach][approach-dictionary-join].
## Which approach to use?
-The `translate` with `maketrans` approach benchmarked over four times faster than the dictionary look-up with `join` approach.
+The `translate()` with `maketrans()` approach benchmarked over four times faster than the dictionary look-up with `join()` approach.
[ASCII]: https://www.asciitable.com/
[approach-translate-maketrans]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/translate-maketrans
From e5f076f88c03ccf1cef29bfa0475a194d2d61177 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 07:18:15 -0600
Subject: [PATCH 295/932] Create content.md
---
.../.approaches/dictionary-join/content.md | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/dictionary-join/content.md
diff --git a/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md
new file mode 100644
index 00000000000..f3ec1f755fb
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md
@@ -0,0 +1,30 @@
+# dictionary look-up with `join`
+
+```python
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
+
+```
+
+This approach starts by defining a [dictionary][dictionaries] to map the DNA values to RNA values.
+
+Python doesn't _enforce_ having real constant values,
+but the `LOOKUP` dictionary is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+
+In the `to_rna()` function, the [`join()`][join] method is called on an empty string,
+and is passed the list created from a [list comprehension][list-comprehension].
+
+The list comprehension iterates each character in the input,
+looks up the DNA character in the look-up dictionary, and outputs its matching RNA character as an element in the list.
+
+The `join()` method collects the list of RNA characters back into a string.
+Since an empty string is the separator for the `join()`, there are no spaces between the RNA characters in the string.
+
+[dictionaries]: https://docs.python.org/3/tutorial/datastructures.html?#dictionaries
+[const]: https://realpython.com/python-constants/
+[join]: https://docs.python.org/3/library/stdtypes.html?#str.join
+[list-comprehension]: https://realpython.com/list-comprehension-python/#using-list-comprehensions
From caf3bf1b2b0091f8f6304cec27b1805f9cb8a29c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 10:31:39 -0600
Subject: [PATCH 296/932] Create content.md
---
.../.articles/performance/content.md | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/performance/content.md
diff --git a/exercises/practice/rna-transcription/.articles/performance/content.md b/exercises/practice/rna-transcription/.articles/performance/content.md
new file mode 100644
index 00000000000..9419c175678
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/performance/content.md
@@ -0,0 +1,27 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently calculate the RNA Transcription.
+
+The [approaches page][approaches] lists two idiomatic approaches to this exercise:
+
+1. [Using `translate()` with `maketrans()` approach][approach-translate-maketrans]
+2. [Using dictionary look-up with `join()` approach][approach-dictionary-join]
+
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+translate maketrans: 2.502872000914067e-07
+dictionary join: 1.0920033999718725e-06
+```
+
+At about `250` nanoseconds, the `translate()` with `maketrans()` approach is more than four times faster than the dictionary with `join()` approach,
+which takes about `1092` nanoseconds.
+
+[approaches]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches
+[approach-translate-maketrans]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/translate-maketrans
+[approach-dictionary-join]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/dictionary-join
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
From 62b9769a9372ee5312f3f37ee80651bd18335e14 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:40:49 -0600
Subject: [PATCH 297/932] Update introduction.md
---
exercises/practice/bob/.approaches/introduction.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.approaches/introduction.md b/exercises/practice/bob/.approaches/introduction.md
index ccdfa93f90f..07d68d1a1e7 100644
--- a/exercises/practice/bob/.approaches/introduction.md
+++ b/exercises/practice/bob/.approaches/introduction.md
@@ -55,8 +55,7 @@ def response(hey_bob):
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
From 2d4d1724cf9dc9a887dc1fb1725c6b6475167d8e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:43:58 -0600
Subject: [PATCH 298/932] Update content.md
---
.../bob/.approaches/if-statements-nested/content.md | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/content.md b/exercises/practice/bob/.approaches/if-statements-nested/content.md
index 06b5ed527cb..5867427afd7 100644
--- a/exercises/practice/bob/.approaches/if-statements-nested/content.md
+++ b/exercises/practice/bob/.approaches/if-statements-nested/content.md
@@ -10,8 +10,7 @@ def response(hey_bob):
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
@@ -21,6 +20,12 @@ def response(hey_bob):
In this approach you have a series of `if` statements using the calculated variables to evaluate the conditions, some of which are nested.
As soon as a `True` condition is found, the correct response is returned.
+```exercism/note
+Note that there are no `elif` or `else` statements.
+If an `if` statement can return, then an `elif` or `else` is not needed.
+Execution will either return or will continue to the next statement anyway.
+```
+
The [`rstrip`][rstrip] method is applied to the input to eliminate any whitespace at the end of the input.
If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the response for saying nothing.
Since it doesn't matter if there is leading whitespace, the `rstrip` function is used instead of [`strip`][strip].
From c29af2e826efe41f0873a393c47aa91b964b0976 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:44:41 -0600
Subject: [PATCH 299/932] Update snippet.txt
---
.../practice/bob/.approaches/if-statements-nested/snippet.txt | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
index 0df6c2fe9e1..2362a7ac91c 100644
--- a/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
+++ b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
@@ -1,8 +1,7 @@
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
From 07bbb745c779bbcaf4955e0f03189246c6a30ef7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:48:23 -0600
Subject: [PATCH 300/932] Update Benchmark.py
---
exercises/practice/bob/.articles/performance/code/Benchmark.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.articles/performance/code/Benchmark.py b/exercises/practice/bob/.articles/performance/code/Benchmark.py
index e726ed19532..3e5a011fcbc 100644
--- a/exercises/practice/bob/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/bob/.articles/performance/code/Benchmark.py
@@ -34,8 +34,7 @@ def response(hey_bob):
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
From ce0a2593ccdc3ff44d996b76403a67bfe8d9d7bf Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Wed, 7 Dec 2022 22:25:34 +0100
Subject: [PATCH 301/932] fix
---
.../locomotive_engineer_test.py | 54 +++++++++----------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
index b3a46939fdb..d3d3178fdfe 100644
--- a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
@@ -15,7 +15,7 @@ def test_get_list_of_wagons(self):
output_data = [[1,5,2,7,4], [1,5], [1], [1,9,3], [1,10,6,3,9,8,4,14,24,7]]
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):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different wagon list instead.'
self.assertEqual(get_list_of_wagons(*input_data), output_data, msg=error_msg)
@@ -32,59 +32,59 @@ def test_fix_list_of_wagons(self):
[1, 8, 6, 4, 5, 9, 21, 2, 13, 25, 7, 19, 10, 3, 14]
]
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):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different wagon list instead.'
self.assertEqual(fix_list_of_wagons(input_data[0], input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=3)
def test_add_missing_stops(self):
- input_data = (({"from": "Berlin", "to": "Hamburg"}, {"stop_1": "Lepzig", "stop_2": "Hannover", "stop_3": "Frankfurt"}),
- ({"from": "Paris", "to": "London"}, {"stop_1": "Lille"}),
- ({"from": "New York", "to": "Philadelphia"},{}),
- ({"from": "Gothenburg", "to": "Copenhagen"}, {"stop_1": "Kungsbacka", "stop_2": "Varberg", "stop_3": "Halmstad", "stop_4": "Angelholm", "stop_5": "Lund", "stop_6": "Malmo"})
+ input_data = (({'from': 'Berlin', 'to': 'Hamburg'}, {'stop_1': 'Lepzig', 'stop_2': 'Hannover', 'stop_3': 'Frankfurt'}),
+ ({'from': 'Paris', 'to': 'London'}, {'stop_1': 'Lille'}),
+ ({'from': 'New York', 'to': 'Philadelphia'},{}),
+ ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'stop_1': 'Kungsbacka', 'stop_2': 'Varberg', 'stop_3': 'Halmstad', 'stop_4': 'Angelholm', 'stop_5': 'Lund', 'stop_6': 'Malmo'})
)
- output_data = [{"from": "Berlin", "to": "Hamburg", "stops": ["Lepzig", "Hannover", "Frankfurt"]},
- {"from": "Paris", "to": "London", "stops": ["Lille"]},
- {"from": "New York", "to": "Philadelphia", "stops": []},
- {"from": "Gothenburg", "to": "Copenhagen", "stops": ["Kungsbacka", "Varberg", "Halmstad", "Angelholm", "Lund", "Malmo"]}
+ output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'stops': ['Lepzig', 'Hannover', 'Frankfurt']},
+ {'from': 'Paris', 'to': 'London', 'stops': ['Lille']},
+ {'from': 'New York', 'to': 'Philadelphia', 'stops': []},
+ {'from': 'Gothenburg', 'to': 'Copenhagen', 'stops': ['Kungsbacka', 'Varberg', 'Halmstad', 'Angelholm', 'Lund', 'Malmo']}
]
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):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different set of stops instead.'
self.assertEqual(add_missing_stops(input_data[0], **input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=4)
def test_extend_route_information(self):
- input_data = [({"from": "Berlin", "to": "Hamburg"}, {"timeOfArrival": "12:00", "precipitation": "10", "temperature": "5", "caboose": "yes"}),
- ({"from": "Paris", "to": "London"}, {"timeOfArrival": "10:30", "temperature": "20", "length": 15}),
- ({"from": "Gothenburg", "to": "Copenhagen"}, {"precipitation": "1", "timeOfArrival": "21:20", "temperature": "-6"})]
- output_data = [{"from": "Berlin", "to": "Hamburg", "timeOfArrival": "12:00", "precipitation": "10", "temperature": "5", "caboose": "yes"},
- {"from": "Paris", "to": "London", "timeOfArrival": "10:30", "temperature": "20", "length": 15},
- {"from": "Gothenburg", "to": "Copenhagen", "precipitation": "1", "timeOfArrival": "21:20", "temperature": "-6"}
+ input_data = [({'from': 'Berlin', 'to': 'Hamburg'}, {'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'}),
+ ({'from': 'Paris', 'to': 'London'}, {'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'}),
+ ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'})]
+ output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'},
+ {'from': 'Paris', 'to': 'London', 'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'},
+ {'from': 'Gothenburg', 'to': 'Copenhagen', 'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'}
]
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):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different route dictionary instead.'
self.assertEqual(extend_route_information(input_data[0], input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=5)
def test_fix_wagon_depot(self):
input_data = (
- [[(2, "red"), (4, "red"), (8, "red")], [(5, "blue"), (9, "blue"), (13, "blue")], [(3, "orange"), (7, "orange"), (11, "orange")]],
- [[(6, "blue"), (10, "blue"), (14, "blue")], [(7, "red"), (4, "red"), (2, "red")], [(3, "orange"), (11, "orange"), (15, "orange")]],
- [[(7, "pink"), (4, "pink"), (2, "pink")],[(10, "green"), (6, "green"), (14, "green")], [(9, "yellow"), (5, "yellow"), (13, "yellow")]],
- [[(3, "purple"), (11, "purple"), (15, "purple")], [(20, "black"), (16, "black"), (12, "black")], [(19, "white"), (17, "white"), (18, "white")]]
+ [[(2, 'red'), (4, 'red'), (8, 'red')], [(5, 'blue'), (9, 'blue'), (13, 'blue')], [(3, 'orange'), (7, 'orange'), (11, 'orange')]],
+ [[(6, 'blue'), (10, 'blue'), (14, 'blue')], [(7, 'red'), (4, 'red'), (2, 'red')], [(3, 'orange'), (11, 'orange'), (15, 'orange')]],
+ [[(7, 'pink'), (4, 'pink'), (2, 'pink')], [(10, 'green'), (6, 'green'), (14, 'green')], [(9, 'yellow'), (5, 'yellow'), (13, 'yellow')]],
+ [[(3, 'purple'), (11, 'purple'), (15, 'purple')], [(20, 'black'), (16, 'black'), (12, 'black')], [(19, 'white'), (17, 'white'), (18, 'white')]]
)
output_data = (
- [[(2, "red"), (5, "blue"), (3, "orange")],[(4, "red"), (9, "blue"), (7, "orange")], [(8, "red"), (13, "blue"), (11, "orange")]],
- [[(6, "blue"), (7, "red"), (3, "orange")],[(10, "blue"), (4, "red"), (11, "orange")], [(14, "blue"), (2, "red"), (15, "orange")]],
- [[(7, "pink"), (10, "green"), (9, "yellow")], [(4, "pink"), (6, "green"), (5, "yellow")], [(2, "pink"), (14, "green"), (13, "yellow")]],
- [[(3, "purple"), (20, "black"), (19, "white")], [(11, "purple"), (16, "black"), (17, "white")], [(15, "purple"), (12, "black"), (18, "white")]]
+ [[(2, 'red'), (5, 'blue'), (3, 'orange')], [(4, 'red'), (9, 'blue'), (7, 'orange')], [(8, 'red'), (13, 'blue'), (11, 'orange')]],
+ [[(6, 'blue'), (7, 'red'), (3, 'orange')], [(10, 'blue'), (4, 'red'), (11, 'orange')], [(14, 'blue'), (2, 'red'), (15, 'orange')]],
+ [[(7, 'pink'), (10, 'green'), (9, 'yellow')], [(4, 'pink'), (6, 'green'), (5, 'yellow')], [(2, 'pink'), (14, 'green'), (13, 'yellow')]],
+ [[(3, 'purple'), (20, 'black'), (19, 'white')], [(11, 'purple'), (16, 'black'), (17, 'white')], [(15, 'purple'), (12, 'black'), (18, 'white')]]
)
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):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different wagon depot list instead.'
self.assertEqual(fix_wagon_depot(input_data), output_data, msg=error_msg)
From 154a809493e4f41a44942c0a17d5755226045d1b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:47:54 -0600
Subject: [PATCH 302/932] Create config.json
---
.../practice/luhn/.approaches/config.json | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/config.json
diff --git a/exercises/practice/luhn/.approaches/config.json b/exercises/practice/luhn/.approaches/config.json
new file mode 100644
index 00000000000..37536d78c14
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "568ca4f5-1046-4750-b3c2-6a50b84ed8ed",
+ "slug": "reversed-for",
+ "title": "reversed for loop",
+ "blurb": "Use reversed with a for loop to validate the number.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "24d6c283-3a7d-4315-91db-b3160f3df567",
+ "slug": "replace-reverse-enumerate",
+ "title": "replace, reverse, enumerate",
+ "blurb": "Use replace, reverse, and enumerate to validate the number.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "fca6a89a-baa9-4aeb-b419-130d6ad14564",
+ "slug": "recursion",
+ "title": "recursion",
+ "blurb": "Use recursion to validate the number.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 2b0d5b7a0e15611ad93a3cae1059a59e8524dde3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:49:40 -0600
Subject: [PATCH 303/932] Create config.json
---
exercises/practice/luhn/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/config.json
diff --git a/exercises/practice/luhn/.articles/config.json b/exercises/practice/luhn/.articles/config.json
new file mode 100644
index 00000000000..132e55513e7
--- /dev/null
+++ b/exercises/practice/luhn/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "fa3d646c-b075-49a4-80b7-b339f9022aea",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach to validating Luhn.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From de25ba653df2dc43146be1c4a10da1b6f2de0adb Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:50:27 -0600
Subject: [PATCH 304/932] Create snippet.md
---
exercises/practice/luhn/.articles/performance/snippet.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/performance/snippet.md
diff --git a/exercises/practice/luhn/.articles/performance/snippet.md b/exercises/practice/luhn/.articles/performance/snippet.md
new file mode 100644
index 00000000000..de2b9ca564f
--- /dev/null
+++ b/exercises/practice/luhn/.articles/performance/snippet.md
@@ -0,0 +1,5 @@
+```
+reversed for: 1.1550310099963099e-05
+replace reverse enumerate: 1.0071774299954995e-05
+recursion: 2.4321520800003783e-05
+```
From fa11649647278783f1d772c8f61c1994d248c2db Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:51:03 -0600
Subject: [PATCH 305/932] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 104 ++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..2f95127822a
--- /dev/null
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -0,0 +1,104 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""Luhn("9999999999 9999999999 9999999999 9999999999").valid()""",
+ """
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ total = 0
+ pos = 0
+ for ltr in reversed(num):
+ if ltr == " ":
+ continue
+ if not ltr.isdigit():
+ return False
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+
+""", number=loops) / loops
+
+print(f"reversed for: {val}")
+
+val = timeit.timeit("""Luhn("9999999999 9999999999 9999999999 9999999999").valid()""",
+ """
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ num = num.replace(' ', '')
+ if not num.isdigit():
+ return False
+ total = 0
+ for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+""", number=loops) / loops
+
+print(f"replace reverse enumerate: {val}")
+
+val = timeit.timeit("""Luhn("9999999999 9999999999 9999999999 9999999999").valid()""",
+ """
+class Luhn:
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(0, 0, list(card_num[::-1]))
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(pos, sum, chars):
+ if not chars:
+ if pos < 2:
+ return False
+ return sum % 10 == 0
+ else:
+ head, *tail = chars
+ if head.isdigit():
+ if pos % 2 == 0:
+ return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
+ else:
+ return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
+ if head == " ":
+ return Luhn.luhny_bin(pos, sum, tail)
+ return False
+
+""", number=loops) / loops
+
+print(f"recursion: {val}")
From d9e5d3a991051860aef7294355a0508ed880b894 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:12:32 -0600
Subject: [PATCH 306/932] Create introduction.md
---
.../practice/luhn/.approaches/introduction.md | 93 +++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/introduction.md
diff --git a/exercises/practice/luhn/.approaches/introduction.md b/exercises/practice/luhn/.approaches/introduction.md
new file mode 100644
index 00000000000..378537c17fa
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/introduction.md
@@ -0,0 +1,93 @@
+# Introduction
+
+There are many idiomatic ways to solve Luhn.
+Among them are:
+- You can use a for loop on a `reversed()` iterator.
+- You can scrub the input with `replace()` and test with `isdigit()` before reversing the input to `enumerate()` it.
+
+## General guidance
+
+One important aspect to solving Luhn is to allow for spaces in the input and to disallow all other non-numeric characters.
+Another important aspect is to handle the value of each digit according to its position in the string.
+Another consideration may be to calculate the validity only once, no matter how many times `valid()` is called.
+
+## Approach: `reversed()` `for` loop
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ total = 0
+ pos = 0
+ for ltr in reversed(num):
+ if ltr == " ":
+ continue
+ if not ltr.isdigit():
+ return False
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+```
+
+For more information, check the [`reversed()` `for` loop approach][approach-reversed-for].
+
+## Approach: `replace()`, reverse, `enumerate()`
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ num = num.replace(' ', '')
+ if not num.isdigit():
+ return False
+ total = 0
+ for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+```
+
+For more information, check the [`replace()`, reverse, `enumerate()` approach][approach-replace-reverse-enumerate].
+
+## Other approaches
+
+Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows:
+
+### Other approach: recursion
+
+Another approach can use recursion to validate the number.
+For more information, check the [recursion approach][approach-recursion].
+
+[approach-reversed-for]: https://exercism.org/tracks/python/exercises/luhn/approaches/reversed-for
+[approach-replace-reverse-enumerate]: https://exercism.org/tracks/python/exercises/luhn/approaches/replace-reverse-enumerate
+[approach-recursion]: https://exercism.org/tracks/python/exercises/luhn/approaches/recursion
From 661677505bba98dd73f7fbbb520d71c66b64ace0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:14:17 -0600
Subject: [PATCH 307/932] Create content.md
---
.../luhn/.approaches/recursion/content.md | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/recursion/content.md
diff --git a/exercises/practice/luhn/.approaches/recursion/content.md b/exercises/practice/luhn/.approaches/recursion/content.md
new file mode 100644
index 00000000000..a4f682c4e71
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/recursion/content.md
@@ -0,0 +1,32 @@
+# Recursion
+
+```python
+class Luhn:
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(0, 0, list(card_num[::-1]))
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(pos, sum, chars):
+ if not chars:
+ if pos < 2:
+ return False
+ return sum % 10 == 0
+ else:
+ head, *tail = chars
+ if head.isdigit():
+ if pos % 2 == 0:
+ return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
+ else:
+ return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
+ if head == " ":
+ return Luhn.luhny_bin(pos, sum, tail)
+ return False
+
+```
From 8dca95d30ae40d0aae7add69ae6d0e438df2f2b0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:20:54 -0600
Subject: [PATCH 308/932] Update snippet.md
---
exercises/practice/luhn/.articles/performance/snippet.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/luhn/.articles/performance/snippet.md b/exercises/practice/luhn/.articles/performance/snippet.md
index de2b9ca564f..532b085ce2a 100644
--- a/exercises/practice/luhn/.articles/performance/snippet.md
+++ b/exercises/practice/luhn/.articles/performance/snippet.md
@@ -1,5 +1,5 @@
```
-reversed for: 1.1550310099963099e-05
-replace reverse enumerate: 1.0071774299954995e-05
+reversed for: 1.0783263299963438e-05
+replace reverse enumerate: 9.933844099985436e-06
recursion: 2.4321520800003783e-05
```
From ee40d4d4bd32918f1f5fc627137cf674c4b4464e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:23:39 -0600
Subject: [PATCH 309/932] Update Benchmark.py
---
.../luhn/.articles/performance/code/Benchmark.py | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
index 2f95127822a..66afd6f49e2 100644
--- a/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -21,18 +21,16 @@ def luhny_bin(num):
total = 0
pos = 0
for ltr in reversed(num):
- if ltr == " ":
- continue
- if not ltr.isdigit():
+ if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ elif ltr != " ":
return False
- if not pos % 2:
- total+= int(ltr)
- else:
- total += Luhn.luhny_tune(int(ltr))
- pos += 1
return pos > 1 and not total % 10
-
""", number=loops) / loops
print(f"reversed for: {val}")
From bd450818d35e4dbfa9e212eb4e0ee62a938db482 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:25:28 -0600
Subject: [PATCH 310/932] Create snippet.txt
---
exercises/practice/luhn/.approaches/recursion/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/recursion/snippet.txt
diff --git a/exercises/practice/luhn/.approaches/recursion/snippet.txt b/exercises/practice/luhn/.approaches/recursion/snippet.txt
new file mode 100644
index 00000000000..c59dd9a5fed
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/recursion/snippet.txt
@@ -0,0 +1,8 @@
+head, *tail = chars
+if head.isdigit():
+ if pos % 2 == 0:
+ return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
+ else:
+ return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
+if head == " ":
+ return Luhn.luhny_bin(pos, sum, tail)
From 8c895d6cbf488b5258f9bbdffee8e09925c40c9f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 11:14:49 -0600
Subject: [PATCH 311/932] Update introduction.md
---
.../practice/luhn/.approaches/introduction.md | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/introduction.md b/exercises/practice/luhn/.approaches/introduction.md
index 378537c17fa..904d1e20361 100644
--- a/exercises/practice/luhn/.approaches/introduction.md
+++ b/exercises/practice/luhn/.approaches/introduction.md
@@ -31,15 +31,14 @@ class Luhn:
total = 0
pos = 0
for ltr in reversed(num):
- if ltr == " ":
- continue
- if not ltr.isdigit():
+ if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ elif ltr != " ":
return False
- if not pos % 2:
- total+= int(ltr)
- else:
- total += Luhn.luhny_tune(int(ltr))
- pos += 1
return pos > 1 and not total % 10
```
From 7f01407de8786b92af48e77c9c0861e5ad2ff2b9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 11:41:09 -0600
Subject: [PATCH 312/932] Create content.md
---
.../luhn/.approaches/reversed-for/content.md | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/reversed-for/content.md
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
new file mode 100644
index 00000000000..24d2897b320
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -0,0 +1,73 @@
+# `reversed()` with a `for` loop
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ total = 0
+ pos = 0
+ for ltr in reversed(num):
+ if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ elif ltr != " ":
+ return False
+ return pos > 1 and not total % 10
+
+```
+
+The `Luhn` object is initialzed with the `card_num` value, which is the number to be validated with the Luhn algorithm.
+The result of the validation is returned from `Luhn`'s `valid()` method.
+In this approach, a member variable is set to the result of running the Luhn algorithm.
+That variable is returned from the `valid()` method.
+
+The methods that do the work have the [`@staticmethod`][static-method] decorator.
+This indicates that the method belongs to the class and is not recreated for every object instance.
+
+In the code example the `luhny_bin` method initializes the `total` and `pos` variables to `0`.
+It then calls [`reversed()`][reversed] on the input and iterates the characters from right to left with a [`for`][for] loop.
+
+The [`isdigit()`][isdigit] method is used to see if the character is a digit.
+The [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+It can be thought of as the expression _not_ having a remainder.
+
+If the position is evenly divided by `2`, then it is even, and the character is converted to an [`int()`][int] and added to the total variable.
+
+If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
+and will subtract `9` from the doubled value if the doubled value is greater than `9`.
+It does this using a [ternary operator][ternary-operator].
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`
+The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
+The resulting value is added to the total variable.
+
+Whether the digit is even or odd, the position is incremented by `1`.
+If the character is not a digit, the the function returns `False` if the character is not a space.
+If the character is a space, the loop will go to the next iteration without incrementing the position variable.
+
+After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+
+[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
+[reversed]: https://docs.python.org/3/library/functions.html?#reversed
+[for]: https://docs.python.org/3/tutorial/controlflow.html#for-statements
+[isdigit]: https://docs.python.org/3/library/stdtypes.html?#str.isdigit
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
+[int]: https://docs.python.org/3/library/functions.html?#int
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[assignment-expression]: https://peps.python.org/pep-0572/
From 71d468ae80fefde30849903e433cd99942818d14 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 11:42:51 -0600
Subject: [PATCH 313/932] Create snippet.txt
---
.../practice/luhn/.approaches/reversed-for/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/reversed-for/snippet.txt
diff --git a/exercises/practice/luhn/.approaches/reversed-for/snippet.txt b/exercises/practice/luhn/.approaches/reversed-for/snippet.txt
new file mode 100644
index 00000000000..46e9ec07fcb
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/reversed-for/snippet.txt
@@ -0,0 +1,8 @@
+if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+elif ltr != " ":
+ return False
From 53c714185e9dd20542e37c9ddd84ae3728adaeb5 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:05:56 -0600
Subject: [PATCH 314/932] Create content.md
---
.../replace-reverse-enumerate/content.md | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
new file mode 100644
index 00000000000..8b2a5919d9d
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
@@ -0,0 +1,73 @@
+# `replace()`, reverse, `enumerate()`
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ num = num.replace(' ', '')
+ if not num.isdigit():
+ return False
+ total = 0
+ for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+```
+
+The `Luhn` object is initialzed with the `card_num` value, which is the number to be validated with the Luhn algorithm.
+The result of the validation is returned from `Luhn`'s `valid()` method.
+In this approach, a member variable is set to the result of running the Luhn algorithm.
+That variable is returned from the `valid()` method.
+
+The methods that do the work have the [`@staticmethod`][static-method] decorator.
+This indicates that the method belongs to the class and is not recreated for every object instance.
+
+In the code example the `luhny_bin` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
+The [`isdigit()`][isdigit] method is then used to see if all of the remaining characters are digits.
+If not, the function returns `False`.
+
+[Slicing][slicing] syntax (`[::-1`) is used to reverse the characters in the input, which is then passed into the [`enumerate()`][enumerate] method.
+The [`for`][for] loop uses `enumerate()` to iterate the reversed characters in the input, returning the character and its position in the string.
+
+The [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+It can be thought of as the expression _not_ having a remainder.
+
+If the position is evenly divided by `2`, then it is even, and the character is converted to an [`int()`][int] and added to the total variable.
+
+If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
+and will subtract `9` from the doubled value if the doubled value is greater than `9`.
+It does this using a [ternary operator][ternary-operator].
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`.
+The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
+The resulting value is added to the total variable.
+
+After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+
+[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
+[replace]: https://docs.python.org/3/library/stdtypes.html?#str.replace
+[isdigit]: https://docs.python.org/3/library/stdtypes.html?#str.isdigit
+[enumerate]: https://docs.python.org/3/library/functions.html?#enumerate
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[for]: https://docs.python.org/3/tutorial/controlflow.html#for-statements
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
+[int]: https://docs.python.org/3/library/functions.html?#int
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[assignment-expression]: https://peps.python.org/pep-0572/
From ea5ec24997851c517069c14666d37309914be7e3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:06:21 -0600
Subject: [PATCH 315/932] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index 24d2897b320..58bd061b2bd 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -51,7 +51,7 @@ If the position is evenly divided by `2`, then it is even, and the character is
If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
and will subtract `9` from the doubled value if the doubled value is greater than `9`.
It does this using a [ternary operator][ternary-operator].
-Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`.
The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
The resulting value is added to the total variable.
From 934dde9a684ab51aa9bedcd5f601150dcef53893 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:07:54 -0600
Subject: [PATCH 316/932] Create snippet.txt
---
.../luhn/.approaches/replace-reverse-enumerate/snippet.txt | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt
new file mode 100644
index 00000000000..d02d2c5a2db
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt
@@ -0,0 +1,7 @@
+for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+return pos > 1 and not total % 10
From 6471b042a34fde874d708255e54143d11ffa5782 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:57:46 -0600
Subject: [PATCH 317/932] Update Benchmark.py
---
.../practice/luhn/.articles/performance/code/Benchmark.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
index 66afd6f49e2..21d4d2f26b2 100644
--- a/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -83,9 +83,7 @@ def luhny_tune(num):
@staticmethod
def luhny_bin(pos, sum, chars):
if not chars:
- if pos < 2:
- return False
- return sum % 10 == 0
+ return pos > 1 and sum % 10 == 0
else:
head, *tail = chars
if head.isdigit():
From 3668b80b19c5a015ec0393b99d6e1bc846856a8b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:05:53 -0600
Subject: [PATCH 318/932] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index 58bd061b2bd..291f5df13a5 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -42,7 +42,7 @@ In the code example the `luhny_bin` method initializes the `total` and `pos` var
It then calls [`reversed()`][reversed] on the input and iterates the characters from right to left with a [`for`][for] loop.
The [`isdigit()`][isdigit] method is used to see if the character is a digit.
-The [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+If so, the [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
It can be thought of as the expression _not_ having a remainder.
From 7322348adfae497d6036c9f7a400def098b255df Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:06:48 -0600
Subject: [PATCH 319/932] Update Benchmark.py
---
exercises/practice/luhn/.articles/performance/code/Benchmark.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
index 21d4d2f26b2..27372f52824 100644
--- a/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -87,7 +87,7 @@ def luhny_bin(pos, sum, chars):
else:
head, *tail = chars
if head.isdigit():
- if pos % 2 == 0:
+ if not pos % 2:
return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
else:
return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
From e8ab1cd663c4140bdf64306772b785b139a4e2eb Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:22:37 -0600
Subject: [PATCH 320/932] Update content.md
---
.../luhn/.approaches/recursion/content.md | 72 +++++++++++++++++--
1 file changed, 68 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/recursion/content.md b/exercises/practice/luhn/.approaches/recursion/content.md
index a4f682c4e71..0c146cbdcdd 100644
--- a/exercises/practice/luhn/.approaches/recursion/content.md
+++ b/exercises/practice/luhn/.approaches/recursion/content.md
@@ -15,13 +15,11 @@ class Luhn:
@staticmethod
def luhny_bin(pos, sum, chars):
if not chars:
- if pos < 2:
- return False
- return sum % 10 == 0
+ return pos > 1 and sum % 10 == 0
else:
head, *tail = chars
if head.isdigit():
- if pos % 2 == 0:
+ if not pos % 2:
return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
else:
return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
@@ -30,3 +28,69 @@ class Luhn:
return False
```
+
+The `Luhn` object is initialzed with the `card_num` value, which is the number to be validated with the Luhn algorithm.
+The result of the validation is returned from `Luhn`'s `valid()` method.
+In this approach, a member variable is set to the result of running the Luhn algorithm.
+That variable is returned from the `valid()` method.
+
+The methods that do the work have the [`@staticmethod`][static-method] decorator.
+This indicates that the method belongs to the class and is not recreated for every object instance.
+
+In the code example the `__init__` method uses [slicing][slicing] syntax (`[::-1]`) to reverse the characters in the input,
+which is then passed into the [list][list] constructor.
+The `luhny_bin()` method takes that list, along with two `0` values that represent the initialized values for the position and the sum.
+
+The `luhny_bin()` can call itself, which is a behavior called [recursion][recursion].
+Since `luhny_bin()` can call itself, the first thing it does is to check that it is done calling itself.
+
+```exercism/note
+This check is called the terminating condition.
+It's critical to have a terminating condition, since every call of a recursive function to itself places another
+[frame on the stack](https://realpython.com/lessons/stack-frames-and-stack-traces/#:~:text=A%20stack%20frame%20represents%20a,is%20removed%20from%20the%20stack.).
+If there is no terminating condition, then the recursive function will keep calling itself until the stack runs out of space
+and a stack overflow error will occur.
+```
+
+The `luhny_bin()` mtehod should terminate when there are no more characters to process.
+By using the [falsiness][falsiness] of an empty list, the [`not` operator][not-operator] can be used instead of comparing the `len()` of the list to `0`.
+When all of the characters have been iterated, the function returns if the position is greater than `1` and if the sum is evenly divisible by `10`.
+
+While there are still characters in the list to iterate, the list is [destructured][destructure] into `head, *tail`.
+The [`isdigit()`][isdigit] method is used to see if the head character is a digit.
+If so, the [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+It can be thought of as the expression _not_ having a remainder.
+
+If the position is evenly divided by `2`, then it is even, and the character is converted to an [`int()`][int] and will be added to the sum variable.
+
+If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
+and will subtract `9` from the doubled value if the doubled value is greater than `9`.
+It does this using a [ternary operator][ternary-operator].
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`.
+The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
+The resulting value will be added to the sum variable.
+
+Whether the digit is even or odd, the position is added to `1` when it and the sum are passed into the next call of the recursive method.
+Also passed in is the tail of the list, which is the list of all the remaining characters after the head character.
+Note that the sum and position variables are not being directly changed.
+(In other words, they are not being mutated.)
+The new sum and position values are calculated as the new arguments to the recursive function.
+
+If the head character is a space, the recursive function calls itself with the same position and sum values, and the tail.
+
+If the head character is neither a digit or a space, the function returns `False`.
+
+[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[list]: https://docs.python.org/3/library/functions.html?#func-list
+[recursion]: https://realpython.com/python-recursion/
+[stack-frame]: https://realpython.com/lessons/stack-frames-and-stack-traces/#:~:text=A%20stack%20frame%20represents%20a,is%20removed%20from%20the%20stack.
+[destructure]: https://riptutorial.com/python/example/14981/destructuring-assignment
+[isdigit]: https://docs.python.org/3/library/stdtypes.html?#str.isdigit
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
+[int]: https://docs.python.org/3/library/functions.html?#int
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[assignment-expression]: https://peps.python.org/pep-0572/
From fa37586695db8a02f73e3ad2bd4e2c9b72ccd773 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:23:02 -0600
Subject: [PATCH 321/932] Update snippet.txt
---
exercises/practice/luhn/.approaches/recursion/snippet.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/recursion/snippet.txt b/exercises/practice/luhn/.approaches/recursion/snippet.txt
index c59dd9a5fed..be95f0818e2 100644
--- a/exercises/practice/luhn/.approaches/recursion/snippet.txt
+++ b/exercises/practice/luhn/.approaches/recursion/snippet.txt
@@ -1,6 +1,6 @@
head, *tail = chars
if head.isdigit():
- if pos % 2 == 0:
+ if not pos % 2:
return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
else:
return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
From e29bbeb1cd178d3872c6bb8112ce60801d3dab51 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:24:15 -0600
Subject: [PATCH 322/932] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index 291f5df13a5..fcc7a58fe6b 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -38,7 +38,7 @@ That variable is returned from the `valid()` method.
The methods that do the work have the [`@staticmethod`][static-method] decorator.
This indicates that the method belongs to the class and is not recreated for every object instance.
-In the code example the `luhny_bin` method initializes the `total` and `pos` variables to `0`.
+In the code example the `luhny_bin()` method initializes the `total` and `pos` variables to `0`.
It then calls [`reversed()`][reversed] on the input and iterates the characters from right to left with a [`for`][for] loop.
The [`isdigit()`][isdigit] method is used to see if the character is a digit.
From 123c50163aa64c0ed466a6217a4700b915946e9e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:24:41 -0600
Subject: [PATCH 323/932] Update content.md
---
.../luhn/.approaches/replace-reverse-enumerate/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
index 8b2a5919d9d..812f51909c2 100644
--- a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
@@ -37,7 +37,7 @@ That variable is returned from the `valid()` method.
The methods that do the work have the [`@staticmethod`][static-method] decorator.
This indicates that the method belongs to the class and is not recreated for every object instance.
-In the code example the `luhny_bin` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
+In the code example the `luhny_bin()` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
The [`isdigit()`][isdigit] method is then used to see if all of the remaining characters are digits.
If not, the function returns `False`.
From 78f399be4df4c57636d0eba52ba3558b76cffbab Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:27:23 -0600
Subject: [PATCH 324/932] Update content.md
---
.../practice/luhn/.approaches/recursion/content.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/recursion/content.md b/exercises/practice/luhn/.approaches/recursion/content.md
index 0c146cbdcdd..b6f5f67f7fc 100644
--- a/exercises/practice/luhn/.approaches/recursion/content.md
+++ b/exercises/practice/luhn/.approaches/recursion/content.md
@@ -52,9 +52,9 @@ If there is no terminating condition, then the recursive function will keep call
and a stack overflow error will occur.
```
-The `luhny_bin()` mtehod should terminate when there are no more characters to process.
+The `luhny_bin()` method should terminate when there are no more characters to process.
By using the [falsiness][falsiness] of an empty list, the [`not` operator][not-operator] can be used instead of comparing the `len()` of the list to `0`.
-When all of the characters have been iterated, the function returns if the position is greater than `1` and if the sum is evenly divisible by `10`.
+When all of the characters have been iterated, the method returns if the position is greater than `1` and if the sum is evenly divisible by `10`.
While there are still characters in the list to iterate, the list is [destructured][destructure] into `head, *tail`.
The [`isdigit()`][isdigit] method is used to see if the head character is a digit.
@@ -75,11 +75,11 @@ Whether the digit is even or odd, the position is added to `1` when it and the s
Also passed in is the tail of the list, which is the list of all the remaining characters after the head character.
Note that the sum and position variables are not being directly changed.
(In other words, they are not being mutated.)
-The new sum and position values are calculated as the new arguments to the recursive function.
+The new sum and position values are calculated as the new arguments to the recursive method.
-If the head character is a space, the recursive function calls itself with the same position and sum values, and the tail.
+If the head character is a space, the recursive method calls itself with the same position and sum values, and the tail.
-If the head character is neither a digit or a space, the function returns `False`.
+If the head character is neither a digit or a space, the method returns `False`.
[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
[slicing]: https://www.learnbyexample.org/python-string-slicing/
From 0060e552cbd2531a559a1c28440e6a5b3ee3506c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:28:51 -0600
Subject: [PATCH 325/932] Update content.md
---
.../luhn/.approaches/replace-reverse-enumerate/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
index 812f51909c2..fd4e49b12c0 100644
--- a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
@@ -39,7 +39,7 @@ This indicates that the method belongs to the class and is not recreated for eve
In the code example the `luhny_bin()` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
The [`isdigit()`][isdigit] method is then used to see if all of the remaining characters are digits.
-If not, the function returns `False`.
+If not, the method returns `False`.
[Slicing][slicing] syntax (`[::-1`) is used to reverse the characters in the input, which is then passed into the [`enumerate()`][enumerate] method.
The [`for`][for] loop uses `enumerate()` to iterate the reversed characters in the input, returning the character and its position in the string.
@@ -57,7 +57,7 @@ Inside the ternary operator an [assignment expression][assignment-expression] as
The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
The resulting value is added to the total variable.
-After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+After the iteration of the characters is done, the method returns if the position is greater than `1` and if the total is evenly divisible by `10`.
[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
[replace]: https://docs.python.org/3/library/stdtypes.html?#str.replace
From 4bbdb16bb5f66e5ab1a4726e2beb543f791aa374 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:30:16 -0600
Subject: [PATCH 326/932] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index fcc7a58fe6b..abf5a591ca4 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -56,10 +56,10 @@ The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise i
The resulting value is added to the total variable.
Whether the digit is even or odd, the position is incremented by `1`.
-If the character is not a digit, the the function returns `False` if the character is not a space.
+If the character is not a digit, the method returns `False` if the character is not a space.
If the character is a space, the loop will go to the next iteration without incrementing the position variable.
-After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+After the iteration of the characters is done, the method returns if the position is greater than `1` and if the total is evenly divisible by `10`.
[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
[reversed]: https://docs.python.org/3/library/functions.html?#reversed
From b43688d439f89128bedb5bf15f3b1abb88328423 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 09:04:14 -0600
Subject: [PATCH 327/932] Create content.md
---
exercises/practice/luhn/.articles/content.md | 31 ++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/content.md
diff --git a/exercises/practice/luhn/.articles/content.md b/exercises/practice/luhn/.articles/content.md
new file mode 100644
index 00000000000..5069a2e53db
--- /dev/null
+++ b/exercises/practice/luhn/.articles/content.md
@@ -0,0 +1,31 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently validate a number with the Luhn algorithm.
+
+The [approaches page][approaches] lists two idiomatic approaches to this exercise:
+
+1. [Using `reversed()` with a `for` loop][approach-reversed-for]
+2. [Using `replace()`, reverse, `enumerate()`][approach-replace-reverse-enumerate]
+
+For our performance investigation, we'll also include a third approach that [uses recursion][approach-recursion].
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+reversed for: 1.0783263299963438e-05
+replace reverse enumerate: 9.933844099985436e-06
+recursion: 2.4321520800003783e-05
+```
+
+At an avergae time per call of `9934` nanoseconds, the `replace()`, reverse, `enumerate()` approach was the fastest.
+The `reversed()` with a `for` loop approach was a bit slower, at `10783` nanoseconds.
+The recursive approach was much slower, at about `24321` nanoseconds.
+
+[approaches]: https://exercism.org/tracks/python/exercises/luhn/approaches
+[approach-reversed-for]: https://exercism.org/tracks/python/exercises/luhn/approaches/reversed-for
+[approach-replace-reverse-enumerate]: https://exercism.org/tracks/python/exercises/luhn/approaches/replace-reverse-enumerate
+[approach-recursion]: https://exercism.org/tracks/python/exercises/luhn/approaches/recursion
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
From a5b79c0a016b3e433be82496d1d0d802495b6df0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 09:05:58 -0600
Subject: [PATCH 328/932] Update introduction.md
---
exercises/practice/luhn/.approaches/introduction.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/exercises/practice/luhn/.approaches/introduction.md b/exercises/practice/luhn/.approaches/introduction.md
index 904d1e20361..2eff18ae075 100644
--- a/exercises/practice/luhn/.approaches/introduction.md
+++ b/exercises/practice/luhn/.approaches/introduction.md
@@ -87,6 +87,13 @@ Besides the aforementioned, idiomatic approaches, you could also approach the ex
Another approach can use recursion to validate the number.
For more information, check the [recursion approach][approach-recursion].
+## Which approach to use?
+
+The `replace()`, reverse, `enumerate()` approach benchmarked the fastest.
+
+To compare performance of the approaches, check the [Performance article][article-performance].
+
[approach-reversed-for]: https://exercism.org/tracks/python/exercises/luhn/approaches/reversed-for
[approach-replace-reverse-enumerate]: https://exercism.org/tracks/python/exercises/luhn/approaches/replace-reverse-enumerate
[approach-recursion]: https://exercism.org/tracks/python/exercises/luhn/approaches/recursion
+[article-performance]: https://exercism.org/tracks/python/exercises/luhn/articles/performance
From b0a957b6cf7d41ff39b1b9f97158e3046b2ac6a7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 06:05:49 -0600
Subject: [PATCH 329/932] Rename exercises/practice/luhn/.articles/content.md
to exercises/practice/luhn/.articles/performance/content.md
---
exercises/practice/luhn/.articles/{ => performance}/content.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename exercises/practice/luhn/.articles/{ => performance}/content.md (100%)
diff --git a/exercises/practice/luhn/.articles/content.md b/exercises/practice/luhn/.articles/performance/content.md
similarity index 100%
rename from exercises/practice/luhn/.articles/content.md
rename to exercises/practice/luhn/.articles/performance/content.md
From de3c41e0deae222c595406ddfa03906a9d200a38 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 2 Dec 2022 14:42:14 -0800
Subject: [PATCH 330/932] Start of linked-list update.
---
.../practice/linked-list/.meta/tests.toml | 67 +++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 exercises/practice/linked-list/.meta/tests.toml
diff --git a/exercises/practice/linked-list/.meta/tests.toml b/exercises/practice/linked-list/.meta/tests.toml
new file mode 100644
index 00000000000..96906d2cc78
--- /dev/null
+++ b/exercises/practice/linked-list/.meta/tests.toml
@@ -0,0 +1,67 @@
+# 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.
+
+[7f7e3987-b954-41b8-8084-99beca08752c]
+description = "pop gets element from the list"
+
+[c3f67e5d-cfa2-4c3e-a18f-7ce999c3c885]
+description = "push/pop respectively add/remove at the end of the list"
+
+[00ea24ce-4f5c-4432-abb4-cc6e85462657]
+description = "shift gets an element from the list"
+
+[37962ee0-3324-4a29-b588-5a4c861e6564]
+description = "shift gets first element from the list"
+
+[30a3586b-e9dc-43fb-9a73-2770cec2c718]
+description = "unshift adds element at start of the list"
+
+[042f71e4-a8a7-4cf0-8953-7e4f3a21c42d]
+description = "pop, push, shift, and unshift can be used in any order"
+
+[88f65c0c-4532-4093-8295-2384fb2f37df]
+description = "count an empty list"
+
+[fc055689-5cbe-4cd9-b994-02e2abbb40a5]
+description = "count a list with items"
+
+[8272cef5-130d-40ea-b7f6-5ffd0790d650]
+description = "count is correct after mutation"
+
+[229b8f7a-bd8a-4798-b64f-0dc0bb356d95]
+description = "popping to empty doesn't break the list"
+
+[4e1948b4-514e-424b-a3cf-a1ebbfa2d1ad]
+description = "shifting to empty doesn't break the list"
+
+[e8f7c600-d597-4f79-949d-8ad8bae895a6]
+description = "deletes the only element"
+
+[fd65e422-51f3-45c0-9fd0-c33da638f89b]
+description = "deletes the element with the specified value from the list"
+
+[59db191a-b17f-4ab7-9c5c-60711ec1d013]
+description = "deletes the element with the specified value from the list, re-assigns tail"
+
+[58242222-5d39-415b-951d-8128247f8993]
+description = "deletes the element with the specified value from the list, re-assigns head"
+
+[ee3729ee-3405-4bd2-9bad-de0d4aa5d647]
+description = "deletes the first of two elements"
+
+[47e3b3b4-b82c-4c23-8c1a-ceb9b17cb9fb]
+description = "deletes the second of two elements"
+
+[7b420958-f285-4922-b8f9-10d9dcab5179]
+description = "delete does not modify the list if the element is not found"
+
+[7e04828f-6082-44e3-a059-201c63252a76]
+description = "deletes only the first occurrence"
From 228f421a13f953f98ec4bd0db2b7aa2e995f6cee Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 18:50:33 +0100
Subject: [PATCH 331/932] Added Jinja template
---
.../practice/linked-list/.meta/config.json | 3 +-
.../practice/linked-list/.meta/example.py | 20 +-
.../practice/linked-list/.meta/template.j2 | 31 +++
.../practice/linked-list/linked_list_test.py | 196 +++++++++++++-----
4 files changed, 189 insertions(+), 61 deletions(-)
create mode 100644 exercises/practice/linked-list/.meta/template.j2
diff --git a/exercises/practice/linked-list/.meta/config.json b/exercises/practice/linked-list/.meta/config.json
index c2d286f6678..894e0a5d3a5 100644
--- a/exercises/practice/linked-list/.meta/config.json
+++ b/exercises/practice/linked-list/.meta/config.json
@@ -9,7 +9,8 @@
"Mofeywalker",
"N-Parsons",
"pheanex",
- "tqa236"
+ "tqa236",
+ "meatball"
],
"files": {
"solution": [
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 280509f244b..997831bd57f 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -54,8 +54,18 @@ def unshift(self, value):
def __len__(self):
return self.length
- def __iter__(self):
- current_node = self.head
- while current_node:
- yield current_node.value
- current_node = current_node.succeeding
+ def delete(self, delete):
+ node = self.head
+ while node:
+ if node.value == delete:
+ if node.prev:
+ node.prev.succeeding = node.succeeding
+ else:
+ self.head = node.succeeding
+ if node.succeeding:
+ node.succeeding.prev = node.prev
+ else:
+ self.tail = node.prev
+ self.length -= 1
+ break
+ node = node.succeeding
\ No newline at end of file
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
new file mode 100644
index 00000000000..c765d68a32e
--- /dev/null
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -0,0 +1,31 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{% macro test_case(case) -%}
+ {%- set input = case["input"]["operations"] -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ lst = LinkedList()
+ {% for x in input -%}
+ {%- if "push" == x["operation"] -%}
+ lst.push({{ x["value"] }})
+ {%- elif "pop" == x["operation"] and x["expected"] -%}
+ self.assertEqual(lst.pop(), {{ x["expected"] }})
+ {%- elif "pop" == x["operation"] -%}
+ lst.pop()
+ {%- elif "shift" == x["operation"] and x["expected"] -%}
+ self.assertEqual(lst.shift(), {{ x["expected"] }})
+ {%- elif "shift" == x["operation"] -%}
+ lst.shift()
+ {%- elif "unshift" == x["operation"] -%}
+ lst.unshift({{ x["value"] }})
+ {%- elif "delete" == x["operation"] -%}
+ lst.delete({{ x["value"] }})
+ {%- elif "count" == x["operation"] -%}
+ self.assertEqual(len(lst), {{ x["expected"] }})
+ {%- endif %}
+ {% endfor %}
+{%- endmacro %}
+{{ macros.header(["LinkedList"]) }}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 74a53147812..822948b823a 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -1,82 +1,168 @@
import unittest
-from linked_list import LinkedList
+from linked_list import (
+ LinkedList,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
class LinkedListTest(unittest.TestCase):
- def test_push_pop(self):
+ def test_pop_gets_element_from_the_list(self):
+ lst = LinkedList()
+ lst.push(7)
+ self.assertEqual(lst.pop(), 7)
+
+ def test_push_pop_respectively_add_remove_at_the_end_of_the_list(self):
+ lst = LinkedList()
+ lst.push(11)
+ lst.push(13)
+ self.assertEqual(lst.pop(), 13)
+ self.assertEqual(lst.pop(), 11)
+
+ def test_shift_gets_an_element_from_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- self.assertEqual(lst.pop(), 20)
- self.assertEqual(lst.pop(), 10)
+ lst.push(17)
+ self.assertEqual(lst.shift(), 17)
- def test_push_shift(self):
+ def test_shift_gets_first_element_from_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- self.assertEqual(lst.shift(), 10)
- self.assertEqual(lst.shift(), 20)
+ lst.push(23)
+ lst.push(5)
+ self.assertEqual(lst.shift(), 23)
+ self.assertEqual(lst.shift(), 5)
- def test_unshift_shift(self):
+ def test_unshift_adds_element_at_start_of_the_list(self):
lst = LinkedList()
- lst.unshift(10)
- lst.unshift(20)
- self.assertEqual(lst.shift(), 20)
- self.assertEqual(lst.shift(), 10)
+ lst.unshift(23)
+ lst.unshift(5)
+ self.assertEqual(lst.shift(), 5)
+ self.assertEqual(lst.shift(), 23)
- def test_unshift_pop(self):
+ def test_pop_push_shift_and_unshift_can_be_used_in_any_order(self):
lst = LinkedList()
- lst.unshift(10)
- lst.unshift(20)
- self.assertEqual(lst.pop(), 10)
- self.assertEqual(lst.pop(), 20)
+ lst.push(1)
+ lst.push(2)
+ self.assertEqual(lst.pop(), 2)
+ lst.push(3)
+ self.assertEqual(lst.shift(), 1)
+ lst.unshift(4)
+ lst.push(5)
+ self.assertEqual(lst.shift(), 4)
+ self.assertEqual(lst.pop(), 5)
+ self.assertEqual(lst.shift(), 3)
- def test_all(self):
+ def test_count_an_empty_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- self.assertEqual(lst.pop(), 20)
- lst.push(30)
- self.assertEqual(lst.shift(), 10)
- lst.unshift(40)
- lst.push(50)
- self.assertEqual(lst.shift(), 40)
- self.assertEqual(lst.pop(), 50)
- self.assertEqual(lst.shift(), 30)
+ self.assertEqual(len(lst), 0)
- @unittest.skip("extra-credit")
- def test_length(self):
+ def test_count_a_list_with_items(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
+ lst.push(37)
+ lst.push(1)
+ self.assertEqual(len(lst), 2)
+
+ def test_count_is_correct_after_mutation(self):
+ lst = LinkedList()
+ lst.push(31)
+ self.assertEqual(len(lst), 1)
+ lst.unshift(43)
self.assertEqual(len(lst), 2)
lst.shift()
self.assertEqual(len(lst), 1)
lst.pop()
self.assertEqual(len(lst), 0)
- @unittest.skip("extra-credit")
- def test_iterator(self):
+ def test_popping_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- iterator = iter(lst)
- self.assertEqual(next(iterator), 10)
- self.assertEqual(next(iterator), 20)
+ lst.push(41)
+ lst.push(59)
+ lst.pop()
+ lst.pop()
+ lst.push(47)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.pop(), 47)
- @unittest.skip("extra-credit")
- def test_iterator_independence(self):
+ def test_shifting_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- iterator_a = iter(lst)
- iterator_b = iter(lst)
- self.assertEqual(next(iterator_a), 10)
- self.assertEqual(next(iterator_a), 20)
- self.assertEqual(next(iterator_b), 10)
- self.assertEqual(next(iterator_b), 20)
+ lst.push(41)
+ lst.push(59)
+ lst.shift()
+ lst.shift()
+ lst.push(47)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.shift(), 47)
+ def test_deletes_the_only_element(self):
+ lst = LinkedList()
+ lst.push(61)
+ lst.delete(61)
+ self.assertEqual(len(lst), 0)
-if __name__ == '__main__':
- unittest.main()
+ def test_deletes_the_element_with_the_specified_value_from_the_list(self):
+ lst = LinkedList()
+ lst.push(71)
+ lst.push(83)
+ lst.push(79)
+ lst.delete(83)
+ self.assertEqual(len(lst), 2)
+ self.assertEqual(lst.pop(), 79)
+ self.assertEqual(lst.shift(), 71)
+
+ def test_deletes_the_element_with_the_specified_value_from_the_list_re_assigns_tail(
+ self,
+ ):
+ lst = LinkedList()
+ lst.push(71)
+ lst.push(83)
+ lst.push(79)
+ lst.delete(83)
+ self.assertEqual(len(lst), 2)
+ self.assertEqual(lst.pop(), 79)
+ self.assertEqual(lst.pop(), 71)
+
+ def test_deletes_the_element_with_the_specified_value_from_the_list_re_assigns_head(
+ self,
+ ):
+ lst = LinkedList()
+ lst.push(71)
+ lst.push(83)
+ lst.push(79)
+ lst.delete(83)
+ self.assertEqual(len(lst), 2)
+ self.assertEqual(lst.shift(), 71)
+ self.assertEqual(lst.shift(), 79)
+
+ def test_deletes_the_first_of_two_elements(self):
+ lst = LinkedList()
+ lst.push(97)
+ lst.push(101)
+ lst.delete(97)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.pop(), 101)
+
+ def test_deletes_the_second_of_two_elements(self):
+ lst = LinkedList()
+ lst.push(97)
+ lst.push(101)
+ lst.delete(101)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.pop(), 97)
+
+ def test_delete_does_not_modify_the_list_if_the_element_is_not_found(self):
+ lst = LinkedList()
+ lst.push(89)
+ lst.delete(103)
+ self.assertEqual(len(lst), 1)
+
+ def test_deletes_only_the_first_occurrence(self):
+ lst = LinkedList()
+ lst.push(73)
+ lst.push(9)
+ lst.push(9)
+ lst.push(107)
+ lst.delete(9)
+ self.assertEqual(len(lst), 3)
+ self.assertEqual(lst.pop(), 107)
+ self.assertEqual(lst.pop(), 9)
+ self.assertEqual(lst.pop(), 73)
From 2aa781a4974b9ed7d769a89725dba970758fa868 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 18:51:25 +0100
Subject: [PATCH 332/932] Fix
---
exercises/practice/linked-list/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/linked-list/.meta/config.json b/exercises/practice/linked-list/.meta/config.json
index 894e0a5d3a5..3c1731c0be4 100644
--- a/exercises/practice/linked-list/.meta/config.json
+++ b/exercises/practice/linked-list/.meta/config.json
@@ -10,7 +10,7 @@
"N-Parsons",
"pheanex",
"tqa236",
- "meatball"
+ "meatball133"
],
"files": {
"solution": [
From 2ff1eebd13a741dd6098c9e893b234d0d8d0d2f4 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 3 Dec 2022 10:51:44 -0800
Subject: [PATCH 333/932] Create instructions.apped.md
Rough outline of instructions.append
---
.../linked-list/.docs/instructions.apped.md | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 exercises/practice/linked-list/.docs/instructions.apped.md
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
new file mode 100644
index 00000000000..7b649b857b3
--- /dev/null
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -0,0 +1,42 @@
+# Instructions append
+
+## How this Exercise is Structured in Python
+
+While linked lists can be implemented in a variety of ways with a variety of underlying data structures, we ask here that you implement your linked list in an OOP fashion.
+
+In the stub file, you will see a `Node` class and a `LinkedList` class.
+Your `Node` class should keep track of its value, as well as which other nodes preceed or follow.
+Your `push`, `pop`, `shift`, `unshift`, and the special method for `len` should be implemented in the `LinkedList` class.
+
+
+In addition to the methods outlined above, we also ask that you implement `delete`.
+`delete` will take one argument, which is the vaule to be removed from the linked list.
+If the value appears more than once, the **first** occurance should be removed.
+
+
+
+## Exception messages
+
+Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.
+
+This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+
+To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
+
+```python
+# When the value passed to `delete()` is not found.
+
+
+# When pop() is called and there are no nodes left in the linked list
+
+
+```
+
+## Special Methods in Python
+
+The tests for this exercise will also be calling `len()` on your `LinkedList`.
+In order for `len()` to work, you will need to create a `__len__` special method.
+For details on implementing special or "dunder" methods in Python, see [Python Docs: Basic Object Customization][basic customization] and [Python Docs: object.__len__(self)][__len__].
+
+
+
From 9c90b83909e5af366d43d4044e76b0518ed81483 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 20:20:36 +0100
Subject: [PATCH 334/932] progress
---
.../linked-list/.meta/additional_tests.json | 14 ++++++++++++++
exercises/practice/linked-list/.meta/template.j2 | 15 +++++++++++++++
.../practice/linked-list/linked_list_test.py | 6 ++++++
3 files changed, 35 insertions(+)
create mode 100644 exercises/practice/linked-list/.meta/additional_tests.json
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
new file mode 100644
index 00000000000..a18e57356dc
--- /dev/null
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -0,0 +1,14 @@
+{
+ "cases": [
+ {
+ "description": "Hi",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "pop"
+ }
+ ]
+ }, "expected": {"error": "List is empty"}
+ }
+]}
\ No newline at end of file
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index c765d68a32e..31eed581c3b 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -1,9 +1,11 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro test_case(case) -%}
{%- set input = case["input"]["operations"] -%}
+ {%- set last = "" -%}
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
{% for x in input -%}
+ {%- set last = x["operation"] -%}
{%- if "push" == x["operation"] -%}
lst.push({{ x["value"] }})
{%- elif "pop" == x["operation"] and x["expected"] -%}
@@ -22,6 +24,11 @@
self.assertEqual(len(lst), {{ x["expected"] }})
{%- endif %}
{% endfor %}
+
+ {% if case["expected"] and last == "pop" %}
+ hi
+ {% endif %}
+
{%- endmacro %}
{{ macros.header(["LinkedList"]) }}
@@ -29,3 +36,11 @@ 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/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 822948b823a..e6bc7270d7c 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -166,3 +166,9 @@ def test_deletes_only_the_first_occurrence(self):
self.assertEqual(lst.pop(), 107)
self.assertEqual(lst.pop(), 9)
self.assertEqual(lst.pop(), 73)
+
+ # Additional tests for this track
+
+ def test_hi(self):
+ lst = LinkedList()
+ lst.pop()
From fc4c03b45d797c19fb8b192560da1ba0199ab92b Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 20:29:50 +0100
Subject: [PATCH 335/932] more stuff
---
exercises/practice/linked-list/.meta/template.j2 | 7 +++----
exercises/practice/linked-list/linked_list_test.py | 2 ++
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 31eed581c3b..4a9ec4b9324 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -1,11 +1,10 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro test_case(case) -%}
{%- set input = case["input"]["operations"] -%}
- {%- set last = "" -%}
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
{% for x in input -%}
- {%- set last = x["operation"] -%}
+ {% set last = x["operation"] %}
{%- if "push" == x["operation"] -%}
lst.push({{ x["value"] }})
{%- elif "pop" == x["operation"] and x["expected"] -%}
@@ -25,8 +24,8 @@
{%- endif %}
{% endfor %}
- {% if case["expected"] and last == "pop" %}
- hi
+ {% if case["expected"] and input[-1]["operation"] == "pop" %}
+
{% endif %}
{%- endmacro %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index e6bc7270d7c..1c58d4995c1 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -172,3 +172,5 @@ def test_deletes_only_the_first_occurrence(self):
def test_hi(self):
lst = LinkedList()
lst.pop()
+
+ hi
From b85fc64b53c4f988efac1110e5925c7bf60838f9 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 21:16:23 +0100
Subject: [PATCH 336/932] progress
---
.../linked-list/.meta/additional_tests.json | 11 ++--
.../practice/linked-list/.meta/template.j2 | 50 +++++++++++--------
.../practice/linked-list/linked_list_test.py | 8 +--
3 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index a18e57356dc..5a316654734 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -5,9 +5,14 @@
"property": "list",
"input": {
"operations": [
- {
- "operation": "pop"
- }
+ {
+ "operation": "push",
+ "value": 1
+ },
+ {
+ "operation": "pop"
+ }
+
]
}, "expected": {"error": "List is empty"}
}
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 4a9ec4b9324..9fe03b776d3 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -1,31 +1,39 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro test_case(case) -%}
- {%- set input = case["input"]["operations"] -%}
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
- {% for x in input -%}
- {% set last = x["operation"] %}
- {%- if "push" == x["operation"] -%}
- lst.push({{ x["value"] }})
- {%- elif "pop" == x["operation"] and x["expected"] -%}
- self.assertEqual(lst.pop(), {{ x["expected"] }})
- {%- elif "pop" == x["operation"] -%}
+ {%- set inputs = case["input"]["operations"] -%}
+ {%- if case["expected"] and inputs|length > 1 -%}
+ {%- set last = inputs[-1]["operation"] -%}
+ {% set inputs = inputs[:-1] %}
+ {%- elif case["expected"] -%}
+ {% set last = inputs[-1]["operation"] %}
+ {% set inputs = [{"operation": "None"}] %}
+ {%- endif %}
+ {% for input in inputs -%}
+ {%- if "push" == input["operation"] -%}
+ lst.push({{ input["value"] }})
+ {%- elif "pop" == input["operation"] and input["expected"] -%}
+ self.assertEqual(lst.pop(), {{ input["expected"] }})
+ {%- elif "pop" == input["operation"] -%}
lst.pop()
- {%- elif "shift" == x["operation"] and x["expected"] -%}
- self.assertEqual(lst.shift(), {{ x["expected"] }})
- {%- elif "shift" == x["operation"] -%}
+ {%- elif "shift" == input["operation"] and input["expected"] -%}
+ self.assertEqual(lst.shift(), {{ input["expected"] }})
+ {%- elif "shift" == input["operation"] -%}
lst.shift()
- {%- elif "unshift" == x["operation"] -%}
- lst.unshift({{ x["value"] }})
- {%- elif "delete" == x["operation"] -%}
- lst.delete({{ x["value"] }})
- {%- elif "count" == x["operation"] -%}
- self.assertEqual(len(lst), {{ x["expected"] }})
+ {%- elif "unshift" == input["operation"] -%}
+ lst.unshift({{ input["value"] }})
+ {%- elif "delete" == input["operation"] -%}
+ lst.delete({{ input["value"] }})
+ {%- elif "count" == input["operation"] -%}
+ self.assertEqual(len(lst), {{ input["expected"] }})
{%- endif %}
- {% endfor %}
-
- {% if case["expected"] and input[-1]["operation"] == "pop" %}
-
+ {% endfor -%}
+ {%- if case["expected"] and last -%}
+ with self.assertRaises(ValueError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
{% endif %}
{%- endmacro %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 1c58d4995c1..c1eeaca35a8 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -171,6 +171,8 @@ def test_deletes_only_the_first_occurrence(self):
def test_hi(self):
lst = LinkedList()
- lst.pop()
-
- hi
+ lst.push(1)
+ with self.assertRaises(ValueError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
From 2d413b1c6d99151acfeae420ed9f7bd7254c3e09 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Tue, 6 Dec 2022 08:57:15 +0100
Subject: [PATCH 337/932] Updated the test file
---
.../linked-list/.meta/additional_tests.json | 106 +++++++++++++++++-
1 file changed, 105 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 5a316654734..ce01e33972b 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -1,7 +1,19 @@
{
"cases": [
{
- "description": "Hi",
+ "description": "Using pop raises an error if the list is empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "pop"
+ }
+
+ ]
+ }, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Can return with pop and then raise an error if empty",
"property": "list",
"input": {
"operations": [
@@ -9,11 +21,103 @@
"operation": "push",
"value": 1
},
+ {
+ "operation": "unshift",
+ "value": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 1
+ },
{
"operation": "pop"
}
]
}, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Using shift raises an error if the list is empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "shift"
+ }
+
+ ]
+ }, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "push",
+ "value": 1
+ },
+ {
+ "operation": "unshift",
+ "value": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 5
+ },
+ {
+ "operation": "shift",
+ "expected": 1
+ },
+ {
+ "operation": "shift"
+ }
+
+ ]
+ }, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Using delete raises an error if the list is empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "delete",
+ "value": 0
+ }
+
+ ]
+ }, "expected": {"error": "Value not found"}
+ },
+ {
+ "description": "Using delete raises an error if the it is not found",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "push",
+ "value": 5
+ },
+ {
+ "operation": "push",
+ "value": 7
+ },
+ {
+ "operation": "pop",
+ "expected": 7
+ },
+ {
+ "operation": "delete",
+ "value": 7
+ }
+
+ ]
+ }, "expected": {"error": "Value not found"}
}
+
+
]}
\ No newline at end of file
From 7b46c1c302648784c9417fe09b274d88e107326f Mon Sep 17 00:00:00 2001
From: Meatball
Date: Tue, 6 Dec 2022 08:58:35 +0100
Subject: [PATCH 338/932] fix
---
exercises/practice/linked-list/.meta/additional_tests.json | 2 --
1 file changed, 2 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index ce01e33972b..25d8ef07762 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -118,6 +118,4 @@
]
}, "expected": {"error": "Value not found"}
}
-
-
]}
\ No newline at end of file
From 95b83515d79ff11f1f62432b4eca81beb8e77659 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 6 Dec 2022 00:01:05 -0800
Subject: [PATCH 339/932] Update template.j2
Reworked template.
---
.../practice/linked-list/.meta/template.j2 | 31 +++++++++----------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 9fe03b776d3..bbbada690df 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -11,22 +11,21 @@
{% set inputs = [{"operation": "None"}] %}
{%- endif %}
{% for input in inputs -%}
- {%- if "push" == input["operation"] -%}
- lst.push({{ input["value"] }})
- {%- elif "pop" == input["operation"] and input["expected"] -%}
- self.assertEqual(lst.pop(), {{ input["expected"] }})
- {%- elif "pop" == input["operation"] -%}
- lst.pop()
- {%- elif "shift" == input["operation"] and input["expected"] -%}
- self.assertEqual(lst.shift(), {{ input["expected"] }})
- {%- elif "shift" == input["operation"] -%}
- lst.shift()
- {%- elif "unshift" == input["operation"] -%}
- lst.unshift({{ input["value"] }})
- {%- elif "delete" == input["operation"] -%}
- lst.delete({{ input["value"] }})
- {%- elif "count" == input["operation"] -%}
- self.assertEqual(len(lst), {{ input["expected"] }})
+ {%- set operation = input["operation"] -%}
+ {%- set value = input["value"] -%}
+ {%- set expected = input["expected"] -%}
+ {%- if operation and value -%}
+ lst.{{ operation }}({{ value }})
+ {%- elif operation and expected -%}
+ {%- if operation == "count" -%}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else -%}
+ self.assertEqual(lst.{{ operation }}(), {{ expected }})
+ {%- endif -%}
+ {%- elif operation == "count" -%}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else -%}
+ lst.{{ operation }}()
{%- endif %}
{% endfor -%}
{%- if case["expected"] and last -%}
From d9255250a986b3b59dcf2e6755ad9f66048ef37b Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 6 Dec 2022 01:29:45 -0800
Subject: [PATCH 340/932] JinJa Redo Number Two
---
.../practice/linked-list/.meta/template.j2 | 42 +++++++++----------
.../practice/linked-list/linked_list_test.py | 10 -----
2 files changed, 21 insertions(+), 31 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index bbbada690df..08294ae78f8 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -5,34 +5,34 @@
{%- set inputs = case["input"]["operations"] -%}
{%- if case["expected"] and inputs|length > 1 -%}
{%- set last = inputs[-1]["operation"] -%}
- {% set inputs = inputs[:-1] %}
+ {%- set inputs = inputs[:-1] -%}
{%- elif case["expected"] -%}
- {% set last = inputs[-1]["operation"] %}
- {% set inputs = [{"operation": "None"}] %}
- {%- endif %}
+ {%- set last = inputs[-1]["operation"] -%}
+ {%- set inputs = [{"operation": "None"}] -%}
+ {%- endif -%}
{% for input in inputs -%}
{%- set operation = input["operation"] -%}
{%- set value = input["value"] -%}
- {%- set expected = input["expected"] -%}
- {%- if operation and value -%}
- lst.{{ operation }}({{ value }})
- {%- elif operation and expected -%}
- {%- if operation == "count" -%}
- self.assertEqual(len(lst), {{ expected }})
- {%- else -%}
- self.assertEqual(lst.{{ operation }}(), {{ expected }})
- {%- endif -%}
- {%- elif operation == "count" -%}
- self.assertEqual(len(lst), {{ expected }})
- {%- else -%}
- lst.{{ operation }}()
+ {%- set expected = input["expected"] %}
+ {%- if operation and value %}
+ lst.{{ operation }}({{ value }})
+ {%- elif operation and expected %}
+ {%- if operation == "count" %}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else %}
+ self.assertEqual(lst.{{ operation }}(), {{ expected }})
+ {%- endif %}
+ {%- elif operation == "count" %}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else %}
+ lst.{{ operation }}()
{%- endif %}
- {% endfor -%}
- {%- if case["expected"] and last -%}
+ {%- endfor %}
+ {% if case["expected"] and last %}
with self.assertRaises(ValueError) as err:
lst.pop()
- self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
{% endif %}
{%- endmacro %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index c1eeaca35a8..822948b823a 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -166,13 +166,3 @@ def test_deletes_only_the_first_occurrence(self):
self.assertEqual(lst.pop(), 107)
self.assertEqual(lst.pop(), 9)
self.assertEqual(lst.pop(), 73)
-
- # Additional tests for this track
-
- def test_hi(self):
- lst = LinkedList()
- lst.push(1)
- with self.assertRaises(ValueError) as err:
- lst.pop()
- self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "List is empty")
From cedb63b9d224d552b0d10ffb6e1bada489e26f7f Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 7 Dec 2022 00:53:00 -0800
Subject: [PATCH 341/932] Updated JinJa Template
Updated template
Re-generated test cases
Re-formatted json file
---
.../linked-list/.meta/additional_tests.json | 63 +++++++++++--------
.../practice/linked-list/.meta/template.j2 | 38 +++++++----
.../practice/linked-list/linked_list_test.py | 60 ++++++++++++++++--
3 files changed, 116 insertions(+), 45 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 25d8ef07762..2b28b6aa0e5 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -1,6 +1,7 @@
{
"cases": [
{
+ "uuid": "c472bec8-45b5-477f-96c1-3bed58ca0b1a",
"description": "Using pop raises an error if the list is empty",
"property": "list",
"input": {
@@ -13,34 +14,36 @@
}, "expected": {"error": "List is empty"}
},
{
- "description": "Can return with pop and then raise an error if empty",
+ "uuid": "394a61d3-7a69-4153-9eb7-f8d8e23cf6f0",
+ "description" : "Can return with pop and then raise an error if empty",
"property": "list",
"input": {
- "operations": [
- {
- "operation": "push",
- "value": 1
- },
- {
- "operation": "unshift",
- "value": 5
- },
- {
- "operation": "pop",
- "expected": 5
- },
- {
- "operation": "pop",
- "expected": 1
- },
- {
- "operation": "pop"
- }
-
- ]
- }, "expected": {"error": "List is empty"}
+ "operations": [
+ {
+ "operation": "push",
+ "value": 1
+ },
+ {
+ "operation": "unshift",
+ "value": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 1
+ },
+ {
+ "operation": "pop"
+ }
+ ]
+ },
+ "expected": {"error": "List is empty"}
},
{
+ "uuid": "6d1a1817-0d4c-4953-abfd-c5cd5639871f",
"description": "Using shift raises an error if the list is empty",
"property": "list",
"input": {
@@ -50,9 +53,11 @@
}
]
- }, "expected": {"error": "List is empty"}
+ },
+ "expected": {"error": "List is empty"}
},
{
+ "uuid": "cae5d135-f948-44ce-8940-c9b898b95756",
"description": "Can return with shift and then raise an error if empty",
"property": "list",
"input": {
@@ -78,9 +83,11 @@
}
]
- }, "expected": {"error": "List is empty"}
+ },
+ "expected": {"error": "List is empty"}
},
{
+ "uuid": "1fa81a88-9af7-49a1-a4b1-b1aa641bfcbb",
"description": "Using delete raises an error if the list is empty",
"property": "list",
"input": {
@@ -91,9 +98,11 @@
}
]
- }, "expected": {"error": "Value not found"}
+ },
+ "expected": {"error": "Value not found"}
},
{
+ "uuid": "f7f9d2dc-be1b-4f42-be2e-c29980ce0823",
"description": "Using delete raises an error if the it is not found",
"property": "list",
"input": {
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 08294ae78f8..0659aff2d8f 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -4,10 +4,15 @@
lst = LinkedList()
{%- set inputs = case["input"]["operations"] -%}
{%- if case["expected"] and inputs|length > 1 -%}
- {%- set last = inputs[-1]["operation"] -%}
+ {%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
- {%- elif case["expected"] -%}
- {%- set last = inputs[-1]["operation"] -%}
+ {% if case["expected"]["error"] %}
+ {%- set error_case = true %}
+ {%- set error_msg = case["expected"]["error"] %}
+ {%- set error_operation = inputs[-1]["operation"] %}
+ {%- endif %}
+ {%- elif case["expected"] and not error_case -%}
+ {%- set final = inputs[-1]["operation"] -%}
{%- set inputs = [{"operation": "None"}] -%}
{%- endif -%}
{% for input in inputs -%}
@@ -24,18 +29,28 @@
{%- endif %}
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
- {%- else %}
- lst.{{ operation }}()
+ {%- elif case["expected"]["error"] %}
+ with self.assertRaises(IndexError) as err:
+ lst.{{ case["input"]["operations"][-1]["operation"] }}()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
{%- endif %}
{%- endfor %}
- {% if case["expected"] and last %}
- with self.assertRaises(ValueError) as err:
- lst.pop()
+ {%- if error_case %}
+ {%- if final == "pop" or final == "shift" %}
+ with self.assertRaises(IndexError) as err:
+ lst.{{ error_operation }}()
self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
- {% endif %}
-
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
+ {% elif final == "delete" %}
+ with self.assertRaises(ValueError) as err:
+ lst.{{ error_operation }}()
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
+ {%- endif %}
+ {%- endif %}
{%- endmacro %}
+
{{ macros.header(["LinkedList"]) }}
class {{ exercise | camel_case }}Test(unittest.TestCase):
@@ -45,7 +60,6 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% if additional_cases | length -%}
# Additional tests for this track
-
{% for case in additional_cases -%}
{{ test_case(case) }}
{% endfor %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 822948b823a..97cab20bab3 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -68,17 +68,13 @@ def test_count_is_correct_after_mutation(self):
self.assertEqual(len(lst), 1)
lst.unshift(43)
self.assertEqual(len(lst), 2)
- lst.shift()
self.assertEqual(len(lst), 1)
- lst.pop()
self.assertEqual(len(lst), 0)
def test_popping_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
- lst.pop()
- lst.pop()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.pop(), 47)
@@ -87,8 +83,6 @@ def test_shifting_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
- lst.shift()
- lst.shift()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.shift(), 47)
@@ -166,3 +160,57 @@ def test_deletes_only_the_first_occurrence(self):
self.assertEqual(lst.pop(), 107)
self.assertEqual(lst.pop(), 9)
self.assertEqual(lst.pop(), 73)
+
+ # Additional tests for this track
+ def test_using_pop_raises_an_error_if_the_list_is_empty(self):
+ lst = LinkedList()
+ with self.assertRaises(IndexError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_can_return_with_pop_and_then_raise_an_error_if_empty(self):
+ lst = LinkedList()
+ lst.push(1)
+ lst.unshift(5)
+ self.assertEqual(lst.pop(), 5)
+ self.assertEqual(lst.pop(), 1)
+ with self.assertRaises(IndexError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_using_shift_raises_an_error_if_the_list_is_empty(self):
+ lst = LinkedList()
+ with self.assertRaises(IndexError) as err:
+ lst.shift()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ lst = LinkedList()
+ lst.push(1)
+ lst.unshift(5)
+ self.assertEqual(lst.pop(), 5)
+ self.assertEqual(lst.shift(), 1)
+ with self.assertRaises(IndexError) as err:
+ lst.shift()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_using_delete_raises_an_error_if_the_list_is_empty(self):
+ lst = LinkedList()
+ with self.assertRaises(IndexError) as err:
+ lst.delete()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "Value not found")
+
+ def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
+ lst = LinkedList()
+ lst.push(5)
+ lst.push(7)
+ self.assertEqual(lst.pop(), 7)
+ with self.assertRaises(ValueError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "Value not found")
From ce16d819b9a92c4db9d9401255fda4b2ea90e361 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 7 Dec 2022 01:48:44 -0800
Subject: [PATCH 342/932] Template Correction
---
exercises/practice/linked-list/.meta/template.j2 | 7 +++++++
exercises/practice/linked-list/linked_list_test.py | 4 ++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 0659aff2d8f..4760945d654 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -30,11 +30,18 @@
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
{%- elif case["expected"]["error"] %}
+ {%- if final == "pop" or final == "shift" %}
with self.assertRaises(IndexError) as err:
lst.{{ case["input"]["operations"][-1]["operation"] }}()
self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
+ {% elif final == "delete" %}
+ with self.assertRaises(ValueError) as err:
+ lst.{{ case["input"]["operations"][-1]["operation"] }}()
+ self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
{%- endif %}
+ {%- endif %}
{%- endfor %}
{%- if error_case %}
{%- if final == "pop" or final == "shift" %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 97cab20bab3..0080921a2f8 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -200,9 +200,9 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
def test_using_delete_raises_an_error_if_the_list_is_empty(self):
lst = LinkedList()
- with self.assertRaises(IndexError) as err:
+ with self.assertRaises(ValueError) as err:
lst.delete()
- self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
From cfdf420f94393bb0685cd5ac75fcc64244161039 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Wed, 7 Dec 2022 16:35:09 +0100
Subject: [PATCH 343/932] Updated example and tests
---
.../practice/linked-list/.meta/additional_tests.json | 8 ++++----
exercises/practice/linked-list/.meta/example.py | 10 +++++++++-
exercises/practice/linked-list/.meta/tests.toml | 1 +
3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 2b28b6aa0e5..5be9653f3f6 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -29,11 +29,11 @@
},
{
"operation": "pop",
- "expected": 5
+ "expected": 1
},
{
"operation": "pop",
- "expected": 1
+ "expected": 5
},
{
"operation": "pop"
@@ -72,11 +72,11 @@
},
{
"operation": "pop",
- "expected": 5
+ "expected": 1
},
{
"operation": "shift",
- "expected": 1
+ "expected": 5
},
{
"operation": "shift"
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 997831bd57f..536bd2491c1 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -23,6 +23,8 @@ def push(self, value):
def pop(self):
node = self.tail
+ if self.length == 0:
+ raise IndexError("List is empty")
if node is None or node.prev is None:
self.head = self.tail = None
else:
@@ -32,6 +34,8 @@ def pop(self):
return node.value
def shift(self):
+ if self.length == 0:
+ raise IndexError("List is empty")
node = self.head
if node is None or node.succeeding is None:
self.head = self.tail = None
@@ -55,6 +59,7 @@ def __len__(self):
return self.length
def delete(self, delete):
+ found = False
node = self.head
while node:
if node.value == delete:
@@ -66,6 +71,9 @@ def delete(self, delete):
node.succeeding.prev = node.prev
else:
self.tail = node.prev
+ found = True
self.length -= 1
break
- node = node.succeeding
\ No newline at end of file
+ node = node.succeeding
+ if not found:
+ raise ValueError("Value not found")
\ No newline at end of file
diff --git a/exercises/practice/linked-list/.meta/tests.toml b/exercises/practice/linked-list/.meta/tests.toml
index 96906d2cc78..c5862ff4c4f 100644
--- a/exercises/practice/linked-list/.meta/tests.toml
+++ b/exercises/practice/linked-list/.meta/tests.toml
@@ -62,6 +62,7 @@ description = "deletes the second of two elements"
[7b420958-f285-4922-b8f9-10d9dcab5179]
description = "delete does not modify the list if the element is not found"
+include = false
[7e04828f-6082-44e3-a059-201c63252a76]
description = "deletes only the first occurrence"
From 75ba3565c0009bbdecba8a51b47149ef2da5f0c1 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 01:32:12 -0800
Subject: [PATCH 344/932] More LL Edits
Final (I hope) touchups on template
Adjustments to example solution
Re-Generated test files.
---
.../practice/linked-list/.meta/example.py | 65 ++++++++++++-------
.../practice/linked-list/.meta/template.j2 | 30 +++++----
.../practice/linked-list/linked_list_test.py | 22 +++----
3 files changed, 70 insertions(+), 47 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 536bd2491c1..1496ef25961 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -11,6 +11,41 @@ def __init__(self):
self.tail = None
self.length = 0
+ def __len__(self):
+ return self.length
+
+ def __iter__(self):
+ current_node = self.head
+ while current_node:
+ yield (current_node.value, current_node.succeeding, current_node.prev)
+ current_node = current_node.succeeding
+
+ def delete(self, to_delete):
+ if self.length == 0:
+ raise ValueError("Value not found")
+ found = False
+ node = self.head
+
+ for value, succeeding, prev in self:
+ if value == to_delete:
+ if prev:
+ node.prev.succeeding = succeeding
+ else:
+ self.head = succeeding
+ if succeeding:
+ node.succeeding.prev = prev
+ else:
+ self.tail = prev
+
+ found = True
+ self.length -= 1
+ break
+
+ node = node.succeeding
+
+ if not found:
+ raise ValueError("Value not found")
+
def push(self, value):
new_node = Node(value)
if not self.head:
@@ -19,10 +54,12 @@ def push(self, value):
new_node.prev = self.tail
self.tail.succeeding = new_node
self.tail = new_node
+
self.length += 1
def pop(self):
node = self.tail
+
if self.length == 0:
raise IndexError("List is empty")
if node is None or node.prev is None:
@@ -30,7 +67,10 @@ def pop(self):
else:
self.tail = self.tail.prev
self.tail.succeeding = None
+
self.length -= 1
+ print(self.length)
+
return node.value
def shift(self):
@@ -43,6 +83,7 @@ def shift(self):
self.head = self.head.succeeding
self.head.prev = None
self.length -= 1
+
return node.value
def unshift(self, value):
@@ -53,27 +94,5 @@ def unshift(self, value):
new_node.succeeding = self.head
self.head.prev = new_node
self.head = new_node
- self.length += 1
- def __len__(self):
- return self.length
-
- def delete(self, delete):
- found = False
- node = self.head
- while node:
- if node.value == delete:
- if node.prev:
- node.prev.succeeding = node.succeeding
- else:
- self.head = node.succeeding
- if node.succeeding:
- node.succeeding.prev = node.prev
- else:
- self.tail = node.prev
- found = True
- self.length -= 1
- break
- node = node.succeeding
- if not found:
- raise ValueError("Value not found")
\ No newline at end of file
+ self.length += 1
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 4760945d654..e9c6a309542 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -6,19 +6,21 @@
{%- if case["expected"] and inputs|length > 1 -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
- {% if case["expected"]["error"] %}
- {%- set error_case = true %}
- {%- set error_msg = case["expected"]["error"] %}
- {%- set error_operation = inputs[-1]["operation"] %}
- {%- endif %}
+ {%- if case["expected"]["error"] -%}
+ {%- set error_case = true -%}
+ {%- set error_msg = case["expected"]["error"] -%}
+ {%- set error_operation = inputs[-1]["operation"] -%}
+ {% endif %}
{%- elif case["expected"] and not error_case -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = [{"operation": "None"}] -%}
{%- endif -%}
- {% for input in inputs -%}
+ {%- for input in inputs -%}
{%- set operation = input["operation"] -%}
{%- set value = input["value"] -%}
{%- set expected = input["expected"] %}
+ {%- set error_msg = case["expected"]["error"] -%}
+ {%- set error_operation = inputs[-1]["operation"] -%}
{%- if operation and value %}
lst.{{ operation }}({{ value }})
{%- elif operation and expected %}
@@ -27,6 +29,8 @@
{%- else %}
self.assertEqual(lst.{{ operation }}(), {{ expected }})
{%- endif %}
+ {%- elif operation == "pop" or operation == "shift" %}
+ lst.{{ operation }}({{ value }})
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
{%- elif case["expected"]["error"] %}
@@ -34,15 +38,15 @@
with self.assertRaises(IndexError) as err:
lst.{{ case["input"]["operations"][-1]["operation"] }}()
self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
- {% elif final == "delete" %}
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
+ {%- elif final == "delete" %}
with self.assertRaises(ValueError) as err:
- lst.{{ case["input"]["operations"][-1]["operation"] }}()
+ lst.{{ case["input"]["operations"][-1]["operation"] }}({{ value if value else 0 }})
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
- {%- endif %}
- {%- endfor %}
+ {%- endif %}
+ {%- endfor %}
{%- if error_case %}
{%- if final == "pop" or final == "shift" %}
with self.assertRaises(IndexError) as err:
@@ -51,7 +55,7 @@
self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{% elif final == "delete" %}
with self.assertRaises(ValueError) as err:
- lst.{{ error_operation }}()
+ lst.{{ final }}({{ value if value else 0 }})
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 0080921a2f8..37d48b3a8da 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -68,13 +68,17 @@ def test_count_is_correct_after_mutation(self):
self.assertEqual(len(lst), 1)
lst.unshift(43)
self.assertEqual(len(lst), 2)
+ lst.shift()
self.assertEqual(len(lst), 1)
+ lst.pop()
self.assertEqual(len(lst), 0)
def test_popping_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
+ lst.pop()
+ lst.pop()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.pop(), 47)
@@ -83,6 +87,8 @@ def test_shifting_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
+ lst.shift()
+ lst.shift()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.shift(), 47)
@@ -143,12 +149,6 @@ def test_deletes_the_second_of_two_elements(self):
self.assertEqual(len(lst), 1)
self.assertEqual(lst.pop(), 97)
- def test_delete_does_not_modify_the_list_if_the_element_is_not_found(self):
- lst = LinkedList()
- lst.push(89)
- lst.delete(103)
- self.assertEqual(len(lst), 1)
-
def test_deletes_only_the_first_occurrence(self):
lst = LinkedList()
lst.push(73)
@@ -173,8 +173,8 @@ def test_can_return_with_pop_and_then_raise_an_error_if_empty(self):
lst = LinkedList()
lst.push(1)
lst.unshift(5)
- self.assertEqual(lst.pop(), 5)
self.assertEqual(lst.pop(), 1)
+ self.assertEqual(lst.pop(), 5)
with self.assertRaises(IndexError) as err:
lst.pop()
self.assertEqual(type(err.exception), IndexError)
@@ -191,8 +191,8 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
lst = LinkedList()
lst.push(1)
lst.unshift(5)
- self.assertEqual(lst.pop(), 5)
- self.assertEqual(lst.shift(), 1)
+ self.assertEqual(lst.pop(), 1)
+ self.assertEqual(lst.shift(), 5)
with self.assertRaises(IndexError) as err:
lst.shift()
self.assertEqual(type(err.exception), IndexError)
@@ -201,7 +201,7 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
def test_using_delete_raises_an_error_if_the_list_is_empty(self):
lst = LinkedList()
with self.assertRaises(ValueError) as err:
- lst.delete()
+ lst.delete(0)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
@@ -211,6 +211,6 @@ def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
lst.push(7)
self.assertEqual(lst.pop(), 7)
with self.assertRaises(ValueError) as err:
- lst.pop()
+ lst.delete(0)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
From 92bf6e3f1be3735188631e446818ee3e41c6c085 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Thu, 8 Dec 2022 21:56:07 +0100
Subject: [PATCH 345/932] Made the error handeling shorter
---
.../practice/linked-list/.meta/template.j2 | 24 +++++--------------
1 file changed, 6 insertions(+), 18 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index e9c6a309542..471ca2f098a 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -3,15 +3,15 @@
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
{%- set inputs = case["input"]["operations"] -%}
+ {%- if case["expected"] -%}
+ {%- set error_case = true -%}
+ {%- set error_msg = case["expected"]["error"] -%}
+ {%- set error_operation = inputs[-1]["operation"] -%}
+ {% endif %}
{%- if case["expected"] and inputs|length > 1 -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
- {%- if case["expected"]["error"] -%}
- {%- set error_case = true -%}
- {%- set error_msg = case["expected"]["error"] -%}
- {%- set error_operation = inputs[-1]["operation"] -%}
- {% endif %}
- {%- elif case["expected"] and not error_case -%}
+ {%- elif case["expected"] -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = [{"operation": "None"}] -%}
{%- endif -%}
@@ -33,18 +33,6 @@
lst.{{ operation }}({{ value }})
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
- {%- elif case["expected"]["error"] %}
- {%- if final == "pop" or final == "shift" %}
- with self.assertRaises(IndexError) as err:
- lst.{{ case["input"]["operations"][-1]["operation"] }}()
- self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
- {%- elif final == "delete" %}
- with self.assertRaises(ValueError) as err:
- lst.{{ case["input"]["operations"][-1]["operation"] }}({{ value if value else 0 }})
- self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
- {%- endif %}
{%- endif %}
{%- endfor %}
{%- if error_case %}
From 83530a738d508492e63df2dc8b67ff028bf23c75 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 19:52:59 -0800
Subject: [PATCH 346/932] Final Edits(I hope)
Added raising error message examples to instruction append.
Added hint about implementing __iter__ to instruction append.
Added ref links for documentation.
Lightly edited example file.
---
.../linked-list/.docs/instructions.apped.md | 25 ++++++++++++++++---
.../practice/linked-list/.meta/example.py | 4 ---
2 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index 7b649b857b3..bf0892ff00f 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -4,9 +4,10 @@
While linked lists can be implemented in a variety of ways with a variety of underlying data structures, we ask here that you implement your linked list in an OOP fashion.
-In the stub file, you will see a `Node` class and a `LinkedList` class.
+In the stub file, you will see the start of a `Node` class, as well as a `LinkedList` class.
Your `Node` class should keep track of its value, as well as which other nodes preceed or follow.
Your `push`, `pop`, `shift`, `unshift`, and the special method for `len` should be implemented in the `LinkedList` class.
+You might also find it useful to implement a special `iter` method for iteration.
In addition to the methods outlined above, we also ask that you implement `delete`.
@@ -17,26 +18,42 @@ If the value appears more than once, the **first** occurance should be removed.
## Exception messages
-Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.
+Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
-This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
```python
# When the value passed to `delete()` is not found.
+if not found:
+ raise ValueError("Value not found")
+```
-# When pop() is called and there are no nodes left in the linked list
+To raise an `IndexError` with a message, write the message as an argument to the `exception` type:
+```python
+# When pop() is called and there are no nodes left in the linked list
+if self.length == 0:
+ raise IndexError("List is empty")
```
+
## Special Methods in Python
The tests for this exercise will also be calling `len()` on your `LinkedList`.
In order for `len()` to work, you will need to create a `__len__` special method.
For details on implementing special or "dunder" methods in Python, see [Python Docs: Basic Object Customization][basic customization] and [Python Docs: object.__len__(self)][__len__].
+We also recommend creating a special [`__iter__`][__iter__] method to help with iterating over your linked list.
+
+[__iter__]: https://docs.python.org/3/reference/datamodel.html#object.__iter__
+[__len__]: https://docs.python.org/3/reference/datamodel.html#object.__len__
+[basic customization]: https://docs.python.org/3/reference/datamodel.html#basic-customization
+[error types]: https://docs.python.org/3/library/exceptions.html#base-classes
+[raise]: https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement
+[raising]: https://docs.python.org/3/tutorial/errors.html#raising-exceptions
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 1496ef25961..acc13d4f9e4 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -40,9 +40,7 @@ def delete(self, to_delete):
found = True
self.length -= 1
break
-
node = node.succeeding
-
if not found:
raise ValueError("Value not found")
@@ -67,9 +65,7 @@ def pop(self):
else:
self.tail = self.tail.prev
self.tail.succeeding = None
-
self.length -= 1
- print(self.length)
return node.value
From 00c560771a1c9f99271d68653cb04f0146b527f5 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 20:17:27 -0800
Subject: [PATCH 347/932] More Edits on Instruction Append
---
exercises/practice/linked-list/.docs/instructions.apped.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index bf0892ff00f..08f494b6551 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -9,10 +9,11 @@ Your `Node` class should keep track of its value, as well as which other nodes p
Your `push`, `pop`, `shift`, `unshift`, and the special method for `len` should be implemented in the `LinkedList` class.
You might also find it useful to implement a special `iter` method for iteration.
+Unlike the core exercise, we will be testing error conditions by calling `pop` and `shift` on empty `LinkedLists`, so you will need to `raise` errors appropriately.
-In addition to the methods outlined above, we also ask that you implement `delete`.
+Finally, we would like you to implement `delete` in addition to the methods outlined above.
`delete` will take one argument, which is the vaule to be removed from the linked list.
-If the value appears more than once, the **first** occurance should be removed.
+If the value appears more than once, only the **first** occurance should be removed.
From acee9f230d79984be9bba74c989f4cc061d4c001 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 20:31:00 -0800
Subject: [PATCH 348/932] One. Last. Change.
---
exercises/practice/linked-list/.meta/template.j2 | 2 --
1 file changed, 2 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 471ca2f098a..103be69f0dd 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -19,8 +19,6 @@
{%- set operation = input["operation"] -%}
{%- set value = input["value"] -%}
{%- set expected = input["expected"] %}
- {%- set error_msg = case["expected"]["error"] -%}
- {%- set error_operation = inputs[-1]["operation"] -%}
{%- if operation and value %}
lst.{{ operation }}({{ value }})
{%- elif operation and expected %}
From 09bd542547a5a9d125ae22dcc8b88516443e4277 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 9 Dec 2022 09:21:41 -0800
Subject: [PATCH 349/932] Apply suggestions from code review
Co-authored-by: Katrina Owen
---
exercises/practice/linked-list/.docs/instructions.apped.md | 6 +++---
exercises/practice/linked-list/.meta/additional_tests.json | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index 08f494b6551..975494d0252 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -12,8 +12,8 @@ You might also find it useful to implement a special `iter` method for iteration
Unlike the core exercise, we will be testing error conditions by calling `pop` and `shift` on empty `LinkedLists`, so you will need to `raise` errors appropriately.
Finally, we would like you to implement `delete` in addition to the methods outlined above.
-`delete` will take one argument, which is the vaule to be removed from the linked list.
-If the value appears more than once, only the **first** occurance should be removed.
+`delete` will take one argument, which is the value to be removed from the linked list.
+If the value appears more than once, only the **first** occurrence should be removed.
@@ -21,7 +21,7 @@ If the value appears more than once, only the **first** occurance should be remo
Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
-This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delete()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 5be9653f3f6..a791426a613 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -103,7 +103,7 @@
},
{
"uuid": "f7f9d2dc-be1b-4f42-be2e-c29980ce0823",
- "description": "Using delete raises an error if the it is not found",
+ "description": "Using delete raises an error if the value is not found",
"property": "list",
"input": {
"operations": [
From a7b7f8ec92ed16eaf282a2c4201f10717fe4702f Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 9 Dec 2022 09:38:47 -0800
Subject: [PATCH 350/932] Updates/Edits
Regenerated test file (don't know why it needed it, but ok.
Re tested solution.
Edited instruction append to be one sentence per line.
---
.../practice/linked-list/.docs/instructions.apped.md | 8 ++++++--
exercises/practice/linked-list/linked_list_test.py | 2 +-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index 975494d0252..00032862c2a 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -19,9 +19,13 @@ If the value appears more than once, only the **first** occurrence should be rem
## Exception messages
-Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
+Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is.
+This makes your code more readable and helps significantly with debugging.
+For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
-This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delete()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delete()`-ed is not found in the linked list.
+Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`.
+The tests will only pass if you both `raise` these `exceptions` and include messages with them.
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 37d48b3a8da..3af5bc221e4 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -205,7 +205,7 @@ def test_using_delete_raises_an_error_if_the_list_is_empty(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
- def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
+ def test_using_delete_raises_an_error_if_the_value_is_not_found(self):
lst = LinkedList()
lst.push(5)
lst.push(7)
From ed310e19496aa3d91114324a30b7060626e75ef6 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Fri, 9 Dec 2022 09:59:51 +0100
Subject: [PATCH 351/932] Add custom token to community contributions workflow
The pause-community-contributions workflow makes a call to the
GitHub API to check if the person contributing is a member of
the organization.
However, this call currently fails if the contributor has set their
membership to 'private'.
This is because the default token provided for GitHub Actions only
has permissions for the repository, not for the organization. With
this token, we're not allowed to see private memberships.
We've created a custom, org-wide secret containing a personal token
that has permissions to read organization membership.
Unfortunately the secret cannot be accessed directly by the shared
workflow, it has to be passed in.
We updated the shared workflow to use the token, if it is provided,
and this PR updates the workflow in this repo to pass the secret.
Until this is merged, contributions from people with private membership
in the Exercism organization will be automatically closed.
Note that this PR also removes the workflow_dispatch which fails if
you try to use it.
---
.github/workflows/pause-community-contributions.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/pause-community-contributions.yml b/.github/workflows/pause-community-contributions.yml
index e91c86cf68e..d764bfe8b63 100644
--- a/.github/workflows/pause-community-contributions.yml
+++ b/.github/workflows/pause-community-contributions.yml
@@ -10,7 +10,6 @@ on:
paths-ignore:
- 'exercises/*/*/.approaches/**'
- 'exercises/*/*/.articles/**'
- workflow_dispatch:
permissions:
issues: write
@@ -22,3 +21,5 @@ jobs:
uses: exercism/github-actions/.github/workflows/community-contributions.yml@main
with:
forum_category: python
+ secrets:
+ github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }}
From ab87e6f7e153261b478a72a98d440346e9909fb9 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 10 Dec 2022 23:24:12 +0100
Subject: [PATCH 352/932] Added Jinja template
---
exercises/practice/react/.meta/config.json | 4 +-
exercises/practice/react/.meta/template.j2 | 62 ++++
exercises/practice/react/react.py | 1 +
exercises/practice/react/react_test.py | 314 +++++++++++++--------
4 files changed, 257 insertions(+), 124 deletions(-)
create mode 100644 exercises/practice/react/.meta/template.j2
diff --git a/exercises/practice/react/.meta/config.json b/exercises/practice/react/.meta/config.json
index 71e3b58ad1e..6ecc14244d6 100644
--- a/exercises/practice/react/.meta/config.json
+++ b/exercises/practice/react/.meta/config.json
@@ -6,7 +6,9 @@
"Dog",
"N-Parsons",
"Nishant23",
- "tqa236"
+ "tqa236",
+ "meatball133",
+ "Bethanyg"
],
"files": {
"solution": [
diff --git a/exercises/practice/react/.meta/template.j2 b/exercises/practice/react/.meta/template.j2
new file mode 100644
index 00000000000..f9fb637c433
--- /dev/null
+++ b/exercises/practice/react/.meta/template.j2
@@ -0,0 +1,62 @@
+{%- import "generator_macros.j2" as macros with context -%}
+from functools import partial
+{% macro test_case(case) -%}
+ {%- set input = case["input"] -%}
+ {%- set callback = [] -%}
+ {%- for operation in input["operations"] -%}
+ {%- if operation["type"] == "add_callback" -%}
+ {% set callback = callback.append(operation["name"]) %}
+ {%- endif -%}
+ {%- endfor -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ {%- for cell in input["cells"] -%}
+ {%- if cell["type"] == "input" %}
+ {{ cell["name"] }} = InputCell({{ cell["initial_value"] }})
+ {%- elif cell["type"] == "compute" -%}
+ {%- if "if" in cell["compute_function"] %}
+ output = ComputeCell([input], lambda inputs: 111 if inputs[0] < 3 else 222)
+ {%- else %}
+ {{ cell["name"] }} = ComputeCell([{%- for input in cell["inputs"] -%}{{ input }},{%- endfor -%}], lambda inputs: {{ cell["compute_function"] }})
+ {%- endif -%}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- if callback -%}
+ {%- for _ in callback %}
+ cb{{ loop.index0 +1 }}_observer = []
+ {%- endfor -%}
+ {%- endif %}
+ {% for sub_callback in callback -%}
+ {{ sub_callback }} = self.callback_factory(cb{{ loop.index0 + 1 }}_observer)
+ {% endfor -%}
+ {%- for operation in input["operations"] -%}
+ {%- if operation["type"] == "add_callback" or operation["type"] == "remove_callback" -%}
+ {{ operation["cell"] }}.{{ operation["type"] }}({{ operation["name"] }})
+ {%- elif operation["type"] == "expect_cell_value" -%}
+ self.assertEqual({{ operation["cell"] }}.value, {{ operation["value"] }})
+ {%- elif operation["type"] == "set_value" -%}
+ {{ operation["cell"] }}.value = {{ operation["value"] }}
+ {%- if operation["expect_callbacks_not_to_be_called"] %}
+ {%- if callback | length == 3 %}
+ self.assertEqual(len(cb{{ operation["expect_callbacks_not_to_be_called"][0][-1] }}_observer), 1)
+ {%- else %}
+ self.assertEqual(cb{{ operation["expect_callbacks_not_to_be_called"][0][-1] }}_observer, [])
+ {%- endif %}
+ {%- endif -%}
+ {%- for exp_callback in operation["expect_callbacks"] %}
+ self.assertEqual(cb{{ exp_callback[-1] }}_observer[-1], {{ operation["expect_callbacks"][exp_callback] }})
+ {%- endfor -%}
+ {%- endif %}
+ {% endfor -%}
+{%- endmacro %}
+{{ macros.header(["InputCell", "ComputeCell"])}}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
+
+ # Utility functions.
+ def callback_factory(self, observer):
+ def callback(observer, value):
+ observer.append(value)
+ return partial(callback, observer)
diff --git a/exercises/practice/react/react.py b/exercises/practice/react/react.py
index 03ff02e7891..ab6be311d97 100644
--- a/exercises/practice/react/react.py
+++ b/exercises/practice/react/react.py
@@ -12,3 +12,4 @@ def add_callback(self, callback):
def remove_callback(self, callback):
pass
+
\ No newline at end of file
diff --git a/exercises/practice/react/react_test.py b/exercises/practice/react/react_test.py
index 7c1870cfda9..2ba86a80ffa 100644
--- a/exercises/practice/react/react_test.py
+++ b/exercises/practice/react/react_test.py
@@ -1,202 +1,270 @@
-import unittest
from functools import partial
-from react import InputCell, ComputeCell
+import unittest
+
+from react import (
+ InputCell,
+ ComputeCell,
+)
+# Tests adapted from `problem-specifications//canonical-data.json`
-# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0
class ReactTest(unittest.TestCase):
-
def test_input_cells_have_a_value(self):
- input_ = InputCell(10)
- self.assertEqual(input_.value, 10)
+ input = InputCell(10)
+ self.assertEqual(input.value, 10)
- def test_can_set_input_cell_value(self):
- input_ = InputCell(4)
- input_.value = 20
- self.assertEqual(input_.value, 20)
+ def test_an_input_cell_s_value_can_be_set(self):
+ input = InputCell(4)
+ input.value = 20
+ self.assertEqual(input.value, 20)
def test_compute_cells_calculate_initial_value(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
self.assertEqual(output.value, 2)
- def test_compute_cells_take_inputs_in_right_order(self):
+ def test_compute_cells_take_inputs_in_the_right_order(self):
one = InputCell(1)
two = InputCell(2)
output = ComputeCell(
- [one, two],
- lambda inputs: inputs[0] + inputs[1]*10
+ [
+ one,
+ two,
+ ],
+ lambda inputs: inputs[0] + inputs[1] * 10,
)
self.assertEqual(output.value, 21)
def test_compute_cells_update_value_when_dependencies_are_changed(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- input_.value = 3
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ input.value = 3
self.assertEqual(output.value, 4)
def test_compute_cells_can_depend_on_other_compute_cells(self):
- input_ = InputCell(1)
- times_two = ComputeCell([input_], lambda inputs: inputs[0] * 2)
- times_thirty = ComputeCell([input_], lambda inputs: inputs[0] * 30)
+ input = InputCell(1)
+ times_two = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] * 2,
+ )
+ times_thirty = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] * 30,
+ )
output = ComputeCell(
- [times_two, times_thirty],
- lambda inputs: inputs[0] + inputs[1]
+ [
+ times_two,
+ times_thirty,
+ ],
+ lambda inputs: inputs[0] + inputs[1],
)
-
self.assertEqual(output.value, 32)
- input_.value = 3
+ input.value = 3
self.assertEqual(output.value, 96)
def test_compute_cells_fire_callbacks(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
- output.add_callback(callback1)
- input_.value = 3
- self.assertEqual(observer[-1], 4)
-
- def test_callbacks_only_fire_on_change(self):
- input_ = InputCell(1)
+ input = InputCell(1)
output = ComputeCell(
- [input_],
- lambda inputs: 111 if inputs[0] < 3 else 222
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
)
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
+ output.add_callback(callback1)
+ input.value = 3
+ self.assertEqual(cb1_observer[-1], 4)
- observer = []
- callback1 = self.callback_factory(observer)
-
+ def test_callback_cells_only_fire_on_change(self):
+ input = InputCell(1)
+ output = ComputeCell([input], lambda inputs: 111 if inputs[0] < 3 else 222)
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
output.add_callback(callback1)
- input_.value = 2
- self.assertEqual(observer, [])
- input_.value = 4
- self.assertEqual(observer[-1], 222)
+ input.value = 2
+ self.assertEqual(cb1_observer, [])
+ input.value = 4
+ self.assertEqual(cb1_observer[-1], 222)
def test_callbacks_do_not_report_already_reported_values(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
output.add_callback(callback1)
- input_.value = 2
- self.assertEqual(observer[-1], 3)
- input_.value = 3
- self.assertEqual(observer[-1], 4)
+ input.value = 2
+ self.assertEqual(cb1_observer[-1], 3)
+ input.value = 3
+ self.assertEqual(cb1_observer[-1], 4)
def test_callbacks_can_fire_from_multiple_cells(self):
- input_ = InputCell(1)
- plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
- minus_one = ComputeCell([input_], lambda inputs: inputs[0] - 1)
-
- cb1_observer, cb2_observer = [], []
+ input = InputCell(1)
+ plus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ minus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
+ cb1_observer = []
+ cb2_observer = []
callback1 = self.callback_factory(cb1_observer)
callback2 = self.callback_factory(cb2_observer)
-
plus_one.add_callback(callback1)
minus_one.add_callback(callback2)
- input_.value = 10
-
+ input.value = 10
self.assertEqual(cb1_observer[-1], 11)
self.assertEqual(cb2_observer[-1], 9)
def test_callbacks_can_be_added_and_removed(self):
- input_ = InputCell(11)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- cb1_observer, cb2_observer, cb3_observer = [], [], []
+ input = InputCell(11)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ cb1_observer = []
+ cb2_observer = []
+ cb3_observer = []
callback1 = self.callback_factory(cb1_observer)
callback2 = self.callback_factory(cb2_observer)
callback3 = self.callback_factory(cb3_observer)
-
output.add_callback(callback1)
output.add_callback(callback2)
- input_.value = 31
+ input.value = 31
self.assertEqual(cb1_observer[-1], 32)
self.assertEqual(cb2_observer[-1], 32)
-
output.remove_callback(callback1)
output.add_callback(callback3)
- input_.value = 41
+ input.value = 41
+ self.assertEqual(len(cb1_observer), 1)
self.assertEqual(cb2_observer[-1], 42)
self.assertEqual(cb3_observer[-1], 42)
- # Expect callback1 not to be called.
- self.assertEqual(len(cb1_observer), 1)
-
- def test_removing_a_callback_multiple_times(self):
- """Guard against incorrect implementations which store their
- callbacks in an array."""
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- cb1_observer, cb2_observer = [], []
+ def test_removing_a_callback_multiple_times_doesn_t_interfere_with_other_callbacks(
+ self,
+ ):
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ cb1_observer = []
+ cb2_observer = []
callback1 = self.callback_factory(cb1_observer)
callback2 = self.callback_factory(cb2_observer)
-
output.add_callback(callback1)
output.add_callback(callback2)
output.remove_callback(callback1)
output.remove_callback(callback1)
output.remove_callback(callback1)
- input_.value = 2
-
+ input.value = 2
self.assertEqual(cb1_observer, [])
self.assertEqual(cb2_observer[-1], 3)
- def test_callbacks_should_only_be_called_once(self):
- """Guard against incorrect implementations which call a callback
- function multiple times when multiple dependencies change."""
- input_ = InputCell(1)
- plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
- minus_one1 = ComputeCell([input_], lambda inputs: inputs[0] - 1)
- minus_one2 = ComputeCell([minus_one1], lambda inputs: inputs[0] - 1)
+ def test_callbacks_should_only_be_called_once_even_if_multiple_dependencies_change(
+ self,
+ ):
+ input = InputCell(1)
+ plus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ minus_one1 = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
+ minus_one2 = ComputeCell(
+ [
+ minus_one1,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
output = ComputeCell(
- [plus_one, minus_one2],
- lambda inputs: inputs[0] * inputs[1]
+ [
+ plus_one,
+ minus_one2,
+ ],
+ lambda inputs: inputs[0] * inputs[1],
)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
output.add_callback(callback1)
- input_.value = 4
- self.assertEqual(observer[-1], 10)
-
- def test_callbacks_not_called_so_long_as_output_not_changed(self):
- """Guard against incorrect implementations which call callbacks
- if dependencies change but output value doesn't change."""
- input_ = InputCell(1)
- plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
- minus_one = ComputeCell([input_], lambda inputs: inputs[0] - 1)
+ input.value = 4
+ self.assertEqual(cb1_observer[-1], 10)
+
+ def test_callbacks_should_not_be_called_if_dependencies_change_but_output_value_doesn_t_change(
+ self,
+ ):
+ input = InputCell(1)
+ plus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ minus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
always_two = ComputeCell(
- [plus_one, minus_one],
- lambda inputs: inputs[0] - inputs[1]
+ [
+ plus_one,
+ minus_one,
+ ],
+ lambda inputs: inputs[0] - inputs[1],
)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
always_two.add_callback(callback1)
- input_.value = 2
- input_.value = 3
- input_.value = 4
- input_.value = 5
- self.assertEqual(observer, [])
+ input.value = 2
+ self.assertEqual(cb1_observer, [])
+ input.value = 3
+ self.assertEqual(cb1_observer, [])
+ input.value = 4
+ self.assertEqual(cb1_observer, [])
+ input.value = 5
+ self.assertEqual(cb1_observer, [])
# Utility functions.
def callback_factory(self, observer):
def callback(observer, value):
observer.append(value)
- return partial(callback, observer)
-
-if __name__ == '__main__':
- unittest.main()
+ return partial(callback, observer)
From c5da4971d562bf401502292be7bcd981d8abfc31 Mon Sep 17 00:00:00 2001
From: PaulT89 <97411029+PaulT89@users.noreply.github.com>
Date: Sun, 11 Dec 2022 09:58:59 +1100
Subject: [PATCH 353/932] [Pascals Triangle] Remastered for recursion (#3150)
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- status = wip for now
- moved entire block up to be in line with other exercises of similar difficulty.
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- moved entire block up to be in line with other exercises of similar difficulty.
* Updated Exercise
-Fixed config prerequisite bug (exercise is now accessible).
- Moved hardcoded additional tests to additional_tests.json file.
- Added test for recursion.
* Fixed bug
Moved import sys
* Fixed template indentation
* Manual recursion limit
* Revert "Manual recursion limit"
This reverts commit f11762983ff1a548cb8930fa83c07d637b722a05.
* Minor formatting changes
* Canonical sync fix
Apparently the RecursionError message changed in Python 3.9. Hopefully this will work across all tested versions now.
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- moved entire block up to be in line with other exercises of similar difficulty.
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- status = wip for now
- moved entire block up to be in line with other exercises of similar difficulty.
* Updated Exercise
-Fixed config prerequisite bug (exercise is now accessible).
- Moved hardcoded additional tests to additional_tests.json file.
- Added test for recursion.
* Fixed bug
Moved import sys
* Fixed template indentation
* Manual recursion limit
* Revert "Manual recursion limit"
This reverts commit f11762983ff1a548cb8930fa83c07d637b722a05.
* Minor formatting changes
* Canonical sync fix
Apparently the RecursionError message changed in Python 3.9. Hopefully this will work across all tested versions now.
* Updated instructions
-Updated Instructions.append.md
-Added hints.md
* Template changes and small reflink edits
Shortened the JinJa Template a bit.
Edited instruction append to use reflinks and a recursion widget, and be one sentence per line.
Retested everything.
* Update config.json
* Update config.json
Co-authored-by: BethanyG
---
config.json | 22 ++++++----
.../practice/pascals-triangle/.docs/hints.md | 10 +++++
.../.docs/instructions.append.md | 43 +++++++++++++++++++
.../.meta/additional_tests.json | 26 +++++++++++
.../pascals-triangle/.meta/config.json | 3 +-
.../pascals-triangle/.meta/example.py | 14 +++---
.../pascals-triangle/.meta/template.j2 | 39 +++++++++++++++++
.../pascals-triangle/pascals_triangle_test.py | 32 +++++++++-----
8 files changed, 159 insertions(+), 30 deletions(-)
create mode 100644 exercises/practice/pascals-triangle/.docs/hints.md
create mode 100644 exercises/practice/pascals-triangle/.docs/instructions.append.md
create mode 100644 exercises/practice/pascals-triangle/.meta/additional_tests.json
create mode 100644 exercises/practice/pascals-triangle/.meta/template.j2
diff --git a/config.json b/config.json
index f267cbd83ac..6c8340b289c 100644
--- a/config.json
+++ b/config.json
@@ -1271,6 +1271,19 @@
],
"difficulty": 3
},
+ {
+ "slug": "pascals-triangle",
+ "name": "Pascals Triangle",
+ "uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
+ "practices": ["recursion"],
+ "prerequisites": [
+ "basics",
+ "conditionals",
+ "lists",
+ "numbers"
+ ],
+ "difficulty": 4
+ },
{
"slug": "grep",
"name": "Grep",
@@ -2140,15 +2153,6 @@
"difficulty": 3,
"status": "deprecated"
},
- {
- "slug": "pascals-triangle",
- "name": "Pascal's Triangle",
- "uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
- "practices": [],
- "prerequisites": [],
- "difficulty": 3,
- "status": "deprecated"
- },
{
"slug": "point-mutations",
"name": "Point Mutations",
diff --git a/exercises/practice/pascals-triangle/.docs/hints.md b/exercises/practice/pascals-triangle/.docs/hints.md
new file mode 100644
index 00000000000..235e786ff83
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.docs/hints.md
@@ -0,0 +1,10 @@
+# Hints
+
+## General
+
+- A more detailed description of recursive programming can be found [here][g4g]
+- This exercise involves a test to ensure that you used a recursive solution
+- If you are having trouble completing this exercise, try [using a loop first, and then convert it into a recursive solution][educative]
+
+[g4g]: https://www.geeksforgeeks.org/recursion/
+[educative]: https://www.educative.io/collection/page/6151088528949248/4547996664463360/6292303276670976
diff --git a/exercises/practice/pascals-triangle/.docs/instructions.append.md b/exercises/practice/pascals-triangle/.docs/instructions.append.md
new file mode 100644
index 00000000000..3b236ae489f
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.docs/instructions.append.md
@@ -0,0 +1,43 @@
+# Instructions append
+
+## How this Exercise is Implemented in Python: Recursion
+
+This exercise is designed to be completed using [concept:python/recursion](), rather than loops.
+A recursive function is a function that calls itself, which is useful when solving problems that are defined in terms of themselves.
+To avoid infinite recursion (or more specifically, to avoid overflowing the stack), something called a "base case" is used.
+When the base case is reached, a non-recursive value is returned, which allows the previous function call to resolve and return its value, and so on, rippling back down the stack until the first function call returns the answer.
+We could write a recursive function to find the answer to 5! (i.e. 5 * 4 * 3 * 2 * 1) like so:
+
+````python
+def factorial(number):
+ if number <= 1: # base case
+ return 1
+
+ return number * factorial(number - 1) # recursive case
+
+print(factorial(5)) # returns 120
+````
+
+Finally, it should be noted that Python limits the number of times recursive calls can be made (1000 by default) and does not optimize for [tail recursion][tail-recursion].
+
+## Exception messages
+
+Sometimes it is necessary to [raise an exception][raising].
+When you do this, you should always include a **meaningful error message** to indicate what the source of the error is.
+This makes your code more readable and helps significantly with debugging.
+For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][built-in-errors], but should still include a meaningful message.
+
+This particular exercise requires that you use the [raise statement][raise-statement] to "throw" multiple `ValueErrors` if the `rows()` function is passed a negative number.
+The tests will only pass if you both `raise` the `exception` and include a message with it.
+
+To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
+
+```python
+# if the rows function is passed a negative number.
+raise ValueError("number of rows is negative")
+```
+
+[built-in-errors]: https://docs.python.org/3/library/exceptions.html#base-classes
+[raise-statement]: https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement
+[raising]: https://docs.python.org/3/tutorial/errors.html#raising-exceptions
+[tail-recursion]: https://www.geeksforgeeks.org/tail-recursion/
diff --git a/exercises/practice/pascals-triangle/.meta/additional_tests.json b/exercises/practice/pascals-triangle/.meta/additional_tests.json
new file mode 100644
index 00000000000..7c1375ab5a8
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.meta/additional_tests.json
@@ -0,0 +1,26 @@
+{
+ "cases": [
+ {
+ "description": "negative rows are invalid",
+ "property": "rows",
+ "input": {
+ "count": -1
+ },
+ "expected": {
+ "error": "number of rows is negative",
+ "type": "ValueError"
+ }
+ },
+ {
+ "description": "solution is recursive",
+ "property": "rows",
+ "input": {
+ "count": "OVERFLOW"
+ },
+ "expected": {
+ "error": "maximum recursion depth exceeded",
+ "type": "RecursionError"
+ }
+ }
+ ]
+}
diff --git a/exercises/practice/pascals-triangle/.meta/config.json b/exercises/practice/pascals-triangle/.meta/config.json
index 167b937cce9..4793e16f1c2 100644
--- a/exercises/practice/pascals-triangle/.meta/config.json
+++ b/exercises/practice/pascals-triangle/.meta/config.json
@@ -1,7 +1,8 @@
{
"blurb": "Compute Pascal's triangle up to a given number of rows.",
"authors": [
- "betegelse"
+ "betegelse",
+ "PaulT89"
],
"contributors": [
"behrtam",
diff --git a/exercises/practice/pascals-triangle/.meta/example.py b/exercises/practice/pascals-triangle/.meta/example.py
index 70f3aec1dd6..9181199bf22 100644
--- a/exercises/practice/pascals-triangle/.meta/example.py
+++ b/exercises/practice/pascals-triangle/.meta/example.py
@@ -1,12 +1,8 @@
-def rows(row_count):
+def rows(row_count, previous_row=[1]):
if row_count < 0:
- return None
+ raise ValueError("number of rows is negative")
elif row_count == 0:
return []
- row_list = []
- for idx in range(row_count):
- ronald = [1]
- for edx in range(idx):
- ronald.append(sum(row_list[-1][edx:edx+2]))
- row_list.append(ronald)
- return row_list
+ temp_row = previous_row + [0]
+ new_row = list(map(sum, zip(temp_row, temp_row[::-1])))
+ return [previous_row] + rows(row_count - 1, new_row)
diff --git a/exercises/practice/pascals-triangle/.meta/template.j2 b/exercises/practice/pascals-triangle/.meta/template.j2
new file mode 100644
index 00000000000..0041fd53c2b
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.meta/template.j2
@@ -0,0 +1,39 @@
+{%- import "generator_macros.j2" as macros with context -%}
+import sys
+{{ macros.header() }}
+
+TRIANGLE = [
+[1],
+[1, 1],
+[1, 2, 1],
+[1, 3, 3, 1],
+[1, 4, 6, 4, 1],
+[1, 5, 10, 10, 5, 1],
+[1, 6, 15, 20, 15, 6, 1],
+[1, 7, 21, 35, 35, 21, 7, 1],
+[1, 8, 28, 56, 70, 56, 28, 8, 1],
+[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
+]
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+{%- for case in cases %}
+ def test_{{ case["description"] | to_snake }}(self):
+ self.assertEqual({{ case["property"] }}({{ case["input"]["count"] }}), TRIANGLE[:{{ case["input"]["count"] }}])
+{% endfor %}
+
+# Additional tests for this track
+{%- for case in additional_cases %}
+ def test_{{ case["description"] | to_snake }}(self):
+ {%- if case is error_case %}
+ with self.assertRaises({{ case["expected"]["type"] }}) as err:
+ {%- if case["input"]["count"] == "OVERFLOW" %}
+ rows(sys.getrecursionlimit() + 10)
+ self.assertEqual(type(err.exception), RecursionError)
+ self.assertEqual(err.exception.args[0][:32], "maximum recursion depth exceeded")
+ {%- else %}
+ rows(-1)
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "number of rows is negative")
+ {%- endif %}
+ {%- endif %}
+{% endfor %}
diff --git a/exercises/practice/pascals-triangle/pascals_triangle_test.py b/exercises/practice/pascals-triangle/pascals_triangle_test.py
index c2c50681457..dc7b7ce1111 100644
--- a/exercises/practice/pascals-triangle/pascals_triangle_test.py
+++ b/exercises/practice/pascals-triangle/pascals_triangle_test.py
@@ -1,9 +1,11 @@
+import sys
import unittest
-from pascals_triangle import rows
+from pascals_triangle import (
+ rows,
+)
-
-# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
+# Tests adapted from `problem-specifications//canonical-data.json`
TRIANGLE = [
[1],
@@ -15,13 +17,13 @@
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
- [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
+ [1, 9, 36, 84, 126, 126, 84, 36, 9, 1],
]
class PascalsTriangleTest(unittest.TestCase):
def test_zero_rows(self):
- self.assertEqual(rows(0), [])
+ self.assertEqual(rows(0), TRIANGLE[:0])
def test_single_row(self):
self.assertEqual(rows(1), TRIANGLE[:1])
@@ -44,9 +46,17 @@ def test_six_rows(self):
def test_ten_rows(self):
self.assertEqual(rows(10), TRIANGLE[:10])
- def test_negative_rows(self):
- self.assertEqual(rows(-1), None)
-
-
-if __name__ == '__main__':
- unittest.main()
+ # Additional tests for this track
+ def test_negative_rows_are_invalid(self):
+ with self.assertRaises(ValueError) as err:
+ rows(-1)
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "number of rows is negative")
+
+ def test_solution_is_recursive(self):
+ with self.assertRaises(RecursionError) as err:
+ rows(sys.getrecursionlimit() + 10)
+ self.assertEqual(type(err.exception), RecursionError)
+ self.assertEqual(
+ err.exception.args[0][:32], "maximum recursion depth exceeded"
+ )
From ee4d9e4120a68b094c32be03ad6baa3882eae045 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 11 Dec 2022 01:44:49 +0100
Subject: [PATCH 354/932] [Exercise]: added square-root (#3279)
* added square-root
* Instruction Append
Co-authored-by: BethanyG
---
config.json | 13 +++++++++
.../square-root/.docs/instructions.append.md | 16 +++++++++++
.../square-root/.docs/instructions.md | 13 +++++++++
.../practice/square-root/.meta/config.json | 21 ++++++++++++++
.../practice/square-root/.meta/example.py | 5 ++++
.../practice/square-root/.meta/template.j2 | 15 ++++++++++
.../practice/square-root/.meta/tests.toml | 28 +++++++++++++++++++
exercises/practice/square-root/square_root.py | 2 ++
.../practice/square-root/square_root_test.py | 27 ++++++++++++++++++
9 files changed, 140 insertions(+)
create mode 100644 exercises/practice/square-root/.docs/instructions.append.md
create mode 100644 exercises/practice/square-root/.docs/instructions.md
create mode 100644 exercises/practice/square-root/.meta/config.json
create mode 100644 exercises/practice/square-root/.meta/example.py
create mode 100644 exercises/practice/square-root/.meta/template.j2
create mode 100644 exercises/practice/square-root/.meta/tests.toml
create mode 100644 exercises/practice/square-root/square_root.py
create mode 100644 exercises/practice/square-root/square_root_test.py
diff --git a/config.json b/config.json
index 6c8340b289c..d1399f2f3fd 100644
--- a/config.json
+++ b/config.json
@@ -593,6 +593,19 @@
],
"difficulty": 2
},
+ {
+ "slug": "square-root",
+ "name": "Square Root",
+ "uuid": "c32f994a-1080-4f05-bf88-051975a75d64",
+ "practices": ["number"],
+ "prerequisites": [
+ "basics",
+ "number",
+ "conditionals",
+ "loops"
+ ],
+ "difficulty": 2
+ },
{
"slug": "scrabble-score",
"name": "Scrabble Score",
diff --git a/exercises/practice/square-root/.docs/instructions.append.md b/exercises/practice/square-root/.docs/instructions.append.md
new file mode 100644
index 00000000000..535038e466d
--- /dev/null
+++ b/exercises/practice/square-root/.docs/instructions.append.md
@@ -0,0 +1,16 @@
+# Instructions append
+
+## How this Exercise is Structured in Python
+
+
+Python offers a wealth of mathematical functions in the form of the [math module][math-module] and built-ins such as [`pow()`][pow] and [`sum()`][sum].
+However, we'd like you to consider the challenge of solving this exercise without those built-ins or modules.
+
+While there is a mathematical formula that will find the square root of _any_ number, we have gone the route of only testing [natural numbers][nautral-number] (positive integers).
+
+
+[math-module]: https://docs.python.org/3/library/math.html
+[pow]: https://docs.python.org/3/library/functions.html#pow
+[sum]: https://docs.python.org/3/library/functions.html#sum
+[nautral-number]: https://en.wikipedia.org/wiki/Natural_number
+
diff --git a/exercises/practice/square-root/.docs/instructions.md b/exercises/practice/square-root/.docs/instructions.md
new file mode 100644
index 00000000000..e9905e9d416
--- /dev/null
+++ b/exercises/practice/square-root/.docs/instructions.md
@@ -0,0 +1,13 @@
+# Instructions
+
+Given a natural radicand, return its square root.
+
+Note that the term "radicand" refers to the number for which the root is to be determined.
+That is, it is the number under the root symbol.
+
+Check out the Wikipedia pages on [square root][square-root] and [methods of computing square roots][computing-square-roots].
+
+Recall also that natural numbers are positive real whole numbers (i.e. 1, 2, 3 and up).
+
+[square-root]: https://en.wikipedia.org/wiki/Square_root
+[computing-square-roots]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots
diff --git a/exercises/practice/square-root/.meta/config.json b/exercises/practice/square-root/.meta/config.json
new file mode 100644
index 00000000000..b1b9630b317
--- /dev/null
+++ b/exercises/practice/square-root/.meta/config.json
@@ -0,0 +1,21 @@
+{
+ "authors": [
+ "meatball133",
+ "Bethanyg"
+ ],
+ "contributors": [],
+ "files": {
+ "solution": [
+ "square_root.py"
+ ],
+ "test": [
+ "square_root_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "blurb": "Given a natural radicand, return its square root.",
+ "source": "wolf99",
+ "source_url": "https://github.com/exercism/problem-specifications/pull/1582"
+}
diff --git a/exercises/practice/square-root/.meta/example.py b/exercises/practice/square-root/.meta/example.py
new file mode 100644
index 00000000000..94cd0fdc711
--- /dev/null
+++ b/exercises/practice/square-root/.meta/example.py
@@ -0,0 +1,5 @@
+def square_root(number):
+ n = 0
+ while n ** 2 != number:
+ n += 1
+ return n
diff --git a/exercises/practice/square-root/.meta/template.j2 b/exercises/practice/square-root/.meta/template.j2
new file mode 100644
index 00000000000..9334317859f
--- /dev/null
+++ b/exercises/practice/square-root/.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"]["radicand"] }}),
+ {{ case["expected"] }}
+ )
+{%- endmacro %}
+{{ macros.header()}}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
diff --git a/exercises/practice/square-root/.meta/tests.toml b/exercises/practice/square-root/.meta/tests.toml
new file mode 100644
index 00000000000..ead7882fc3b
--- /dev/null
+++ b/exercises/practice/square-root/.meta/tests.toml
@@ -0,0 +1,28 @@
+# 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.
+
+[9b748478-7b0a-490c-b87a-609dacf631fd]
+description = "root of 1"
+
+[7d3aa9ba-9ac6-4e93-a18b-2e8b477139bb]
+description = "root of 4"
+
+[6624aabf-3659-4ae0-a1c8-25ae7f33c6ef]
+description = "root of 25"
+
+[93beac69-265e-4429-abb1-94506b431f81]
+description = "root of 81"
+
+[fbddfeda-8c4f-4bc4-87ca-6991af35360e]
+description = "root of 196"
+
+[c03d0532-8368-4734-a8e0-f96a9eb7fc1d]
+description = "root of 65025"
diff --git a/exercises/practice/square-root/square_root.py b/exercises/practice/square-root/square_root.py
new file mode 100644
index 00000000000..0a2fc38927d
--- /dev/null
+++ b/exercises/practice/square-root/square_root.py
@@ -0,0 +1,2 @@
+def square_root(number):
+ pass
diff --git a/exercises/practice/square-root/square_root_test.py b/exercises/practice/square-root/square_root_test.py
new file mode 100644
index 00000000000..635728fee45
--- /dev/null
+++ b/exercises/practice/square-root/square_root_test.py
@@ -0,0 +1,27 @@
+import unittest
+
+from square_root import (
+ square_root,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class SquareRootTest(unittest.TestCase):
+ def test_root_of_1(self):
+ self.assertEqual(square_root(1), 1)
+
+ def test_root_of_4(self):
+ self.assertEqual(square_root(4), 2)
+
+ def test_root_of_25(self):
+ self.assertEqual(square_root(25), 5)
+
+ def test_root_of_81(self):
+ self.assertEqual(square_root(81), 9)
+
+ def test_root_of_196(self):
+ self.assertEqual(square_root(196), 14)
+
+ def test_root_of_65025(self):
+ self.assertEqual(square_root(65025), 255)
From 0765a702e117ba7aad6d80578f3cfb4f5e17e86b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 16 Dec 2022 16:21:02 -0600
Subject: [PATCH 355/932] Update content.md (#3280)
Changed `This concept is also aparat of` to `This concept is also a part of`.
---
.../practice/wordy/.approaches/dunder-getattribute/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
index f64bc6fac30..dd3e6de39d6 100644
--- a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
@@ -76,7 +76,7 @@ It sets the list to the result of the dunder method plus the remaining elements
```exercism/note
The `*` prefix in `*tail` [unpacks](https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/) the `tail` list back into its elements.
-This concept is also aparat of [unpacking-and-multiple-assignment](https://exercism.org/tracks/python/concepts/unpacking-and-multiple-assignment) concept in the syllabus..
+This concept is also a part of [unpacking-and-multiple-assignment](https://exercism.org/tracks/python/concepts/unpacking-and-multiple-assignment) concept in the syllabus.
```
When the loop exhausts, the first element of the list is selected as the function return value.
From 090a5eb2828c4acd36f0cf6e9fa855ec2ed3f016 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 16 Dec 2022 23:01:30 +0000
Subject: [PATCH 356/932] Bump exercism/pr-commenter-action from 1.3.1 to 1.4.0
Bumps [exercism/pr-commenter-action](https://github.com/exercism/pr-commenter-action) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/exercism/pr-commenter-action/releases)
- [Changelog](https://github.com/exercism/pr-commenter-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/exercism/pr-commenter-action/compare/v1.3.1...v1.4.0)
---
updated-dependencies:
- dependency-name: exercism/pr-commenter-action
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
.github/workflows/pr-commenter.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pr-commenter.yml b/.github/workflows/pr-commenter.yml
index 12591d49004..13c0b62d44c 100644
--- a/.github/workflows/pr-commenter.yml
+++ b/.github/workflows/pr-commenter.yml
@@ -6,7 +6,7 @@ jobs:
pr-comment:
runs-on: ubuntu-latest
steps:
- - uses: exercism/pr-commenter-action@v1.3.1
+ - uses: exercism/pr-commenter-action@v1.4.0
with:
github-token: "${{ github.token }}"
config-file: ".github/pr-commenter.yml"
\ No newline at end of file
From 6ac97d58b28f8669f9e332bfcb8399cb5efd192d Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 10:53:15 -0800
Subject: [PATCH 357/932] Update SGF Parsing template, tests and escape
function
---
.../practice/sgf-parsing/.meta/example.py | 215 +++++++++---------
.../practice/sgf-parsing/.meta/template.j2 | 8 +-
.../practice/sgf-parsing/.meta/tests.toml | 8 +-
.../practice/sgf-parsing/sgf_parsing_test.py | 46 +++-
4 files changed, 160 insertions(+), 117 deletions(-)
diff --git a/exercises/practice/sgf-parsing/.meta/example.py b/exercises/practice/sgf-parsing/.meta/example.py
index 7711625518b..369c7de6436 100644
--- a/exercises/practice/sgf-parsing/.meta/example.py
+++ b/exercises/practice/sgf-parsing/.meta/example.py
@@ -1,108 +1,115 @@
-class SgfTree:
- def __init__(self, properties=None, children=None):
- self.properties = properties or {}
- self.children = children or []
-
- def __eq__(self, other):
- if not isinstance(other, SgfTree):
- return False
-
- for key, value in self.properties.items():
- if key not in other.properties:
- return False
-
- if other.properties[key] != value:
- return False
-
- for key in other.properties.keys():
- if key not in self.properties:
- return False
-
- if len(self.children) != len(other.children):
- return False
-
- for child, other_child in zip(self.children, other.children):
- if not child == other_child:
- return False
- return True
-
- def __repr__(self):
- """Ironically, encoding to SGF is much easier."""
- rep = '(;'
- for key, property_value in self.properties.items():
- rep += key
- for value in property_value:
- rep += '[{}]'.format(value)
- if self.children:
- if len(self.children) > 1:
- rep += '('
- for child in self.children:
- rep += repr(child)[1:-1]
- if len(self.children) > 1:
- rep += ')'
- return rep + ')'
-
-
-def parse(input_string):
- root = None
- current = None
- stack = list(input_string)
- if input_string == '()':
- raise ValueError('tree with no nodes')
+"""Parse an SGF tree."""
+from __future__ import annotations
- if not stack:
- raise ValueError('tree missing')
+import collections
+import dataclasses
+import string
- def pop():
- if stack[0] == '\\':
- stack.pop(0)
- characters = stack.pop(0)
- return ' ' if characters in ['\t'] else characters
-
- def pop_until(delimiter):
- try:
- value = ''
- while stack[0] != delimiter:
- if stack[0] == "\n":
- stack[0] = "n"
- if stack[0] == "\t":
- stack[0] = "t"
- value += pop()
- return value
- except IndexError as error:
- raise ValueError('properties without delimiter') from error
-
- while stack:
- if pop() == '(' and stack[0] == ';':
- while pop() == ';':
- properties = {}
- while stack[0].isupper():
- key = pop_until('[')
- if not key.isupper():
- raise ValueError('property must be in uppercase')
- values = []
- while stack[0] == '[':
- pop()
- values.append(pop_until(']'))
- pop()
- properties[key] = values
-
- if stack[0].isalpha():
- if not stack[0].isupper():
- raise ValueError('property must be in uppercase')
-
- if root is None:
- current = root = SgfTree(properties)
+@dataclasses.dataclass
+class SgfTree:
+ """SGF Node."""
+
+ properties: dict[str, str] = dataclasses.field(default_factory=dict)
+ children: list[SgfTree] = dataclasses.field(default_factory=list)
+
+
+def parse_property_vals(sgf: str, idx: int) -> tuple[int, list[str]]:
+ """Parse property values, returning the next index and values."""
+ values = []
+ while idx < len(sgf):
+ if sgf[idx] != "[":
+ break
+
+ # Start of the value.
+ idx += 1
+ prop_val = ""
+ while sgf[idx] != "]":
+ # \ has special SGF handling.
+ if sgf[idx] == "\\":
+ if sgf[idx:idx + 2] == "\\\n":
+ # Newlines are removed if they come immediately after a \,
+ # otherwise they remain as newlines.
+ pass
else:
- current = SgfTree(properties)
- root.children.append(current)
-
- while stack[0] == '(':
- child_input = pop() + pop_until(')') + pop()
- current.children.append(parse(child_input))
-
- elif root is None and current is None:
- raise ValueError('tree missing')
-
- return root
+ # \ is the escape character. Any non-whitespace character
+ # after \ is inserted as-is
+ prop_val += sgf[idx + 1]
+ idx += 2
+ else:
+ prop_val += sgf[idx]
+ idx += 1
+
+ # All whitespace characters other than newline are converted to spaces.
+ for char in string.whitespace:
+ if char == "\n":
+ continue
+ prop_val = prop_val.replace(char, " ")
+
+ values.append(prop_val)
+ idx += 1
+
+ return idx, values
+
+
+def parse_node(sgf: str) -> SgfTree:
+ """Parse and return a Node."""
+ if not sgf.startswith(";"):
+ raise ValueError("node must start with ';'")
+
+ idx = 1
+ prop_key_start = idx
+
+ properties = collections.defaultdict(list)
+ children = []
+
+ while idx < len(sgf):
+ match sgf[idx]:
+ case "[":
+ # Parse property values.
+ if idx == prop_key_start:
+ raise ValueError("propery key is empty")
+ prop_key = sgf[prop_key_start:idx]
+ if not prop_key.isupper():
+ raise ValueError('property must be in uppercase')
+
+ idx, prop_vals = parse_property_vals(sgf, idx)
+ properties[prop_key].extend(prop_vals)
+
+ # New property.
+ prop_key_start = idx
+ case ";":
+ # Single child.
+ child = parse_node(sgf[idx:])
+ children.append(child)
+ break
+ case "(":
+ # Multiple children.
+ children = []
+ while idx < len(sgf):
+ if sgf[idx] != "(":
+ break
+ # Child start.
+ idx += 1
+ child_start = idx
+ while sgf[idx] != ")":
+ idx += 1
+ # Child end.
+ child = parse_node(sgf[child_start:idx])
+ children.append(child)
+ idx += 1
+ case _:
+ idx += 1
+
+ if idx > prop_key_start and not properties:
+ raise ValueError('properties without delimiter')
+ return SgfTree(children=children, properties=dict(properties))
+
+
+def parse(sgf: str) -> SgfTree:
+ """Parse an SGF tree."""
+ if not sgf.startswith("(") and not sgf.endswith(")"):
+ raise ValueError('tree missing')
+ if not sgf.startswith("(;"):
+ raise ValueError('tree with no nodes')
+ return parse_node(sgf.removeprefix("(").removesuffix(")"))
diff --git a/exercises/practice/sgf-parsing/.meta/template.j2 b/exercises/practice/sgf-parsing/.meta/template.j2
index f3da29b39ca..7017a436480 100644
--- a/exercises/practice/sgf-parsing/.meta/template.j2
+++ b/exercises/practice/sgf-parsing/.meta/template.j2
@@ -1,8 +1,12 @@
{%- import "generator_macros.j2" as macros with context -%}
+{% macro escape_sequences(string) -%}
+ {{ string | replace("\\", "\\\\") | replace("\n", "\\n") | replace("\t", "\\t") }}
+{%- endmacro -%}
+
{% macro test_case(case) -%}
def test_{{ case["description"] | to_snake }}(self):
- input_string = '{{ case["input"]["encoded"] | escape_invalid_escapes }}'
+ input_string = '{{ escape_sequences(case["input"]["encoded"]) }}'
{% if case["expected"]["error"] -%}
with self.assertRaises(ValueError) as err:
{{ case["property"] | to_snake }}(input_string)
@@ -33,7 +37,7 @@
{%- for key, values in properties.items() -%}
'{{ key }}':[
{%- for value in values -%}
- '{{ value }}'{% if not loop.last %}, {% endif -%}
+ '{{ escape_sequences(value) }}'{% if not loop.last %}, {% endif -%}
{% endfor -%}]
{%- if not loop.last %}, {% endif -%}
{% endfor -%} }
diff --git a/exercises/practice/sgf-parsing/.meta/tests.toml b/exercises/practice/sgf-parsing/.meta/tests.toml
index 71f5b83a64d..2a9d7d927bf 100644
--- a/exercises/practice/sgf-parsing/.meta/tests.toml
+++ b/exercises/practice/sgf-parsing/.meta/tests.toml
@@ -47,23 +47,18 @@ description = "multiple property values"
[28092c06-275f-4b9f-a6be-95663e69d4db]
description = "within property values, whitespace characters such as tab are converted to spaces"
-include = false
[deaecb9d-b6df-4658-aa92-dcd70f4d472a]
-description = "within property values, newli es remain as newlines"
-include = false
+description = "within property values, newlines remain as newlines"
[8e4c970e-42d7-440e-bfef-5d7a296868ef]
description = "escaped closing bracket within property value becomes just a closing bracket"
-include = false
[cf371fa8-ba4a-45ec-82fb-38668edcb15f]
description = "escaped backslash in property value becomes just a backslash"
-include = false
[dc13ca67-fac0-4b65-b3fe-c584d6a2c523]
description = "opening bracket within property value doesn't need to be escaped"
-include = false
[a780b97e-8dbb-474e-8f7e-4031902190e8]
description = "semicolon in property value doesn't need to be escaped"
@@ -83,7 +78,6 @@ description = "escaped t and n in property value are just letters, not whitespac
[08e4b8ba-bb07-4431-a3d9-b1f4cdea6dab]
description = "mixing various kinds of whitespace and escaped characters in property value"
reimplements = "11c36323-93fc-495d-bb23-c88ee5844b8c"
-include = false
[11c36323-93fc-495d-bb23-c88ee5844b8c]
description = "escaped property"
diff --git a/exercises/practice/sgf-parsing/sgf_parsing_test.py b/exercises/practice/sgf-parsing/sgf_parsing_test.py
index 9b01fb11d09..b3d43ed09df 100644
--- a/exercises/practice/sgf-parsing/sgf_parsing_test.py
+++ b/exercises/practice/sgf-parsing/sgf_parsing_test.py
@@ -84,6 +84,38 @@ def test_multiple_property_values(self):
expected = SgfTree(properties={"A": ["b", "c", "d"]})
self.assertEqual(parse(input_string), expected)
+ def test_within_property_values_whitespace_characters_such_as_tab_are_converted_to_spaces(
+ self,
+ ):
+ input_string = "(;A[hello\t\tworld])"
+ expected = SgfTree(properties={"A": ["hello world"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_within_property_values_newlines_remain_as_newlines(self):
+ input_string = "(;A[hello\n\nworld])"
+ expected = SgfTree(properties={"A": ["hello\n\nworld"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_escaped_closing_bracket_within_property_value_becomes_just_a_closing_bracket(
+ self,
+ ):
+ input_string = "(;A[\\]])"
+ expected = SgfTree(properties={"A": ["]"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_escaped_backslash_in_property_value_becomes_just_a_backslash(self):
+ input_string = "(;A[\\\\])"
+ expected = SgfTree(properties={"A": ["\\"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_opening_bracket_within_property_value_doesn_t_need_to_be_escaped(self):
+ input_string = "(;A[x[y\\]z][foo]B[bar];C[baz])"
+ expected = SgfTree(
+ properties={"A": ["x[y]z", "foo"], "B": ["bar"]},
+ children=[SgfTree({"C": ["baz"]})],
+ )
+ self.assertEqual(parse(input_string), expected)
+
def test_semicolon_in_property_value_doesn_t_need_to_be_escaped(self):
input_string = "(;A[a;b][foo]B[bar];C[baz])"
expected = SgfTree(
@@ -101,17 +133,23 @@ def test_parentheses_in_property_value_don_t_need_to_be_escaped(self):
self.assertEqual(parse(input_string), expected)
def test_escaped_tab_in_property_value_is_converted_to_space(self):
- input_string = "(;A[hello\\ world])"
+ input_string = "(;A[hello\\\tworld])"
expected = SgfTree(properties={"A": ["hello world"]})
self.assertEqual(parse(input_string), expected)
def test_escaped_newline_in_property_value_is_converted_to_nothing_at_all(self):
- input_string = "(;A[hello\
-world])"
+ input_string = "(;A[hello\\\nworld])"
expected = SgfTree(properties={"A": ["helloworld"]})
self.assertEqual(parse(input_string), expected)
def test_escaped_t_and_n_in_property_value_are_just_letters_not_whitespace(self):
- input_string = "(;A[\t = t and \n = n])"
+ input_string = "(;A[\\t = t and \\n = n])"
expected = SgfTree(properties={"A": ["t = t and n = n"]})
self.assertEqual(parse(input_string), expected)
+
+ def test_mixing_various_kinds_of_whitespace_and_escaped_characters_in_property_value(
+ self,
+ ):
+ input_string = "(;A[\\]b\nc\\\nd\t\te\\\\ \\\n\\]])"
+ expected = SgfTree(properties={"A": ["]b\ncd e\\ ]"]})
+ self.assertEqual(parse(input_string), expected)
From cfbebf6708d1ab3d81519b7beed3f097159dfa4a Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 12:32:57 -0800
Subject: [PATCH 358/932] Bump flake8 version for requirements-generator from
3.7.8 to 5.0.4
---
requirements-generator.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/requirements-generator.txt b/requirements-generator.txt
index 44391b2d663..3c096bebc1e 100644
--- a/requirements-generator.txt
+++ b/requirements-generator.txt
@@ -1,6 +1,6 @@
black==22.3.0
-flake8==3.7.8
+flake8~=5.0.4
Jinja2~=3.1.2
python-dateutil==2.8.1
markupsafe==2.0.1
-tomli~=2.0.1
\ No newline at end of file
+tomli~=2.0.1
From 33afa506a44a0991bb121b02ab618eacf29f2bcb Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 12:35:38 -0800
Subject: [PATCH 359/932] Add IsaacG as a contributor to SGF Parsing
---
exercises/practice/sgf-parsing/.meta/config.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/sgf-parsing/.meta/config.json b/exercises/practice/sgf-parsing/.meta/config.json
index 284ea0fb2f9..565a8ce0d51 100644
--- a/exercises/practice/sgf-parsing/.meta/config.json
+++ b/exercises/practice/sgf-parsing/.meta/config.json
@@ -7,6 +7,7 @@
"crsmi",
"Dog",
"elyashiv",
+ "IsaacG",
"thomasjpfan",
"tqa236",
"yawpitch"
From 2fba89110c2567b3e7f0ff7c6e00cdc910fd2bbd Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 12:43:51 -0800
Subject: [PATCH 360/932] Drop newer features like `match case` and
str.removeprefix
---
.../practice/sgf-parsing/.meta/example.py | 70 +++++++++----------
1 file changed, 35 insertions(+), 35 deletions(-)
diff --git a/exercises/practice/sgf-parsing/.meta/example.py b/exercises/practice/sgf-parsing/.meta/example.py
index 369c7de6436..2b14e371530 100644
--- a/exercises/practice/sgf-parsing/.meta/example.py
+++ b/exercises/practice/sgf-parsing/.meta/example.py
@@ -64,42 +64,41 @@ def parse_node(sgf: str) -> SgfTree:
children = []
while idx < len(sgf):
- match sgf[idx]:
- case "[":
- # Parse property values.
- if idx == prop_key_start:
- raise ValueError("propery key is empty")
- prop_key = sgf[prop_key_start:idx]
- if not prop_key.isupper():
- raise ValueError('property must be in uppercase')
-
- idx, prop_vals = parse_property_vals(sgf, idx)
- properties[prop_key].extend(prop_vals)
-
- # New property.
- prop_key_start = idx
- case ";":
- # Single child.
- child = parse_node(sgf[idx:])
- children.append(child)
- break
- case "(":
- # Multiple children.
- children = []
- while idx < len(sgf):
- if sgf[idx] != "(":
- break
- # Child start.
- idx += 1
- child_start = idx
- while sgf[idx] != ")":
- idx += 1
- # Child end.
- child = parse_node(sgf[child_start:idx])
- children.append(child)
+ if sgf[idx] == "[":
+ # Parse property values.
+ if idx == prop_key_start:
+ raise ValueError("propery key is empty")
+ prop_key = sgf[prop_key_start:idx]
+ if not prop_key.isupper():
+ raise ValueError('property must be in uppercase')
+
+ idx, prop_vals = parse_property_vals(sgf, idx)
+ properties[prop_key].extend(prop_vals)
+
+ # New property.
+ prop_key_start = idx
+ elif sgf[idx] == ";":
+ # Single child.
+ child = parse_node(sgf[idx:])
+ children.append(child)
+ break
+ elif sgf[idx] == "(":
+ # Multiple children.
+ children = []
+ while idx < len(sgf):
+ if sgf[idx] != "(":
+ break
+ # Child start.
+ idx += 1
+ child_start = idx
+ while sgf[idx] != ")":
idx += 1
- case _:
+ # Child end.
+ child = parse_node(sgf[child_start:idx])
+ children.append(child)
idx += 1
+ else:
+ idx += 1
if idx > prop_key_start and not properties:
raise ValueError('properties without delimiter')
@@ -112,4 +111,5 @@ def parse(sgf: str) -> SgfTree:
raise ValueError('tree missing')
if not sgf.startswith("(;"):
raise ValueError('tree with no nodes')
- return parse_node(sgf.removeprefix("(").removesuffix(")"))
+ inside = sgf[1:-1]
+ return parse_node(inside)
From 2e58919bfb22b20f89782b79a6a06e0bc3c4c90d Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 19 Dec 2022 09:34:52 +0100
Subject: [PATCH 361/932] removed 3 lines
---
exercises/practice/linked-list/.meta/template.j2 | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 103be69f0dd..a753efc3df4 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -9,11 +9,9 @@
{%- set error_operation = inputs[-1]["operation"] -%}
{% endif %}
{%- if case["expected"] and inputs|length > 1 -%}
- {%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
{%- elif case["expected"] -%}
- {%- set final = inputs[-1]["operation"] -%}
- {%- set inputs = [{"operation": "None"}] -%}
+ {%- set inputs = [] -%}
{%- endif -%}
{%- for input in inputs -%}
{%- set operation = input["operation"] -%}
@@ -34,17 +32,16 @@
{%- endif %}
{%- endfor %}
{%- if error_case %}
- {%- if final == "pop" or final == "shift" %}
+ {%- if error_operation == "pop" or error_operation == "shift" %}
with self.assertRaises(IndexError) as err:
lst.{{ error_operation }}()
self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
- {% elif final == "delete" %}
+ {%- elif error_operation == "delete" %}
with self.assertRaises(ValueError) as err:
- lst.{{ final }}({{ value if value else 0 }})
+ lst.{{ error_operation }}({{ value if value else 0 }})
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
{%- endmacro %}
From 2107057937c77f0a3e36f7154483d5cb7c7f8498 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 22 Dec 2022 22:15:27 +0100
Subject: [PATCH 362/932] Fix file-name
---
.../.docs/{instructions.apped.md => instructions.append.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename exercises/practice/linked-list/.docs/{instructions.apped.md => instructions.append.md} (100%)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.append.md
similarity index 100%
rename from exercises/practice/linked-list/.docs/instructions.apped.md
rename to exercises/practice/linked-list/.docs/instructions.append.md
From ad8e8c94058ada935479b22024811b5e2f0e0c8b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 20 Dec 2022 23:01:32 +0000
Subject: [PATCH 363/932] Bump actions/stale from 6 to 7
Bumps [actions/stale](https://github.com/actions/stale) from 6 to 7.
- [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/v6...v7)
---
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 9e4ecd1d5c9..4742a0db0a0 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@v6
+ - uses: actions/stale@v7
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
From a45dce1c28c66812800eb9fa5b0bacb37f6bd892 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Wed, 21 Dec 2022 20:47:56 +0100
Subject: [PATCH 364/932] First push
---
.../electric-bill/.docs/instructions.md | 81 +++++++++++++++++++
.../concept/electric-bill/.meta/config.json | 19 +++++
.../concept/electric-bill/.meta/exemplar.py | 48 +++++++++++
.../concept/electric-bill/electric_bill.py | 48 +++++++++++
.../electric-bill/electric_bill_test.py | 58 +++++++++++++
5 files changed, 254 insertions(+)
create mode 100644 exercises/concept/electric-bill/.docs/instructions.md
create mode 100644 exercises/concept/electric-bill/.meta/config.json
create mode 100644 exercises/concept/electric-bill/.meta/exemplar.py
create mode 100644 exercises/concept/electric-bill/electric_bill.py
create mode 100644 exercises/concept/electric-bill/electric_bill_test.py
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
new file mode 100644
index 00000000000..c8022915da8
--- /dev/null
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -0,0 +1,81 @@
+# Instructions
+
+The company you work for want to reduce their carbon footprint, so they wants you to write a program which calculates the power usage of their electronics and the cost of running them.
+
+1. Get extra hours
+
+Your employer wants a program that calculates the time it takes to run different electronics.
+Currently the time is stored in hours.
+When your employer added the hours they noticed that the time was not correct and they want you to add 3 extra hours to the time data.
+They also would like to know how many hours needs to be removed to get the data in full days(24 hours).
+The time given doesn't have to be in full days.
+
+Implement a function `get_extra_hours()` that accepts an integer which holds the number of hours.
+The function should then `return` an integer with how many hours which has to be removed to get the time in full days.
+
+```python
+>>> get_extra_hours(25)
+4
+```
+
+2. Get kW value
+
+Your employer wants to know the power usage of the different electronics.
+They want to know the power usage in kW.
+kW stands for kilowatt, there watts is a unit of power.
+Kilo in the unit name is a prefix in the metric system meaning 1000.
+So 1 kilowatt is equal to 1000 watts.
+
+Implement a function `get_kW_value()` that accepts an integer which holds the number of watts.
+The function should then `return` the watts as kilowatts rounded to 1 decimal.
+
+```python
+>>> get_kW_value(1150)
+1.2
+```
+
+3. Get kwh value
+
+To be able to calculate the cost of running the electronics your employer wants to know the power usage in kWh.
+kWh stands for kilowatt-hour, there hour is a unit of time.
+So 1 kilowatt-hour is equal to 1000 watts used for 1 hour.
+An hour is made of 60 minutes and a minute is made of 60 seconds.
+So 1 hour is equal to 3600 seconds.
+To get the kWh value you have to have to convert the watts to kW and then floor-divide it by 3600.
+
+Implement a function `get_kWh_value()` that accepts an integer which holds the number of watts.
+The function should then `return` the watts as an integer.
+
+```python
+>>> get_kWh_value(5000000)
+1
+```
+
+4. Get efficiency
+
+Electronics are not 100% efficient.
+Therefore your employer wants to know the efficiency of the electronics.
+To get efficiency you have to divide the power factor by 100.
+The power factor is a float between 0 and 100.
+
+Implement a function `get_efficiency()` that accepts a float that holds the power factor.
+The function should then `return` the power factor as a float.
+
+```python
+>>> get_efficiency(80)
+0.8
+```
+
+5. Get cost
+
+Your employer wants to know the cost of running the electronics.
+The power used is the power given divided by the efficiency.
+The cost of running the electronics is the power used multiplied by the cost per kWh.
+
+Implement a function `get_cost()` that accepts an integer that holds the number of watts and a float that has the power factor and a float that holds the cost per kwh.
+The function should then `return` the cost of running the electronics as a float rounded to 2 decimals.
+
+```python
+>>> get_cost(5000000, 80, 0.25)
+0.3125
+```
diff --git a/exercises/concept/electric-bill/.meta/config.json b/exercises/concept/electric-bill/.meta/config.json
new file mode 100644
index 00000000000..d1560b2033e
--- /dev/null
+++ b/exercises/concept/electric-bill/.meta/config.json
@@ -0,0 +1,19 @@
+{
+ "authors": [
+ "meatball133",
+ "BethanyG"
+ ],
+ "files": {
+ "solution": [
+ "electric_bill.py"
+ ],
+ "test": [
+ "electric_bill_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "city-office",
+ "blurb": "to do"
+}
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
new file mode 100644
index 00000000000..996b9e3afa4
--- /dev/null
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -0,0 +1,48 @@
+"""Functions which helps company to calculate their power usage"""
+
+
+def get_the_amount_of_hours(hours, missing_hours):
+ """Return the amount of hours.
+
+ :param: hours: int - amount of hours.
+ :return: int - amount of hours.
+ """
+ return (hours + missing_hours) % 24
+
+
+def get_kW_value(watts):
+ """Return the kW value of a given watt value.
+
+ :param: watts: int - watt value.
+ :return: float - kW value.
+ """
+ return round(watts / 1000, 1) # rounds here
+
+
+def get_kwh_value(watts):
+ """Return the kWh value of a given watt value and hours.
+
+ :param: watts: int - watt value.
+ :param: hours: int - kilowatt hour value.
+ """
+ return get_kW_value(watts) // 3600
+
+
+def get_efficiency(efficiency):
+ """Return the efficiency as a power factor.
+
+ :param: efficiency: float - efficiency.
+ :return: float - efficiency.
+ """
+ return efficiency / 100
+
+
+def get_price_of_kwh(watts, efficiency, price):
+ """Return the price of a given kWh value, efficiency and price.
+
+ :param: watts: int - watt value.
+ :param: efficiency: float - efficiency.
+ :param: price: float - price of kWh.
+ :return: float - price of kWh.
+ """
+ return price * (get_kwh_value(watts) / get_efficiency(efficiency))
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
new file mode 100644
index 00000000000..e7e90c59c60
--- /dev/null
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -0,0 +1,48 @@
+"""Functions which helps company to calculate their power usage"""
+
+
+def get_the_amount_of_hours(hours):
+ """Return the amount of hours.
+
+ :param: hours: int - amount of hours.
+ :return: int - amount of hours.
+ """
+ return (hours + 3) % 24
+
+
+def get_kW_value(watts):
+ """Return the kW value of a given watt value.
+
+ :param: watts: int - watt value.
+ :return: float - kW value.
+ """
+ return round(watts / 1000, 1) # rounds here
+
+
+def get_kwh_value(watts):
+ """Return the kWh value of a given watt value and hours.
+
+ :param: watts: int - watt value.
+ :param: hours: int - kilowatt hour value.
+ """
+ return int(get_kW_value(watts) // 3600)
+
+
+def get_efficiency(efficiency):
+ """Return the efficiency as a power factor.
+
+ :param: efficiency: float - efficiency.
+ :return: float - efficiency.
+ """
+ return efficiency / 100
+
+
+def get_price_of_kwh(watts, efficiency, price):
+ """Return the price of a given kWh value, efficiency and price.
+
+ :param: watts: int - watt value.
+ :param: efficiency: float - efficiency.
+ :param: price: float - price of kWh.
+ :return: float - price of kWh.
+ """
+ return price * (get_kwh_value(watts) / get_efficiency(efficiency))
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
new file mode 100644
index 00000000000..95745e74a8d
--- /dev/null
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -0,0 +1,58 @@
+import unittest
+import pytest
+from electric_bill import (get_the_amount_of_hours,
+ get_kW_value,
+ get_kwh_value,
+ get_efficiency,
+ get_price_of_kwh)
+
+
+class LocomotiveEngineerTest(unittest.TestCase):
+
+ @pytest.mark.task(taskno=1)
+ def test_get_the_amount_of_hours(self):
+ input_data = [25, 10, 5, 2, 1, 120, 21]
+ output_data = [4, 13, 8, 5, 4, 3, 0]
+
+ 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 value.'
+ self.assertEqual(get_the_amount_of_hours(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=2)
+ def test_get_kW_value(self):
+ input_data = [1000, 2200, 2900, 900, 1160]
+ output_data = [1, 2.2, 2.9, 0.9, 1.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 value.'
+ self.assertEqual(get_kW_value(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=3)
+ def test_get_kwh_value(self):
+ input_data = (5000000, 2141241, 43252135, 5324623462, 4321512)
+ output_data = [1, 0, 12, 1479, 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 value.'
+ self.assertEqual(get_kwh_value(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=4)
+ def test_get_efficiency(self):
+ input_data = [80.0, 99.99, 0.8, 40.0]
+ output_data = [0.8, 0.9999, 0.008, 0.4]
+
+ 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 value.'
+ self.assertAlmostEqual(get_efficiency(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=5)
+ def test_get_price_of_kwh(self):
+ input_data = ((5000000, 80.0, 0.25), (2141241, 99.99, 2), (43252135, 0.8, 4), (4321512, 40.0, 2))
+ output_data = (0.3125, 0, 6000, 5)
+
+ 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 value.'
+ self.assertEqual(get_price_of_kwh(*input_data), output_data, msg=error_msg)
From a4d877299b6a57c6f0cf12ef1f364b5460276c8f Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 21 Dec 2022 21:21:52 +0100
Subject: [PATCH 365/932] Update
---
.../concept/electric-bill/.docs/instructions.md | 13 ++++++-------
exercises/concept/electric-bill/.meta/exemplar.py | 4 ++--
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index c8022915da8..ee81c2b23f6 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -54,12 +54,11 @@ The function should then `return` the watts as an integer.
4. Get efficiency
Electronics are not 100% efficient.
-Therefore your employer wants to know the efficiency of the electronics.
-To get efficiency you have to divide the power factor by 100.
-The power factor is a float between 0 and 100.
+Therefore, your employer wants you to calculate the efficiency of the electronics.
+To get efficiency you have to divide the power factor (a float between 0 and 100) by 100.
Implement a function `get_efficiency()` that accepts a float that holds the power factor.
-The function should then `return` the power factor as a float.
+The function should then `return` the calculated efficiency as a float.
```python
>>> get_efficiency(80)
@@ -69,11 +68,11 @@ The function should then `return` the power factor as a float.
5. Get cost
Your employer wants to know the cost of running the electronics.
-The power used is the power given divided by the efficiency.
The cost of running the electronics is the power used multiplied by the cost per kWh.
+The power used is the power given divided by the calculated efficiency.
-Implement a function `get_cost()` that accepts an integer that holds the number of watts and a float that has the power factor and a float that holds the cost per kwh.
-The function should then `return` the cost of running the electronics as a float rounded to 2 decimals.
+Implement a function `get_cost()` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
+The function should then `return` the cost of running the electronics as a float.
```python
>>> get_cost(5000000, 80, 0.25)
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 996b9e3afa4..7102c5d6c1d 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -1,13 +1,13 @@
"""Functions which helps company to calculate their power usage"""
-def get_the_amount_of_hours(hours, missing_hours):
+def get_the_amount_of_hours(hours):
"""Return the amount of hours.
:param: hours: int - amount of hours.
:return: int - amount of hours.
"""
- return (hours + missing_hours) % 24
+ return (hours + 3) % 24
def get_kW_value(watts):
From a652bb712c1874381c45af9e4f8eff32c456c20b Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 22 Dec 2022 22:13:20 +0100
Subject: [PATCH 366/932] started
---
concepts/numbers/about.md | 136 +++++++++++++++++++-------------------
1 file changed, 68 insertions(+), 68 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index e746972d1fd..34ddd9573c1 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -15,115 +15,115 @@ Whole numbers (_including hex, octals and binary numbers_) **without** decimal p
-12
```
-Hex numbers are prefixed with `0x`:
+Numbers containing a decimal point (with or without fractional parts) are identified as `floats`:
```python
-# Hex numbers start with 0x.
->>> 0x17
-23
->>> type(0x17)
-
+>>> 3.45
+3.45
+>>> type(3.45)
+
```
-Octals are prefixed with a `0o`:
+## Arithmetic
+
+Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
+### Addition and subtraction
+
+Addition and subtraction act like in normal math.
+If one of the operands is a `float`, the other will be converted to a `float` as well.
+Otherwise both operands will be converted to `ints`:
```python
-# Octal numbers start with a 0o.
->>> 0o446
-294
->>> type(0o446)
-
+>>> 5 - 3
+2
+# The int is widened to a float here, and a float is returned.
+>>> 3 + 4.0
+7.0
```
-Binary numbers are prefixed with `0b`, and written with only zeros and ones:
+### Multiplication
+
+As with addition and subtraction, multiplication will convert narrower numbers to match their less narrow counterparts:
```python
-# Binary numbers are made up of 0s and 1s.
->>> 0b1100110
-102
->>> type(0b1100110)
-
+>>> 3 * 2
+6
+
+>>> 3 * 2.0
+6.0
```
-Each of these `int` displays can be converted into the other via constructor:
+### Division
-```python
+Division always returns a `float`, even if the result is a whole number:
->>> starting_number = 1234
+```python
+>>> 6/5
+1.2
->>> hex(starting_number)
-'0x4d2'
+>>> 6/2
+3.0
+```
->>> oct(starting_number)
-'0o2322'
+### Floor division
->>> bin(starting_number)
-'0b10011010010'
+If an `int` result is needed, you can use floor division to truncate the result.
+Floor division is performed using the `//` operator:
->>> hex(0b10011010010)
-'0x4d2'
+```python
+>>> 6//5
+1
->>> int(0x4d2)
-1234
+>>> 6//2
+3
```
-Numbers containing a decimal point (with or without fractional parts) are identified as `floats`:
+### Modulo
+
+The modulo operator (`%`) returns the remainder of the division of the two operands:
```python
->>> 3.45
-3.45
->>> type(3.45)
-
+>>> 5 % 3
+2
```
-Appending `j` or `J` to a number creates an _imaginary number_ -- a `complex` number with a zero real part. `ints` or `floats` can then be added to an imaginary number to create a `complex` number with both real and imaginary parts:
+### Exponentiation
-```python
->>> 3j
-3j
->>> type(3j)
-
+Exponentiation is performed using the `**` operator:
->>> 3.5+4j
-(3.5+4j)
+```python
+>>> 2 ** 3
+8
```
-## Arithmetic
+All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
-Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+## Conversions
-Python considers `ints` narrower than `floats`, which are considered narrower than `complex` numbers. Comparisons between different number types behave as if the _exact_ values of those numbers were being compared:
+Numbers can be converted from one type to another using the built-in functions `int()` and `float()`:
```python
-# The int is widened to a float here, and a float is returned.
->>> 3 + 4.0
-7.0
-
-# The int is widened to a complex number, and a complex number is returned.
->>> 6/(3+2j)
-(2+2j)
+>>> int(3.45)
+3
-# Division always returns a float, even if integers are used.
->>> 6/2
+>>> float(3)
3.0
+```
-# If an int result is needed, you can use floor division to truncate the result.
->>> 6//2
-3
+## Round
-# When comparing, exact values are used.
->>> 23 == 0x17
-True
+Python provides a built-in function `round()` to round off a floating point number to a given number of decimal places.
+If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
->>> 0b10111 == 0x17
-True
+```python
+>>> round(3.1415926535, 2)
+3.14
->>> 6 == (6+0j)
-True
+>>> round(3.1415926535)
+3
```
-All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
-
## Precision & Representation
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
From edce70c43929ed46160f5d339ccf7a4d0262c328 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 23 Dec 2022 16:59:18 +0100
Subject: [PATCH 367/932] Add round
---
concepts/numbers/about.md | 33 +++++++++++++++++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 34ddd9573c1..1ea487843fc 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -113,7 +113,7 @@ Numbers can be converted from one type to another using the built-in functions `
## Round
-Python provides a built-in function `round()` to round off a floating point number to a given number of decimal places.
+Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
```python
@@ -124,13 +124,42 @@ If no number of decimal places is specified, the number is rounded off to the ne
3
```
+## Priority and parentheses
+
+Python allows you to use parentheses to group expressions.
+This is useful when you want to override the default order of operations.
+
+```python
+>>> 2 + 3 * 4
+14
+
+>>> (2 + 3) * 4
+20
+```
+
+Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+This means `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
+
+```python
+>>> 2 + 3 - 4 * 4
+-11
+
+>>> (2 + 3 - 4) * 4
+20
+
+# In the following example, the `**` operator has the highest priority, then `*`, then `+`
+# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
+>>> 2 + 3 * 4 ** 4
+770
+```
+
## Precision & Representation
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
-For a more detailed discussions of the issues and limitations of floating point arithmetic across programming langages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
+For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[int]: https://docs.python.org/3/library/functions.html#int
[float]: https://docs.python.org/3/library/functions.html#float
From eed14b390110b245c0c24de7e10b049794110ea5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sat, 24 Dec 2022 23:16:56 +0100
Subject: [PATCH 368/932] Minor changes
---
concepts/numbers/about.md | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 1ea487843fc..2c3863aa58d 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -31,7 +31,7 @@ Python fully supports arithmetic between these different number types, and will
### Addition and subtraction
Addition and subtraction act like in normal math.
-If one of the operands is a `float`, the other will be converted to a `float` as well.
+If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
Otherwise both operands will be converted to `ints`:
```python
@@ -86,6 +86,9 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
```python
>>> 5 % 3
2
+
+>>> 8 % 2
+0
```
### Exponentiation
@@ -95,13 +98,16 @@ Exponentiation is performed using the `**` operator:
```python
>>> 2 ** 3
8
+
+>>> 4 ** 0.5
+2
```
All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
## Conversions
-Numbers can be converted from one type to another using the built-in functions `int()` and `float()`:
+Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
```python
>>> int(3.45)
@@ -138,7 +144,7 @@ This is useful when you want to override the default order of operations.
```
Python follows the [PEMDAS][operator precedence] rule for operator precedence.
-This means `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
+This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
```python
>>> 2 + 3 - 4 * 4
From 37ed22a9519e669d51a9174fb3581f986be30f9c Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:22:27 +0100
Subject: [PATCH 369/932] Fix config
---
config.json | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/config.json b/config.json
index d1399f2f3fd..a96132bd475 100644
--- a/config.json
+++ b/config.json
@@ -43,9 +43,9 @@
"status": "beta"
},
{
- "slug": "currency-exchange",
- "name": "Currency Exchange",
- "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
+ "slug": "electric-bill",
+ "name": "Electric Bill",
+ "uuid": "b2fd556b-07a8-47d6-9811-4d847cf0c6da",
"concepts": ["numbers"],
"prerequisites": ["basics"],
"status": "beta"
@@ -2103,6 +2103,14 @@
],
"difficulty": 9
},
+ {
+ "slug": "currency-exchange",
+ "name": "Currency Exchange",
+ "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
+ "concepts": [],
+ "prerequisites": [],
+ "status": "deprecated"
+ },
{
"slug": "accumulate",
"name": "Accumulate",
From 4de9c1b89663e80516c0e7f532b03b1bc8e830a7 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:25:24 +0100
Subject: [PATCH 370/932] Fixed
---
config.json | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/config.json b/config.json
index a96132bd475..e292ece5b7d 100644
--- a/config.json
+++ b/config.json
@@ -50,6 +50,14 @@
"prerequisites": ["basics"],
"status": "beta"
},
+ {
+ "slug": "currency-exchange",
+ "name": "Currency Exchange",
+ "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
+ "concepts": [],
+ "prerequisites": [],
+ "status": "wip"
+ },
{
"slug": "meltdown-mitigation",
"name": "Meltdown Mitigation",
@@ -2103,14 +2111,6 @@
],
"difficulty": 9
},
- {
- "slug": "currency-exchange",
- "name": "Currency Exchange",
- "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": [],
- "prerequisites": [],
- "status": "deprecated"
- },
{
"slug": "accumulate",
"name": "Accumulate",
From 8cf33d44f5f2b35f69bf8580a71212702ad269ac Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:27:24 +0100
Subject: [PATCH 371/932] Added introduction
---
exercises/concept/electric-bill/.docs/hint.md | 1 +
.../electric-bill/.docs/introduction.md | 179 ++++++++++++++++++
2 files changed, 180 insertions(+)
create mode 100644 exercises/concept/electric-bill/.docs/hint.md
create mode 100644 exercises/concept/electric-bill/.docs/introduction.md
diff --git a/exercises/concept/electric-bill/.docs/hint.md b/exercises/concept/electric-bill/.docs/hint.md
new file mode 100644
index 00000000000..1f600ada684
--- /dev/null
+++ b/exercises/concept/electric-bill/.docs/hint.md
@@ -0,0 +1 @@
+# Todo
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
new file mode 100644
index 00000000000..2c3863aa58d
--- /dev/null
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -0,0 +1,179 @@
+# About
+
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]). Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
+
+Whole numbers (_including hex, octals and binary numbers_) **without** decimal places are identified as `ints`:
+
+```python
+# Ints are whole numbers.
+>>> 1234
+1234
+>>> type(1234)
+
+
+>>> -12
+-12
+```
+
+Numbers containing a decimal point (with or without fractional parts) are identified as `floats`:
+
+```python
+>>> 3.45
+3.45
+>>> type(3.45)
+
+```
+
+## Arithmetic
+
+Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
+### Addition and subtraction
+
+Addition and subtraction act like in normal math.
+If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
+Otherwise both operands will be converted to `ints`:
+
+```python
+>>> 5 - 3
+2
+# The int is widened to a float here, and a float is returned.
+>>> 3 + 4.0
+7.0
+```
+
+### Multiplication
+
+As with addition and subtraction, multiplication will convert narrower numbers to match their less narrow counterparts:
+
+```python
+>>> 3 * 2
+6
+
+>>> 3 * 2.0
+6.0
+```
+
+### Division
+
+Division always returns a `float`, even if the result is a whole number:
+
+```python
+>>> 6/5
+1.2
+
+>>> 6/2
+3.0
+```
+
+### Floor division
+
+If an `int` result is needed, you can use floor division to truncate the result.
+Floor division is performed using the `//` operator:
+
+```python
+>>> 6//5
+1
+
+>>> 6//2
+3
+```
+
+### Modulo
+
+The modulo operator (`%`) returns the remainder of the division of the two operands:
+
+```python
+>>> 5 % 3
+2
+
+>>> 8 % 2
+0
+```
+
+### Exponentiation
+
+Exponentiation is performed using the `**` operator:
+
+```python
+>>> 2 ** 3
+8
+
+>>> 4 ** 0.5
+2
+```
+
+All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
+
+## Conversions
+
+Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
+
+```python
+>>> int(3.45)
+3
+
+>>> float(3)
+3.0
+```
+
+## Round
+
+Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
+If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
+
+```python
+>>> round(3.1415926535, 2)
+3.14
+
+>>> round(3.1415926535)
+3
+```
+
+## Priority and parentheses
+
+Python allows you to use parentheses to group expressions.
+This is useful when you want to override the default order of operations.
+
+```python
+>>> 2 + 3 * 4
+14
+
+>>> (2 + 3) * 4
+20
+```
+
+Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
+
+```python
+>>> 2 + 3 - 4 * 4
+-11
+
+>>> (2 + 3 - 4) * 4
+20
+
+# In the following example, the `**` operator has the highest priority, then `*`, then `+`
+# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
+>>> 2 + 3 * 4 ** 4
+770
+```
+
+## Precision & Representation
+
+Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
+
+Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
+
+For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
+
+[int]: https://docs.python.org/3/library/functions.html#int
+[float]: https://docs.python.org/3/library/functions.html#float
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[fractions]: https://docs.python.org/3/library/fractions.html
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[0.30000000000000004.com]: https://0.30000000000000004.com/
+[cmath]: https://docs.python.org/3.9/library/cmath.html
+[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
From 2172e42befaccd2f72b1c6aa4264b5c03acf1c4f Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:29:15 +0100
Subject: [PATCH 372/932] Fix
---
config.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/config.json b/config.json
index e292ece5b7d..c7e5b9e080f 100644
--- a/config.json
+++ b/config.json
@@ -54,8 +54,8 @@
"slug": "currency-exchange",
"name": "Currency Exchange",
"uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": [],
- "prerequisites": [],
+ "concepts": ["numbers"],
+ "prerequisites": ["basics"],
"status": "wip"
},
{
From 68ec0e47665325ea3a39c83075c94d9ef8036782 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:30:56 +0100
Subject: [PATCH 373/932] fix
---
exercises/concept/electric-bill/.docs/{hint.md => hints.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename exercises/concept/electric-bill/.docs/{hint.md => hints.md} (100%)
diff --git a/exercises/concept/electric-bill/.docs/hint.md b/exercises/concept/electric-bill/.docs/hints.md
similarity index 100%
rename from exercises/concept/electric-bill/.docs/hint.md
rename to exercises/concept/electric-bill/.docs/hints.md
From 33fc34c77fbc61de4092035fecf1911d59e8bdff Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:10:44 -0800
Subject: [PATCH 374/932] Small Edits for Modulo
---
concepts/numbers/about.md | 62 ++++++++++++++++++++++++++++-----------
1 file changed, 45 insertions(+), 17 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 2c3863aa58d..05f214825b4 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -1,8 +1,9 @@
# About
-Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]). Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]).
+Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
-Whole numbers (_including hex, octals and binary numbers_) **without** decimal places are identified as `ints`:
+Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`:
```python
# Ints are whole numbers.
@@ -28,11 +29,11 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
### Addition and subtraction
-Addition and subtraction act like in normal math.
-If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
-Otherwise both operands will be converted to `ints`:
+Addition and subtraction operators behave as they do in normal math.
+If one or more of the operands is a `float`, the remaining `int`s will be converted to `float`s as well:
```python
>>> 5 - 3
@@ -84,11 +85,29 @@ Floor division is performed using the `//` operator:
The modulo operator (`%`) returns the remainder of the division of the two operands:
```python
+# The result of % is zero here, because dividing 8 by 2 leaves no remainder
+>>> 8 % 2
+0
+
+
>>> 5 % 3
2
+```
+
+Which is equivalent to:
+
+
+```python
+>>> whole_part = int(5/3)
+1
+
+>>> decimal_part = 5/3 - whole_part
+0.6666666666666667
+
+>>> whole_remainder = decimal_part * 3
+2.0
+```
->>> 8 % 2
-0
```
### Exponentiation
@@ -103,12 +122,15 @@ Exponentiation is performed using the `**` operator:
2
```
-All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
+All numbers (except complex) support all [arithmetic operations][arithmetic-operations], evaluated according to [operator precedence][operator precedence].
+Support for mathematical functions (beyond `+` and `-`) for complex numbers can be found in the [cmath][cmath] module.
+
## Conversions
Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
+
```python
>>> int(3.45)
3
@@ -117,9 +139,10 @@ Numbers can be converted from `int` to `floats` and `floats` to `int` using the
3.0
```
+
## Round
-Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
+Python provides a built-in function [`round(number, )`][round] to round off a floating point number to a given number of decimal places.
If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
```python
@@ -163,17 +186,22 @@ This means calculations within `()` have the highest priority, followed by `**`,
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
-Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
+Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
+Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
-[int]: https://docs.python.org/3/library/functions.html#int
-[float]: https://docs.python.org/3/library/functions.html#float
-[complex]: https://docs.python.org/3/library/functions.html#complex
-[fractions]: https://docs.python.org/3/library/fractions.html
-[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[cmath]: https://docs.python.org/3.9/library/cmath.html
[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
-[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[bin]: https://docs.python.org/3/library/functions.html#bin
+[cmath]: https://docs.python.org/3.9/library/cmath.html
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[float]: https://docs.python.org/3/library/functions.html#float
[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
+[fractions]: https://docs.python.org/3/library/fractions.html
+[hex]: https://docs.python.org/3/library/functions.html#hex
+[int]: https://docs.python.org/3/library/functions.html#int
+[oct]: https://docs.python.org/3/library/functions.html#oct
+[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[round]: https://docs.python.org/3/library/functions.html#round
From 7b7ec005e52b16b33c32d06e7feaa56a40c96e84 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:22:38 -0800
Subject: [PATCH 375/932] More Modulo
---
concepts/numbers/about.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 05f214825b4..66eb14801d8 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -89,12 +89,12 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
>>> 8 % 2
0
-
+# The result of % is 2 here, because 3 only goes into 5 once, with 2 left over
>>> 5 % 3
2
```
-Which is equivalent to:
+Another way to look at 5 % 3:
```python
@@ -108,7 +108,6 @@ Which is equivalent to:
2.0
```
-```
### Exponentiation
From c8e5a7572fe57ed3d94585b136afc04d42ca5df0 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:40:15 -0800
Subject: [PATCH 376/932] added links
---
concepts/numbers/about.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 66eb14801d8..659d63cb8c9 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -165,7 +165,7 @@ This is useful when you want to override the default order of operations.
20
```
-Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+Python follows the [PEMDAS][pemdas] rule for operator precedence.
This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
```python
@@ -191,7 +191,8 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
[bin]: https://docs.python.org/3/library/functions.html#bin
[cmath]: https://docs.python.org/3.9/library/cmath.html
[complex]: https://docs.python.org/3/library/functions.html#complex
@@ -203,4 +204,4 @@ For a more detailed discussions of the issues and limitations of floating point
[int]: https://docs.python.org/3/library/functions.html#int
[oct]: https://docs.python.org/3/library/functions.html#oct
[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
-[round]: https://docs.python.org/3/library/functions.html#round
+[round]: https://docs.python.org/3/library/functions.html#round
\ No newline at end of file
From 36ad472fea996dd14995622d039c9bbfa5cd2920 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 21:41:36 +0100
Subject: [PATCH 377/932] Fix
---
concepts/numbers/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json
index 583a8284a81..25c2db79814 100644
--- a/concepts/numbers/.meta/config.json
+++ b/concepts/numbers/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "There are three different types of built-in numbers: integers (\"int\"), floating-point (\"float\"), and complex (\"complex\"). Ints have arbitrary precision and floats typically have 15 decimal places of precision -- but both Int and float precision vary by host system. Complex numbers have a real and an imaginary part - each represented by floats.",
"authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007"],
- "contributors": ["BethanyG", "KaiAragaki"]
+ "contributors": ["BethanyG", "KaiAragaki", "meatball133"]
}
From 13ee826acd1f7815577d2af7a38b8defb677d3f5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 21:48:22 +0100
Subject: [PATCH 378/932] Removed finished code
---
exercises/concept/electric-bill/electric_bill.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index e7e90c59c60..0c2938d2dd8 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -7,7 +7,7 @@ def get_the_amount_of_hours(hours):
:param: hours: int - amount of hours.
:return: int - amount of hours.
"""
- return (hours + 3) % 24
+ pass
def get_kW_value(watts):
@@ -16,7 +16,7 @@ def get_kW_value(watts):
:param: watts: int - watt value.
:return: float - kW value.
"""
- return round(watts / 1000, 1) # rounds here
+ pass
def get_kwh_value(watts):
@@ -25,7 +25,7 @@ def get_kwh_value(watts):
:param: watts: int - watt value.
:param: hours: int - kilowatt hour value.
"""
- return int(get_kW_value(watts) // 3600)
+ pass
def get_efficiency(efficiency):
@@ -34,7 +34,7 @@ def get_efficiency(efficiency):
:param: efficiency: float - efficiency.
:return: float - efficiency.
"""
- return efficiency / 100
+ pass
def get_price_of_kwh(watts, efficiency, price):
@@ -45,4 +45,4 @@ def get_price_of_kwh(watts, efficiency, price):
:param: price: float - price of kWh.
:return: float - price of kWh.
"""
- return price * (get_kwh_value(watts) / get_efficiency(efficiency))
+ pass
From f03898b0d2235134f63deccf0ff19fb3aaf78277 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:54:54 -0800
Subject: [PATCH 379/932] Introduction files Edits
Edited concept and exercise introduction.md files.
---
concepts/numbers/introduction.md | 97 +------------------
.../electric-bill/.docs/introduction.md | 71 +++++++-------
2 files changed, 38 insertions(+), 130 deletions(-)
diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md
index c72139289a7..aad55a0e61b 100644
--- a/concepts/numbers/introduction.md
+++ b/concepts/numbers/introduction.md
@@ -1,97 +1,8 @@
# Introduction
-## Numbers
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]).
+Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
-There are three different kinds of built-in numbers in Python : `ints`, `floats`, and `complex`. However, in this exercise you'll be dealing only with `ints` and `floats`.
+Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`.
-### ints
-
-`ints` are whole numbers. e.g. `1234`, `-10`, `20201278`.
-
-Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system.
-
-### floats
-
-`floats` or `floating point numbers` contain a decimal point. e.g. `0.0`,`3.14`,`-9.01`.
-
-Floating point numbers are usually implemented in Python using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system and other implementation details. This can create some surprises when working with floats, but is "good enough" for most situations.
-
-You can see more details and discussions in the following resources:
-
-- [Python numeric type documentation][numeric-type-docs]
-- [The Python Tutorial][floating point math]
-- [Documentation for `int()` built in][`int()` built in]
-- [Documentation for `float()` built in][`float()` built in]
-- [0.30000000000000004.com][0.30000000000000004.com]
-
-### Precision
-
-Before diving into arithmetic, it is worth thinking about what precision means. Precision is the level of exactness at which a number can be represented. An `int` is less precise than a `float` in the same way that `1` is less precise than `1.125`.
-
-## Arithmetic
-
-Python fully supports arithmetic between `ints` and `floats`. It will convert narrower numbers to match their wider (or more precise) counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`). When division with `/`, `//` returns the quotient and `%` returns the remainder.
-
-Python considers `ints` narrower than `floats`. So, using a float in an expression ensures the result will be a float too. However, when doing division, the result will always be a float, even if only integers are used.
-
-```python
-# The int is widened to a float here, and a float type is returned.
->>> 3 + 4.0
-7.0
->>> 3 * 4.0
-12.0
->>> 3 - 2.0
-1.0
-# Division always returns a float.
->>> 6 / 2
-3.0
->>> 7 / 4
-1.75
-# Calculating remainders.
->>> 7 % 4
-3
->>> 2 % 4
-2
->>> 12.75 % 3
-0.75
-```
-
-If an int result is needed, you can use `//` to truncate the result.
-
-```python
->>> 6 // 2
-3
->>> 7 // 4
-1
-```
-
-To convert a float to an integer, you can use `int()`. Also, to convert an integer to a float, you can use `float()`.
-
-```python
->>> int(6 / 2)
-3
->>> float(1 + 2)
-3.0
-```
-
-## Underscores in Numeric Literals
-
-As of version 3.6, Python supports the use of underscores in numerical literals to improve readability:
-```python
-# A float with underscores
->>> dollars = 35_000_000.0
->>> print(dollars)
-35000000.0
-```
-
-The rules for underscores are outline in [pep 515][pep 515] under 'Literal Grammar' are quite dense, but essentially boil down to:
-* Underscores can only be between two digits (not at beginning or ends of numbers, or next to signs (+/-) or decimals points)
-* No consecutive underscores
-
-[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic#:~:text=In%20computer%20science%2C%20arbitrary%2Dprecision,memory%20of%20the%20host%20system.
-[numeric-type-docs]: https://docs.python.org/3/library/stdtypes.html#typesnumeric
-[`int()` built in]: https://docs.python.org/3/library/functions.html#int
-[`float()` built in]: https://docs.python.org/3/library/functions.html#float
-[0.30000000000000004.com]: https://0.30000000000000004.com/
-[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
-[pep 515]: https://www.python.org/dev/peps/pep-0515/
+Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 2c3863aa58d..c3187bbe7fa 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -1,8 +1,9 @@
-# About
+# Introduction
-Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]). Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]).
+Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
-Whole numbers (_including hex, octals and binary numbers_) **without** decimal places are identified as `ints`:
+Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`:
```python
# Ints are whole numbers.
@@ -28,11 +29,11 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
### Addition and subtraction
-Addition and subtraction act like in normal math.
-If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
-Otherwise both operands will be converted to `ints`:
+Addition and subtraction operators behave as they do in normal math.
+If one or more of the operands is a `float`, the remaining `int`s will be converted to `float`s as well:
```python
>>> 5 - 3
@@ -84,42 +85,32 @@ Floor division is performed using the `//` operator:
The modulo operator (`%`) returns the remainder of the division of the two operands:
```python
->>> 5 % 3
-2
-
+# The result of % is zero here, because dividing 8 by 2 leaves no remainder
>>> 8 % 2
0
-```
-
-### Exponentiation
-
-Exponentiation is performed using the `**` operator:
-```python
->>> 2 ** 3
-8
-
->>> 4 ** 0.5
+# The result of % is 2 here, because 3 only goes into 5 once, with 2 left over
+>>> 5 % 3
2
```
-All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
-
-## Conversions
+Another way to look at 5 % 3:
-Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
```python
->>> int(3.45)
-3
+>>> whole_part = int(5/3)
+1
->>> float(3)
-3.0
+>>> decimal_part = 5/3 - whole_part
+0.6666666666666667
+
+>>> whole_remainder = decimal_part * 3
+2.0
```
## Round
-Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
+Python provides a built-in function [`round(number, )`][round] to round off a floating point number to a given number of decimal places.
If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
```python
@@ -143,7 +134,7 @@ This is useful when you want to override the default order of operations.
20
```
-Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+Python follows the [PEMDAS][pemdas] rule for operator precedence.
This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
```python
@@ -163,17 +154,23 @@ This means calculations within `()` have the highest priority, followed by `**`,
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
-Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
+Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
+Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
-[int]: https://docs.python.org/3/library/functions.html#int
-[float]: https://docs.python.org/3/library/functions.html#float
-[complex]: https://docs.python.org/3/library/functions.html#complex
-[fractions]: https://docs.python.org/3/library/fractions.html
-[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
[0.30000000000000004.com]: https://0.30000000000000004.com/
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[bin]: https://docs.python.org/3/library/functions.html#bin
[cmath]: https://docs.python.org/3.9/library/cmath.html
-[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
-[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[float]: https://docs.python.org/3/library/functions.html#float
[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
+[fractions]: https://docs.python.org/3/library/fractions.html
+[hex]: https://docs.python.org/3/library/functions.html#hex
+[int]: https://docs.python.org/3/library/functions.html#int
+[oct]: https://docs.python.org/3/library/functions.html#oct
+[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[round]: https://docs.python.org/3/library/functions.html#round
From f0195b3175ede1d98ad08930d65f79b9fa4bf90b Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 13:05:24 -0800
Subject: [PATCH 380/932] Link Adjustmets
---
concepts/numbers/about.md | 7 ++++---
concepts/numbers/introduction.md | 10 ++++++++++
exercises/concept/electric-bill/.docs/introduction.md | 8 +++-----
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 659d63cb8c9..874e600edc9 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -183,7 +183,7 @@ This means calculations within `()` have the highest priority, followed by `**`,
## Precision & Representation
-Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
+Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system.
Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
@@ -191,7 +191,7 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
[bin]: https://docs.python.org/3/library/functions.html#bin
[cmath]: https://docs.python.org/3.9/library/cmath.html
@@ -204,4 +204,5 @@ For a more detailed discussions of the issues and limitations of floating point
[int]: https://docs.python.org/3/library/functions.html#int
[oct]: https://docs.python.org/3/library/functions.html#oct
[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
-[round]: https://docs.python.org/3/library/functions.html#round
\ No newline at end of file
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[round]: https://docs.python.org/3/library/functions.html#round
diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md
index aad55a0e61b..3491bc20a3c 100644
--- a/concepts/numbers/introduction.md
+++ b/concepts/numbers/introduction.md
@@ -6,3 +6,13 @@ Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][
Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`.
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
+
+[bin]: https://docs.python.org/3/library/functions.html#bin
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[float]: https://docs.python.org/3/library/functions.html#float
+[fractions]: https://docs.python.org/3/library/fractions.html
+[hex]: https://docs.python.org/3/library/functions.html#hex
+[int]: https://docs.python.org/3/library/functions.html#int
+[oct]: https://docs.python.org/3/library/functions.html#oct
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index c3187bbe7fa..5b6f2d19da3 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -149,10 +149,11 @@ This means calculations within `()` have the highest priority, followed by `**`,
>>> 2 + 3 * 4 ** 4
770
```
+[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
## Precision & Representation
-Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
+Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system.
Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
@@ -160,10 +161,7 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
-[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
[bin]: https://docs.python.org/3/library/functions.html#bin
-[cmath]: https://docs.python.org/3.9/library/cmath.html
[complex]: https://docs.python.org/3/library/functions.html#complex
[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
[float]: https://docs.python.org/3/library/functions.html#float
@@ -172,5 +170,5 @@ For a more detailed discussions of the issues and limitations of floating point
[hex]: https://docs.python.org/3/library/functions.html#hex
[int]: https://docs.python.org/3/library/functions.html#int
[oct]: https://docs.python.org/3/library/functions.html#oct
-[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
[round]: https://docs.python.org/3/library/functions.html#round
From e1d6ff256ff862d83d615542b9f81acfdf8dc58d Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 22:07:49 +0100
Subject: [PATCH 381/932] Smal changes
---
concepts/numbers/about.md | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 874e600edc9..1f894b0aa84 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -29,6 +29,8 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+All numbers (except complex) support all [arithmetic operations][arithmetic-operations], evaluated according to [operator precedence][operator precedence].
+Support for mathematical functions (beyond `+` and `-`) for complex numbers can be found in the [cmath][cmath] module.
### Addition and subtraction
@@ -96,7 +98,6 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
Another way to look at 5 % 3:
-
```python
>>> whole_part = int(5/3)
1
@@ -108,8 +109,7 @@ Another way to look at 5 % 3:
2.0
```
-
-### Exponentiation
+## Exponentiation
Exponentiation is performed using the `**` operator:
@@ -121,15 +121,10 @@ Exponentiation is performed using the `**` operator:
2
```
-All numbers (except complex) support all [arithmetic operations][arithmetic-operations], evaluated according to [operator precedence][operator precedence].
-Support for mathematical functions (beyond `+` and `-`) for complex numbers can be found in the [cmath][cmath] module.
-
-
## Conversions
Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
-
```python
>>> int(3.45)
3
@@ -138,7 +133,6 @@ Numbers can be converted from `int` to `floats` and `floats` to `int` using the
3.0
```
-
## Round
Python provides a built-in function [`round(number, )`][round] to round off a floating point number to a given number of decimal places.
From 065ef79030d76a5d9f5e2ee0fc5b29f0a9eb89be Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 22:13:30 +0100
Subject: [PATCH 382/932] Fixed capital
---
concepts/numbers/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json
index 25c2db79814..7898f6099aa 100644
--- a/concepts/numbers/.meta/config.json
+++ b/concepts/numbers/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "There are three different types of built-in numbers: integers (\"int\"), floating-point (\"float\"), and complex (\"complex\"). Ints have arbitrary precision and floats typically have 15 decimal places of precision -- but both Int and float precision vary by host system. Complex numbers have a real and an imaginary part - each represented by floats.",
+ "blurb": "There are three different types of built-in numbers: integers (\"int\"), floating-point (\"float\"), and complex (\"complex\"). Ints have arbitrary precision and floats typically have 15 decimal places of precision -- but both int and float precision vary by host system. Complex numbers have a real and an imaginary part - each represented by floats.",
"authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007"],
"contributors": ["BethanyG", "KaiAragaki", "meatball133"]
}
From 036ad99247757d2024f7c3ac1e30919f5053c0bf Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 22:24:29 +0100
Subject: [PATCH 383/932] Fixes
---
exercises/concept/electric-bill/.docs/introduction.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 5b6f2d19da3..1543afe5d5d 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -29,7 +29,6 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
-
### Addition and subtraction
Addition and subtraction operators behave as they do in normal math.
@@ -96,7 +95,6 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
Another way to look at 5 % 3:
-
```python
>>> whole_part = int(5/3)
1
@@ -149,7 +147,6 @@ This means calculations within `()` have the highest priority, followed by `**`,
>>> 2 + 3 * 4 ** 4
770
```
-[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
## Precision & Representation
@@ -161,6 +158,7 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
+[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
[bin]: https://docs.python.org/3/library/functions.html#bin
[complex]: https://docs.python.org/3/library/functions.html#complex
[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
From a170b492a9d4bcba4c1fd78e6e5124512bbade95 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 14:13:35 -0800
Subject: [PATCH 384/932] Hints File and Edits
---
.../concept/electric-bill/.docs/hints.md | 28 ++++++++++++-
.../electric-bill/.docs/instructions.md | 42 +++++++++----------
.../concept/electric-bill/.meta/config.json | 2 +-
.../concept/electric-bill/.meta/exemplar.py | 29 +++++++------
.../concept/electric-bill/electric_bill.py | 18 ++++----
.../electric-bill/electric_bill_test.py | 12 +++---
6 files changed, 80 insertions(+), 51 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/hints.md b/exercises/concept/electric-bill/.docs/hints.md
index 1f600ada684..77aaa50d9c3 100644
--- a/exercises/concept/electric-bill/.docs/hints.md
+++ b/exercises/concept/electric-bill/.docs/hints.md
@@ -1 +1,27 @@
-# Todo
+# General
+
+Remember that you can always reuse/call previously completed functions when writing new ones.
+
+
+## 1. Get extra hours
+- This is all about calculating the _remainder_ left after whole division.
+- Take a look at [`divmod()`][divmod], and look for an operator that does something similar.
+
+## 2. Get kW value
+- Remember to give [`round()`][round] a number of _decimal places_, or you will get a whole number back as a result.
+
+## 3. Get kwh value
+- The result of dividing an `int` by a `float` is always a `float`.
+- To get only an integer value from division, use [_floor_ division][floor], which will truncate the decimal.
+
+## 4. Get efficiency
+- The result of dividing an `int` by a `float` is always a `float`.
+
+## 5. Get cost
+- It might be good to _reuse_ or call other functions you have already completed here.
+- The result of dividing an `int` by a `float` is always a `float`.
+
+
+[divmod]: https://docs.python.org/3/library/functions.html#divmod
+[floor]: https://docs.python.org/3/glossary.html#term-floor-division
+[round]: https://docs.python.org/3/library/functions.html#round
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index ee81c2b23f6..9c96ee924fd 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -1,17 +1,18 @@
# Instructions
-The company you work for want to reduce their carbon footprint, so they wants you to write a program which calculates the power usage of their electronics and the cost of running them.
+The company you work for wants to reduce their carbon footprint, so they want you to write a program to calculate the power usage and cost of running their electronics.
1. Get extra hours
-Your employer wants a program that calculates the time it takes to run different electronics.
-Currently the time is stored in hours.
-When your employer added the hours they noticed that the time was not correct and they want you to add 3 extra hours to the time data.
-They also would like to know how many hours needs to be removed to get the data in full days(24 hours).
-The time given doesn't have to be in full days.
+Your employer has a program that calculates the time it takes to run different electronics.
+Currently, the time is stored in hours.
+When your employer added the hours, they noticed that the time duration was not correct.
+They want you to add 3 extra hours to the time data.
+They would also like to know how many "extra" hours there are after converting the data to "full" days (a day is 24 hours).
+The time to convert may not be in full days.
Implement a function `get_extra_hours()` that accepts an integer which holds the number of hours.
-The function should then `return` an integer with how many hours which has to be removed to get the time in full days.
+The function should make the appropriate "extra hours" adjustment, and then `return` an integer representing how many hours needs to be removed from the total to get the time in "full" days.
```python
>>> get_extra_hours(25)
@@ -20,14 +21,13 @@ The function should then `return` an integer with how many hours which has to be
2. Get kW value
-Your employer wants to know the power usage of the different electronics.
-They want to know the power usage in kW.
-kW stands for kilowatt, there watts is a unit of power.
+Your employer wants to know the power usage of the different electronics in kW.
+kW stands for kilowatt, where watts are a unit of power.
Kilo in the unit name is a prefix in the metric system meaning 1000.
-So 1 kilowatt is equal to 1000 watts.
+One kilowatt == 1000 watts.
Implement a function `get_kW_value()` that accepts an integer which holds the number of watts.
-The function should then `return` the watts as kilowatts rounded to 1 decimal.
+The function should then `return` the watts as kilowatts rounded to 1 decimal place.
```python
>>> get_kW_value(1150)
@@ -36,12 +36,12 @@ The function should then `return` the watts as kilowatts rounded to 1 decimal.
3. Get kwh value
-To be able to calculate the cost of running the electronics your employer wants to know the power usage in kWh.
-kWh stands for kilowatt-hour, there hour is a unit of time.
-So 1 kilowatt-hour is equal to 1000 watts used for 1 hour.
-An hour is made of 60 minutes and a minute is made of 60 seconds.
-So 1 hour is equal to 3600 seconds.
-To get the kWh value you have to have to convert the watts to kW and then floor-divide it by 3600.
+To be able to calculate the cost of running the electronics, your employer needs to know the power usage in kWh.
+kWh stands for kilowatt-hour, where hour is a unit of time.
+One kilowatt-hour == 1000 watts used for 1 hour.
+An hour is made up of 60 minutes and a minute is made up of 60 seconds.
+One hour is equal to 3600 seconds.
+To calculate the kWh value, you must convert watts to kW, and then floor-divide the result by 3600.
Implement a function `get_kWh_value()` that accepts an integer which holds the number of watts.
The function should then `return` the watts as an integer.
@@ -54,8 +54,8 @@ The function should then `return` the watts as an integer.
4. Get efficiency
Electronics are not 100% efficient.
-Therefore, your employer wants you to calculate the efficiency of the electronics.
-To get efficiency you have to divide the power factor (a float between 0 and 100) by 100.
+Therefore, your employer wants you to calculate the _efficiency_ of the electronics.
+To get efficiency, you must divide the power factor (_a float between 0 and 100_) by 100.
Implement a function `get_efficiency()` that accepts a float that holds the power factor.
The function should then `return` the calculated efficiency as a float.
@@ -71,7 +71,7 @@ Your employer wants to know the cost of running the electronics.
The cost of running the electronics is the power used multiplied by the cost per kWh.
The power used is the power given divided by the calculated efficiency.
-Implement a function `get_cost()` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
+Implement a function `get_cost(,,)` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
The function should then `return` the cost of running the electronics as a float.
```python
diff --git a/exercises/concept/electric-bill/.meta/config.json b/exercises/concept/electric-bill/.meta/config.json
index d1560b2033e..eca6e91c08b 100644
--- a/exercises/concept/electric-bill/.meta/config.json
+++ b/exercises/concept/electric-bill/.meta/config.json
@@ -15,5 +15,5 @@
]
},
"icon": "city-office",
- "blurb": "to do"
+ "blurb": "Learn about numbers in Python while saving your employers money on their electric bill."
}
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 7102c5d6c1d..230f892519e 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -1,12 +1,13 @@
-"""Functions which helps company to calculate their power usage"""
+"""Functions to help the company calculate their power usage."""
-def get_the_amount_of_hours(hours):
+def get_extra_hours(hours):
"""Return the amount of hours.
:param: hours: int - amount of hours.
- :return: int - amount of hours.
+ :return: int - amount of "extra" hours.
"""
+
return (hours + 3) % 24
@@ -16,7 +17,9 @@ def get_kW_value(watts):
:param: watts: int - watt value.
:return: float - kW value.
"""
- return round(watts / 1000, 1) # rounds here
+
+ # rounds to one decimal place here
+ return round(watts / 1000, 1)
def get_kwh_value(watts):
@@ -28,21 +31,21 @@ def get_kwh_value(watts):
return get_kW_value(watts) // 3600
-def get_efficiency(efficiency):
- """Return the efficiency as a power factor.
+def get_efficiency(power_factor):
+ """Return the efficiency calculated from the power factor.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float.
:return: float - efficiency.
"""
- return efficiency / 100
+ return power_factor / 100
-def get_price_of_kwh(watts, efficiency, price):
- """Return the price of a given kWh value, efficiency and price.
+def get_cost(watts, power_factor, price):
+ """Calculate the cost of a given kWh value, efficiency and price.
:param: watts: int - watt value.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float - efficiency.
:param: price: float - price of kWh.
- :return: float - price of kWh.
+ :return: float - cost of kWh.
"""
- return price * (get_kwh_value(watts) / get_efficiency(efficiency))
+ return price * (get_kwh_value(watts) / get_efficiency(power_factor))
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index 0c2938d2dd8..5a3ba53973c 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -1,11 +1,11 @@
"""Functions which helps company to calculate their power usage"""
-def get_the_amount_of_hours(hours):
+def get_extra_hours(hours):
"""Return the amount of hours.
:param: hours: int - amount of hours.
- :return: int - amount of hours.
+ :return: int - amount of "extra" hours.
"""
pass
@@ -28,21 +28,21 @@ def get_kwh_value(watts):
pass
-def get_efficiency(efficiency):
- """Return the efficiency as a power factor.
+def get_efficiency(power_factor):
+ """Return the efficiency calculated from the power factor.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float.
:return: float - efficiency.
"""
pass
-def get_price_of_kwh(watts, efficiency, price):
- """Return the price of a given kWh value, efficiency and price.
+def get_cost(watts, power_factor, price):
+ """Calculate the cost of a given kWh value, efficiency and price.
:param: watts: int - watt value.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float - efficiency.
:param: price: float - price of kWh.
- :return: float - price of kWh.
+ :return: float - cost of kWh.
"""
pass
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index 95745e74a8d..fdcfe32064c 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -1,23 +1,23 @@
import unittest
import pytest
-from electric_bill import (get_the_amount_of_hours,
+from electric_bill import (get_extra_hours,
get_kW_value,
get_kwh_value,
get_efficiency,
- get_price_of_kwh)
+ get_cost)
class LocomotiveEngineerTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
- def test_get_the_amount_of_hours(self):
+ def test_get_extra_hours(self):
input_data = [25, 10, 5, 2, 1, 120, 21]
output_data = [4, 13, 8, 5, 4, 3, 0]
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 value.'
- self.assertEqual(get_the_amount_of_hours(input_data), output_data, msg=error_msg)
+ self.assertEqual(get_extra_hours(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
def test_get_kW_value(self):
@@ -48,11 +48,11 @@ def test_get_efficiency(self):
self.assertAlmostEqual(get_efficiency(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=5)
- def test_get_price_of_kwh(self):
+ def test_get_cost(self):
input_data = ((5000000, 80.0, 0.25), (2141241, 99.99, 2), (43252135, 0.8, 4), (4321512, 40.0, 2))
output_data = (0.3125, 0, 6000, 5)
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 value.'
- self.assertEqual(get_price_of_kwh(*input_data), output_data, msg=error_msg)
+ self.assertEqual(get_cost(*input_data), output_data, msg=error_msg)
From aa638625f3a7c349c8f5da0cfbd12e448618a4a3 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 25 Dec 2022 23:27:05 +0100
Subject: [PATCH 385/932] Fixes
---
exercises/concept/electric-bill/.docs/hints.md | 17 ++++++++++-------
.../concept/electric-bill/.docs/instructions.md | 6 +++---
.../concept/electric-bill/.docs/introduction.md | 2 +-
.../concept/electric-bill/electric_bill.py | 2 +-
.../concept/electric-bill/electric_bill_test.py | 4 +++-
5 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/hints.md b/exercises/concept/electric-bill/.docs/hints.md
index 77aaa50d9c3..34a6eb7aa4a 100644
--- a/exercises/concept/electric-bill/.docs/hints.md
+++ b/exercises/concept/electric-bill/.docs/hints.md
@@ -2,26 +2,29 @@
Remember that you can always reuse/call previously completed functions when writing new ones.
-
## 1. Get extra hours
+
- This is all about calculating the _remainder_ left after whole division.
- Take a look at [`divmod()`][divmod], and look for an operator that does something similar.
-## 2. Get kW value
+## 2. Get kW value
+
- Remember to give [`round()`][round] a number of _decimal places_, or you will get a whole number back as a result.
-## 3. Get kwh value
+## 3. Get kwh value
+
- The result of dividing an `int` by a `float` is always a `float`.
- To get only an integer value from division, use [_floor_ division][floor], which will truncate the decimal.
## 4. Get efficiency
+
- The result of dividing an `int` by a `float` is always a `float`.
-## 5. Get cost
+## 5. Get cost
+
- It might be good to _reuse_ or call other functions you have already completed here.
- The result of dividing an `int` by a `float` is always a `float`.
-
[divmod]: https://docs.python.org/3/library/functions.html#divmod
-[floor]: https://docs.python.org/3/glossary.html#term-floor-division
-[round]: https://docs.python.org/3/library/functions.html#round
+[floor]: https://docs.python.org/3/glossary.html#term-floor-division
+[round]: https://docs.python.org/3/library/functions.html#round
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index 9c96ee924fd..59aed2919b2 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -34,7 +34,7 @@ The function should then `return` the watts as kilowatts rounded to 1 decimal pl
1.2
```
-3. Get kwh value
+3. Get kWh value
To be able to calculate the cost of running the electronics, your employer needs to know the power usage in kWh.
kWh stands for kilowatt-hour, where hour is a unit of time.
@@ -44,7 +44,7 @@ One hour is equal to 3600 seconds.
To calculate the kWh value, you must convert watts to kW, and then floor-divide the result by 3600.
Implement a function `get_kWh_value()` that accepts an integer which holds the number of watts.
-The function should then `return` the watts as an integer.
+The function should then `return` the kilowatt-hours as an integer.
```python
>>> get_kWh_value(5000000)
@@ -71,7 +71,7 @@ Your employer wants to know the cost of running the electronics.
The cost of running the electronics is the power used multiplied by the cost per kWh.
The power used is the power given divided by the calculated efficiency.
-Implement a function `get_cost(,,)` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
+Implement a function `get_cost(,,)` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kWh.
The function should then `return` the cost of running the electronics as a float.
```python
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 1543afe5d5d..719aea05297 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -88,7 +88,7 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
>>> 8 % 2
0
-# The result of % is 2 here, because 3 only goes into 5 once, with 2 left over
+# The result of % is 2 here, because 3 only goes into 5 once, with 2 leftover
>>> 5 % 3
2
```
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index 5a3ba53973c..2a2dc715fdd 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -1,4 +1,4 @@
-"""Functions which helps company to calculate their power usage"""
+"""Functions to help the company calculate their power usage."""
def get_extra_hours(hours):
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index fdcfe32064c..d5963c988f9 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -23,6 +23,7 @@ def test_get_extra_hours(self):
def test_get_kW_value(self):
input_data = [1000, 2200, 2900, 900, 1160]
output_data = [1, 2.2, 2.9, 0.9, 1.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 value.'
@@ -32,6 +33,7 @@ def test_get_kW_value(self):
def test_get_kwh_value(self):
input_data = (5000000, 2141241, 43252135, 5324623462, 4321512)
output_data = [1, 0, 12, 1479, 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 value.'
@@ -51,7 +53,7 @@ def test_get_efficiency(self):
def test_get_cost(self):
input_data = ((5000000, 80.0, 0.25), (2141241, 99.99, 2), (43252135, 0.8, 4), (4321512, 40.0, 2))
output_data = (0.3125, 0, 6000, 5)
-
+
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 value.'
From 6008ca8da6e8a5d821241319813d5adac9f26dd9 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 26 Dec 2022 00:10:05 +0100
Subject: [PATCH 386/932] Fixes
---
.../concept/electric-bill/.docs/instructions.md | 10 +++++-----
exercises/concept/electric-bill/.meta/design.md | 12 ++++++++++++
2 files changed, 17 insertions(+), 5 deletions(-)
create mode 100644 exercises/concept/electric-bill/.meta/design.md
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index 59aed2919b2..03fa156074c 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -2,7 +2,7 @@
The company you work for wants to reduce their carbon footprint, so they want you to write a program to calculate the power usage and cost of running their electronics.
-1. Get extra hours
+## 1. Get extra hours
Your employer has a program that calculates the time it takes to run different electronics.
Currently, the time is stored in hours.
@@ -19,7 +19,7 @@ The function should make the appropriate "extra hours" adjustment, and then `ret
4
```
-2. Get kW value
+## 2. Get kW value
Your employer wants to know the power usage of the different electronics in kW.
kW stands for kilowatt, where watts are a unit of power.
@@ -34,7 +34,7 @@ The function should then `return` the watts as kilowatts rounded to 1 decimal pl
1.2
```
-3. Get kWh value
+## 3. Get kWh value
To be able to calculate the cost of running the electronics, your employer needs to know the power usage in kWh.
kWh stands for kilowatt-hour, where hour is a unit of time.
@@ -51,7 +51,7 @@ The function should then `return` the kilowatt-hours as an integer.
1
```
-4. Get efficiency
+## 4. Get efficiency
Electronics are not 100% efficient.
Therefore, your employer wants you to calculate the _efficiency_ of the electronics.
@@ -65,7 +65,7 @@ The function should then `return` the calculated efficiency as a float.
0.8
```
-5. Get cost
+## 5. Get cost
Your employer wants to know the cost of running the electronics.
The cost of running the electronics is the power used multiplied by the cost per kWh.
diff --git a/exercises/concept/electric-bill/.meta/design.md b/exercises/concept/electric-bill/.meta/design.md
new file mode 100644
index 00000000000..bc4c298dacf
--- /dev/null
+++ b/exercises/concept/electric-bill/.meta/design.md
@@ -0,0 +1,12 @@
+# Design
+
+## Goal
+
+The goal of this exercise is to teach the student how to use arithmetic operators and type casting between `int` and `float` in Python
+
+## Learning objectives
+
+- use `+`, `-`, `*`, `/` to add, subtract, multiply, divide numbers(`int` and `float`).
+- use `round()` to round values.
+- use `//` to floor divide
+- use `%` to calculate remainders.
From 644f66b3c0ffb13a62c190ccb313fe1ce4230e21 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 01:17:23 +0100
Subject: [PATCH 387/932] depricate exercise
---
config.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/config.json b/config.json
index c7e5b9e080f..575558b764d 100644
--- a/config.json
+++ b/config.json
@@ -54,9 +54,9 @@
"slug": "currency-exchange",
"name": "Currency Exchange",
"uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": ["numbers"],
- "prerequisites": ["basics"],
- "status": "wip"
+ "concepts": [],
+ "prerequisites": [],
+ "status": "deprecated"
},
{
"slug": "meltdown-mitigation",
From 2fe2e6ffc5b0aaf3b242ab4f82ec81d146d359b5 Mon Sep 17 00:00:00 2001
From: Jason Hollis
Date: Sun, 25 Dec 2022 22:53:25 -0500
Subject: [PATCH 388/932] Fixed example with wrong answer
---
exercises/concept/electric-bill/.docs/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 719aea05297..02f56f06c2f 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -140,7 +140,7 @@ This means calculations within `()` have the highest priority, followed by `**`,
-11
>>> (2 + 3 - 4) * 4
-20
+4
# In the following example, the `**` operator has the highest priority, then `*`, then `+`
# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
From 145b291ed49fdc4b1197462665e17c35a1e39af5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:13:19 +0100
Subject: [PATCH 389/932] Fix stuff
---
exercises/concept/electric-bill/.meta/exemplar.py | 2 +-
exercises/concept/electric-bill/electric_bill.py | 2 +-
exercises/concept/electric-bill/electric_bill_test.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 230f892519e..789ffcf395b 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -26,7 +26,7 @@ def get_kwh_value(watts):
"""Return the kWh value of a given watt value and hours.
:param: watts: int - watt value.
- :param: hours: int - kilowatt hour value.
+ :return: int - kilowatt hour value.
"""
return get_kW_value(watts) // 3600
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index 2a2dc715fdd..cae6af33a2b 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -23,7 +23,7 @@ def get_kwh_value(watts):
"""Return the kWh value of a given watt value and hours.
:param: watts: int - watt value.
- :param: hours: int - kilowatt hour value.
+ :return: int - kilowatt hour value.
"""
pass
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index d5963c988f9..c6c1611fec2 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -7,7 +7,7 @@
get_cost)
-class LocomotiveEngineerTest(unittest.TestCase):
+class ElecticBillTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_get_extra_hours(self):
From 9d9cc93b637126365d61fcec8c61cc983e124806 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:16:36 +0100
Subject: [PATCH 390/932] Changed to amount
---
.../concept/electric-bill/.meta/config.json | 1 +
.../concept/electric-bill/.meta/exemplar.py | 16 ++++++++--------
exercises/concept/electric-bill/electric_bill.py | 16 ++++++++--------
.../concept/electric-bill/electric_bill_test.py | 12 ++++++------
4 files changed, 23 insertions(+), 22 deletions(-)
diff --git a/exercises/concept/electric-bill/.meta/config.json b/exercises/concept/electric-bill/.meta/config.json
index eca6e91c08b..752ed40c1a4 100644
--- a/exercises/concept/electric-bill/.meta/config.json
+++ b/exercises/concept/electric-bill/.meta/config.json
@@ -3,6 +3,7 @@
"meatball133",
"BethanyG"
],
+ "contributors": ["MatthijsBlom"],
"files": {
"solution": [
"electric_bill.py"
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 789ffcf395b..1b60339011b 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -11,22 +11,22 @@ def get_extra_hours(hours):
return (hours + 3) % 24
-def get_kW_value(watts):
- """Return the kW value of a given watt value.
+def get_kW_amount(watts):
+ """Return the kW amount of a given watt amount.
- :param: watts: int - watt value.
- :return: float - kW value.
+ :param: watts: int - watt amount.
+ :return: float - kW amount.
"""
# rounds to one decimal place here
return round(watts / 1000, 1)
-def get_kwh_value(watts):
- """Return the kWh value of a given watt value and hours.
+def get_kwh_amount(watts):
+ """Return the kWh amount of a given watt amount and hours.
- :param: watts: int - watt value.
- :return: int - kilowatt hour value.
+ :param: watts: int - watt amount.
+ :return: int - kilowatt hour amount.
"""
return get_kW_value(watts) // 3600
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index cae6af33a2b..52a92865a0f 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -10,20 +10,20 @@ def get_extra_hours(hours):
pass
-def get_kW_value(watts):
- """Return the kW value of a given watt value.
+def get_kW_amount(watts):
+ """Return the kW amount of a given watt amount.
- :param: watts: int - watt value.
- :return: float - kW value.
+ :param: watts: int - watt amount.
+ :return: float - kW amount.
"""
pass
-def get_kwh_value(watts):
- """Return the kWh value of a given watt value and hours.
+def get_kwh_amount(watts):
+ """Return the kWh amount of a given watt amount and hours.
- :param: watts: int - watt value.
- :return: int - kilowatt hour value.
+ :param: watts: int - watt amount.
+ :return: int - kilowatt hour amount.
"""
pass
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index c6c1611fec2..dea3b66c15a 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -1,8 +1,8 @@
import unittest
import pytest
from electric_bill import (get_extra_hours,
- get_kW_value,
- get_kwh_value,
+ get_kW_amount,
+ get_kwh_amount,
get_efficiency,
get_cost)
@@ -20,24 +20,24 @@ def test_get_extra_hours(self):
self.assertEqual(get_extra_hours(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
- def test_get_kW_value(self):
+ def test_get_kW_amount(self):
input_data = [1000, 2200, 2900, 900, 1160]
output_data = [1, 2.2, 2.9, 0.9, 1.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 value.'
- self.assertEqual(get_kW_value(input_data), output_data, msg=error_msg)
+ self.assertEqual(get_kW_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=3)
- def test_get_kwh_value(self):
+ def test_get_kwh_amount(self):
input_data = (5000000, 2141241, 43252135, 5324623462, 4321512)
output_data = [1, 0, 12, 1479, 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 value.'
- self.assertEqual(get_kwh_value(input_data), output_data, msg=error_msg)
+ self.assertEqual(get_kwh_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=4)
def test_get_efficiency(self):
From 9bf352d547d15c505bec44fa82e743d098a89a94 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:19:00 +0100
Subject: [PATCH 391/932] Fix
---
exercises/concept/electric-bill/.meta/exemplar.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 1b60339011b..2a00a39b9b5 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -28,7 +28,7 @@ def get_kwh_amount(watts):
:param: watts: int - watt amount.
:return: int - kilowatt hour amount.
"""
- return get_kW_value(watts) // 3600
+ return get_kW_amount(watts) // 3600
def get_efficiency(power_factor):
@@ -48,4 +48,4 @@ def get_cost(watts, power_factor, price):
:param: price: float - price of kWh.
:return: float - cost of kWh.
"""
- return price * (get_kwh_value(watts) / get_efficiency(power_factor))
+ return price * (get_kwh_amount(watts) / get_efficiency(power_factor))
From b4c2a9f446f353343e867fd05965feb48291459c Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:23:24 +0100
Subject: [PATCH 392/932] fix
---
exercises/concept/electric-bill/electric_bill_test.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index dea3b66c15a..ffbd2fa5a6a 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -16,7 +16,7 @@ def test_get_extra_hours(self):
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 value.'
+ error_msg=f'Expected: {output_data} but got a different amount.'
self.assertEqual(get_extra_hours(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
@@ -26,7 +26,7 @@ def test_get_kW_amount(self):
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 value.'
+ error_msg=f'Expected: {output_data} but got a different amount.'
self.assertEqual(get_kW_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=3)
@@ -36,7 +36,7 @@ def test_get_kwh_amount(self):
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 value.'
+ error_msg=f'Expected: {output_data} but got a different amount.'
self.assertEqual(get_kwh_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=4)
From 71e353a32f281db01eb9d16f05fa03c7d3eaf93e Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 27 Dec 2022 00:42:35 +0100
Subject: [PATCH 393/932] Revert number
---
concepts/numbers/about.md | 2 +-
config.json | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 1f894b0aa84..5171b693545 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -170,7 +170,7 @@ This means calculations within `()` have the highest priority, followed by `**`,
20
# In the following example, the `**` operator has the highest priority, then `*`, then `+`
-# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
+# Meaning we first do 4 ** 4, then 3 * 256, then 2 + 768
>>> 2 + 3 * 4 ** 4
770
```
diff --git a/config.json b/config.json
index 575558b764d..b2222755ebd 100644
--- a/config.json
+++ b/config.json
@@ -46,17 +46,17 @@
"slug": "electric-bill",
"name": "Electric Bill",
"uuid": "b2fd556b-07a8-47d6-9811-4d847cf0c6da",
- "concepts": ["numbers"],
- "prerequisites": ["basics"],
- "status": "beta"
+ "concepts": [],
+ "prerequisites": [],
+ "status": "deprecated"
},
{
"slug": "currency-exchange",
"name": "Currency Exchange",
"uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": [],
- "prerequisites": [],
- "status": "deprecated"
+ "concepts": ["numbers"],
+ "prerequisites": ["basics"],
+ "status": "beta"
},
{
"slug": "meltdown-mitigation",
From 92b57da775356996bc164962307f87ba0fca26f6 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 27 Dec 2022 21:48:54 +0100
Subject: [PATCH 394/932] minor improvments
---
.../practice/dnd-character/.meta/template.j2 | 18 +++++++--------
.../practice/dnd-character/dnd_character.py | 4 ++++
.../dnd-character/dnd_character_test.py | 22 ++++++++-----------
3 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/exercises/practice/dnd-character/.meta/template.j2 b/exercises/practice/dnd-character/.meta/template.j2
index 25642bb7600..473af1609a6 100644
--- a/exercises/practice/dnd-character/.meta/template.j2
+++ b/exercises/practice/dnd-character/.meta/template.j2
@@ -1,9 +1,7 @@
{%- import "generator_macros.j2" as macros with context -%}
-{% set class = exercise | camel_case -%}
{{ macros.header(["Character", "modifier"]) }}
-class {{ exercise | camel_case }}Test(unittest.TestCase):
- {% for supercase in cases -%}
+{% macro test_case(supercase) -%}
{% set property = supercase["property"] -%}
{% set description = supercase["description"] | to_snake -%}
{% if "cases" in supercase -%}
@@ -17,7 +15,7 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% elif property == "ability" -%}
def test_{{ description }}(self):
score = Character().{{ property }}()
- self.assertIs({{ supercase["expected"] | replace("&&","and") }}, True)
+ self.assertTrue({{ supercase["expected"] | replace("&&","and") }})
{% elif property == "character" -%}
def test_{{ description}}(self):
@@ -27,15 +25,17 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% if ability == "hitpoints" -%}
{% set statement = statement | replace("constitution","Char.constitution") -%}
{%- endif -%}
- self.assertIs({{ statement }}, True)
+ self.assertTrue({{ statement }})
{% endfor %}
{% elif property == "strength" -%}
def test_{{ description }}(self):
Char = Character()
- self.assertIs({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}}, True)
-
+ self.assertTrue({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}})
{%- endif -%}
- {% endfor %}
+{%- endmacro %}
-{{ macros.footer() }}
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for supercase in cases -%}
+ {{ test_case(supercase) }}
+ {% endfor %}
\ No newline at end of file
diff --git a/exercises/practice/dnd-character/dnd_character.py b/exercises/practice/dnd-character/dnd_character.py
index f8ecd30bb88..acfe6aef067 100644
--- a/exercises/practice/dnd-character/dnd_character.py
+++ b/exercises/practice/dnd-character/dnd_character.py
@@ -1,3 +1,7 @@
class Character:
def __init__(self):
pass
+
+
+def modifier(value):
+ pass
diff --git a/exercises/practice/dnd-character/dnd_character_test.py b/exercises/practice/dnd-character/dnd_character_test.py
index b3a93e5759a..d79074ca3ac 100644
--- a/exercises/practice/dnd-character/dnd_character_test.py
+++ b/exercises/practice/dnd-character/dnd_character_test.py
@@ -59,22 +59,18 @@ def test_ability_modifier_for_score_18_is_4(self):
def test_random_ability_is_within_range(self):
score = Character().ability()
- self.assertIs(score >= 3 and score <= 18, True)
+ self.assertTrue(score >= 3 and score <= 18)
def test_random_character_is_valid(self):
Char = Character()
- self.assertIs(Char.strength >= 3 and Char.strength <= 18, True)
- self.assertIs(Char.dexterity >= 3 and Char.dexterity <= 18, True)
- self.assertIs(Char.constitution >= 3 and Char.constitution <= 18, True)
- self.assertIs(Char.intelligence >= 3 and Char.intelligence <= 18, True)
- self.assertIs(Char.wisdom >= 3 and Char.wisdom <= 18, True)
- self.assertIs(Char.charisma >= 3 and Char.charisma <= 18, True)
- self.assertIs(Char.hitpoints == 10 + modifier(Char.constitution), True)
+ self.assertTrue(Char.strength >= 3 and Char.strength <= 18)
+ self.assertTrue(Char.dexterity >= 3 and Char.dexterity <= 18)
+ self.assertTrue(Char.constitution >= 3 and Char.constitution <= 18)
+ self.assertTrue(Char.intelligence >= 3 and Char.intelligence <= 18)
+ self.assertTrue(Char.wisdom >= 3 and Char.wisdom <= 18)
+ self.assertTrue(Char.charisma >= 3 and Char.charisma <= 18)
+ self.assertTrue(Char.hitpoints == 10 + modifier(Char.constitution))
def test_each_ability_is_only_calculated_once(self):
Char = Character()
- self.assertIs(Char.strength == Char.strength, True)
-
-
-if __name__ == "__main__":
- unittest.main()
+ self.assertTrue(Char.strength == Char.strength)
From 6841758d29a757cfd10a2f2b2dab272e97dc9fca Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 14:26:16 +0100
Subject: [PATCH 395/932] Reversed some changes
---
exercises/practice/dnd-character/dnd_character.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/exercises/practice/dnd-character/dnd_character.py b/exercises/practice/dnd-character/dnd_character.py
index acfe6aef067..f8ecd30bb88 100644
--- a/exercises/practice/dnd-character/dnd_character.py
+++ b/exercises/practice/dnd-character/dnd_character.py
@@ -1,7 +1,3 @@
class Character:
def __init__(self):
pass
-
-
-def modifier(value):
- pass
From 6b8b69de4f5816bd68dedfd56dee26ee2f53a958 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 28 Dec 2022 12:46:09 -0800
Subject: [PATCH 396/932] Apply suggestions from code review
Reverting to `assertIs`.
---
exercises/practice/dnd-character/.meta/template.j2 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/dnd-character/.meta/template.j2 b/exercises/practice/dnd-character/.meta/template.j2
index 473af1609a6..adb15650537 100644
--- a/exercises/practice/dnd-character/.meta/template.j2
+++ b/exercises/practice/dnd-character/.meta/template.j2
@@ -15,7 +15,7 @@
{% elif property == "ability" -%}
def test_{{ description }}(self):
score = Character().{{ property }}()
- self.assertTrue({{ supercase["expected"] | replace("&&","and") }})
+ self.assertIs({{ supercase["expected"] | replace("&&","and") }}, True)
{% elif property == "character" -%}
def test_{{ description}}(self):
@@ -25,13 +25,13 @@
{% if ability == "hitpoints" -%}
{% set statement = statement | replace("constitution","Char.constitution") -%}
{%- endif -%}
- self.assertTrue({{ statement }})
+ self.assertIs({{ statement }}, True)
{% endfor %}
{% elif property == "strength" -%}
def test_{{ description }}(self):
Char = Character()
- self.assertTrue({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}})
+ self.assertIs({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}}, True)
{%- endif -%}
{%- endmacro %}
From 01d9cafa04e71cea70f7eed2ff34d8ed9c195049 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 28 Dec 2022 12:52:29 -0800
Subject: [PATCH 397/932] Regenerated Test File
---
.../dnd-character/dnd_character_test.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/exercises/practice/dnd-character/dnd_character_test.py b/exercises/practice/dnd-character/dnd_character_test.py
index d79074ca3ac..3f370bc4bda 100644
--- a/exercises/practice/dnd-character/dnd_character_test.py
+++ b/exercises/practice/dnd-character/dnd_character_test.py
@@ -59,18 +59,18 @@ def test_ability_modifier_for_score_18_is_4(self):
def test_random_ability_is_within_range(self):
score = Character().ability()
- self.assertTrue(score >= 3 and score <= 18)
+ self.assertIs(score >= 3 and score <= 18, True)
def test_random_character_is_valid(self):
Char = Character()
- self.assertTrue(Char.strength >= 3 and Char.strength <= 18)
- self.assertTrue(Char.dexterity >= 3 and Char.dexterity <= 18)
- self.assertTrue(Char.constitution >= 3 and Char.constitution <= 18)
- self.assertTrue(Char.intelligence >= 3 and Char.intelligence <= 18)
- self.assertTrue(Char.wisdom >= 3 and Char.wisdom <= 18)
- self.assertTrue(Char.charisma >= 3 and Char.charisma <= 18)
- self.assertTrue(Char.hitpoints == 10 + modifier(Char.constitution))
+ self.assertIs(Char.strength >= 3 and Char.strength <= 18, True)
+ self.assertIs(Char.dexterity >= 3 and Char.dexterity <= 18, True)
+ self.assertIs(Char.constitution >= 3 and Char.constitution <= 18, True)
+ self.assertIs(Char.intelligence >= 3 and Char.intelligence <= 18, True)
+ self.assertIs(Char.wisdom >= 3 and Char.wisdom <= 18, True)
+ self.assertIs(Char.charisma >= 3 and Char.charisma <= 18, True)
+ self.assertIs(Char.hitpoints == 10 + modifier(Char.constitution), True)
def test_each_ability_is_only_calculated_once(self):
Char = Character()
- self.assertTrue(Char.strength == Char.strength)
+ self.assertIs(Char.strength == Char.strength, True)
From 2983d723ddc9ec9cde4268295741e6d76cd0ee3b Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 21:57:36 +0100
Subject: [PATCH 398/932] Fixed spacing
---
exercises/practice/collatz-conjecture/.meta/template.j2 | 4 ++--
.../practice/collatz-conjecture/collatz_conjecture_test.py | 6 ------
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.meta/template.j2 b/exercises/practice/collatz-conjecture/.meta/template.j2
index e8b36f8b4d9..4dcbae8550b 100644
--- a/exercises/practice/collatz-conjecture/.meta/template.j2
+++ b/exercises/practice/collatz-conjecture/.meta/template.j2
@@ -3,12 +3,12 @@
def test_{{ case["description"] | to_snake }}(self):
{% set expected = case["expected"] -%}
{% set exp_error = expected["error"] -%}
- {%- if case is error_case %}
+ {%- if case is error_case -%}
with self.assertRaises(ValueError) as err:
{{ case["property"] }}({{ case["input"]["number"] }})
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ exp_error }}")
- {% else %}
+ {%- else -%}
self.assertEqual(
{{ case["property"] }}({{ case["input"]["number"] }}),
{{ case["expected"] }}
diff --git a/exercises/practice/collatz-conjecture/collatz_conjecture_test.py b/exercises/practice/collatz-conjecture/collatz_conjecture_test.py
index c11e246b54c..b9c6df93c82 100644
--- a/exercises/practice/collatz-conjecture/collatz_conjecture_test.py
+++ b/exercises/practice/collatz-conjecture/collatz_conjecture_test.py
@@ -9,30 +9,24 @@
class CollatzConjectureTest(unittest.TestCase):
def test_zero_steps_for_one(self):
-
self.assertEqual(steps(1), 0)
def test_divide_if_even(self):
-
self.assertEqual(steps(16), 4)
def test_even_and_odd_steps(self):
-
self.assertEqual(steps(12), 9)
def test_large_number_of_even_and_odd_steps(self):
-
self.assertEqual(steps(1000000), 152)
def test_zero_is_an_error(self):
-
with self.assertRaises(ValueError) as err:
steps(0)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Only positive integers are allowed")
def test_negative_value_is_an_error(self):
-
with self.assertRaises(ValueError) as err:
steps(-15)
self.assertEqual(type(err.exception), ValueError)
From 5d384f236d21e330ea90c9e155c7120b8944a97a Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 19:05:25 +0100
Subject: [PATCH 399/932] Spell fix
---
.../practice/leap/.approaches/datetime-addition/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/leap/.approaches/datetime-addition/content.md b/exercises/practice/leap/.approaches/datetime-addition/content.md
index 3748fa47caf..681e19101a5 100644
--- a/exercises/practice/leap/.approaches/datetime-addition/content.md
+++ b/exercises/practice/leap/.approaches/datetime-addition/content.md
@@ -17,9 +17,9 @@ This approach may be considered a "cheat" for this exercise.
By adding a day to February 28th for the year, you can see if the new day is the 29th or the 1st.
If it is the 29th, then the function returns `True` for the year being a leap year.
-- A new [datetime][datetime] object is created for Febuary 28th of the year.
+- A new [datetime][datetime] object is created for February 28th of the year.
- Then the [timedelta][timedelta] of one day is added to that `datetime`,
-and the function returns if the [day][day] property of the resulting `datetime` object is the 29th.
+ and the function returns if the [day][day] property of the resulting `datetime` object is the 29th.
[timedelta]: https://docs.python.org/3/library/datetime.html#timedelta-objects
[day]: https://docs.python.org/3/library/datetime.html#datetime.datetime.day
From ef7760ce2296fdb4a198ac544bc5c625de54b0a5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 17:42:17 +0100
Subject: [PATCH 400/932] Start
---
.../practice/hamming/.approaches/config.json | 29 ++++++++
.../hamming/.approaches/introduction.md | 67 +++++++++++++++++++
.../hamming/.approaches/range/content.md | 38 +++++++++++
.../hamming/.approaches/sum/content.md | 55 +++++++++++++++
.../hamming/.approaches/zip/content.md | 47 +++++++++++++
5 files changed, 236 insertions(+)
create mode 100644 exercises/practice/hamming/.approaches/config.json
create mode 100644 exercises/practice/hamming/.approaches/introduction.md
create mode 100644 exercises/practice/hamming/.approaches/range/content.md
create mode 100644 exercises/practice/hamming/.approaches/sum/content.md
create mode 100644 exercises/practice/hamming/.approaches/zip/content.md
diff --git a/exercises/practice/hamming/.approaches/config.json b/exercises/practice/hamming/.approaches/config.json
new file mode 100644
index 00000000000..9b5ee619e12
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "9e0baea1-8b3b-46be-8d26-e247b6a59eee",
+ "slug": "range",
+ "title": "Range",
+ "blurb": "Use range to compare 2 strings",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "75883c53-cc84-483a-8375-eb9375a5f739",
+ "slug": "zip",
+ "title": "Zip",
+ "blurb": "Use zip to compare 2 strings",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "9db3f200-2816-40a8-b660-a6bf87abe470",
+ "slug": "sum",
+ "title": "Sum",
+ "blurb": "Use sum to compare 2 strings",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
new file mode 100644
index 00000000000..88419a879b4
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -0,0 +1,67 @@
+# Introduction
+
+There are various ways to solve Hamming.
+One approach is to iterate over either a range of indexs or to use [zip][zip].
+Another appraoch is to use the range of indexs.
+
+## General guidance
+
+The goal of this exercise is to compare two DNA strands and count how many of the nucleotides are different from their equivalent in the other string.
+The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides which has the same index.
+
+## Approach: Iterating over a range of indexes
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for x in range(len(strand_a)):
+ if strand_a[x] != strand_b[x]:
+ count += 1
+ return count
+```
+
+For more information, check the [range approach][approach-range].
+
+## Approach: Iterating with zip
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for a, b in zip(strand_a, strand_b):
+ if a != b:
+ count += 1
+ return count
+```
+
+For more information, check the [zip approach][approach-zip].
+
+## Approach: Using sum
+
+With zip:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(a != b for a, b in zip(strand_a, strand_b))
+```
+
+With range:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
+```
+
+For more information, check the [sum approach][approach-sum].
+
+[zip]: https://docs.python.org/3/library/functions.html#zip
+[approach-range]: https://exercism.org/tracks/python/exercises/hamming/approaches/range
+[approach-sum]: https://exercism.org/tracks/python/exercises/hamming/approaches/sum
+[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
diff --git a/exercises/practice/hamming/.approaches/range/content.md b/exercises/practice/hamming/.approaches/range/content.md
new file mode 100644
index 00000000000..00c633066fa
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/range/content.md
@@ -0,0 +1,38 @@
+# range
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for index in range(len(strand_a)):
+ if strand_a[index] != strand_b[index]:
+ count += 1
+ return count
+```
+
+This approach starts by checking if the two strands are of equal length.
+If not, a [`ValueError`][value-error] is raised.
+
+After that is checked, a variable `count` is initialized to 0.
+The count variable will be used to keep track of the number of differences between the two strands.
+
+[`range()`][range] in Python is a built-in function that returns a sequence of numbers.
+Range is an infinite sequence, but it can be limited by providing a `stop` argument.
+The `range()` function can also take a `start` argument and a `step` argument.
+The inputs are built up like this: `range(, stop, )`.
+Since we are only providing a `stop` argument, the `start` argument defaults to 0 and the `step` argument defaults to 1.
+
+We use range to iterate over the indexes of the `strand_a` string.
+We do that by passing the length of the string to the `range()` function by using [`len()`][len].
+The iteration gives us the index of the character in the string.
+We then use that index to access the character in the string.
+
+Then we compare the character at the index in `strand_a` to the character at the same index in `strand_b`.
+If they are not equal, we increment the `count` variable by 1.
+
+After the loop is finished, we return the `count` variable.
+
+[len]: https://docs.python.org/3/library/functions.html?#len
+[range]: https://docs.python.org/3/library/stdtypes.html?#range
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
new file mode 100644
index 00000000000..7d03212c528
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -0,0 +1,55 @@
+# sum
+
+The benefit of using [`sum()`][sum] is that we can use a list comprehension to create a list of booleans.
+Then we can pass that list to `sum()` and it will add up all the booleans.
+Where `True` is treated as 1 and `False` is treated as 0.
+Then that sum is returned.
+
+This can make the code a bit more concise.
+
+Here is an example with using `sum()` and `zip()`:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
+```
+
+This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+If not, a [`ValueError`][value-error] is raised.
+
+After that is checked, a variable `count` is initialized to 0.
+The count variable will be used to keep track of the number of differences between the two strands.
+
+This approach uses the [`zip()`][zip] function to iterate over two strings.
+You can read more about how to solve this exercise with `zip()` in the [zip approach][approach-zip].
+
+What differs in this approach is that we use a list comprehension to create a list of booleans.
+The list comprehension iterates over the tuples returned by `zip()`.
+Then under the iteration so are the tuples unpacked into two variables, `nucleotide_a` and `nucleotide_b`.
+We then compare the characters `nucleotide_a` and `nucleotide_b`.
+If they are not equal, we add `True` to the list.
+If they are equal, we add `False` to the list.
+The list comprehension is then passed to the `sum()` function.
+
+The [`sum()`][sum] function will add up all the booleans in the list.
+Where `True` is treated as 1 and `False` is treated as 0.
+You can read more about this behavior in [Boolean as numbers][booleans].
+Then that sum is returned.
+
+This approach is also doable with range but it is a bit more verbose:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
+```
+
+[booleans]: https://realpython.com/python-boolean/#python-booleans-as-numbers
+[len]: https://docs.python.org/3/library/functions.html?#len
+[sum]: https://docs.python.org/3/library/functions.html?#sum
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
+[zip]: https://docs.python.org/3.3/library/functions.html#zip
+[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
diff --git a/exercises/practice/hamming/.approaches/zip/content.md b/exercises/practice/hamming/.approaches/zip/content.md
new file mode 100644
index 00000000000..e8bfab7d094
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/zip/content.md
@@ -0,0 +1,47 @@
+# zip
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for nucleotide_a, nucleotide_b in zip(strand_a, strand_b):
+ if nucleotide_a != nucleotide_b:
+ count += 1
+ return count
+```
+
+This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+If not, a [`ValueError`][value-error] is raised.
+
+After that is checked, a variable `count` is initialized to 0.
+The count variable will be used to keep track of the number of differences between the two strands.
+
+We use [`zip()`][zip] to iterate over the characters in `strand_a` and `strand_b` simultaneously.
+`zip()` is a built in function.
+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.
+
+In python are strings are iterables.
+
+Here is an example of using `zip()` to iterate over two strings:
+
+```python
+>>> zipped = zip("GGACGG", "AGGACG")
+>>> list(zipped)
+[('G', 'A'), ('G', 'G'), ('A', 'G'), ('C', 'A'), ('G', 'C'), ('G', 'G')]
+```
+
+We then use the `zip()` iterator to iterate over the tuples.
+We unpack the tuple into two variables, `nucleotide_a` and `nucleotide_b`.
+You can read more about unpacking in the concept [concept:python/unpacking-and-multiple-assignment]().
+
+We then compare the characters `nucleotide_a` and `nucleotide_b`.
+If they are not equal, we increment the `count` variable by 1.
+
+After the loop is finished, we return the `count` variable.
+
+[len]: https://docs.python.org/3/library/functions.html?#len
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
+[zip]: https://docs.python.org/3.3/library/functions.html#zip
From d043098b04689f5dc97f489165c2dd25be0289b7 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 17:43:32 +0100
Subject: [PATCH 401/932] Fix variable naming
---
exercises/practice/hamming/.approaches/introduction.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 88419a879b4..f6cd3f77cdc 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -31,8 +31,8 @@ def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
count = 0
- for a, b in zip(strand_a, strand_b):
- if a != b:
+ for nucleotide_a, nucleotide_b in zip(strand_a, strand_b):
+ if nucleotide_a != nucleotide_b:
count += 1
return count
```
@@ -47,7 +47,7 @@ With zip:
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
- return sum(a != b for a, b in zip(strand_a, strand_b))
+ return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
```
With range:
From 2ac003c060e87fed3dba0b6575b60680588cae84 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 17:46:18 +0100
Subject: [PATCH 402/932] Add missing files
---
exercises/practice/hamming/.approaches/range/snippet.txt | 8 ++++++++
exercises/practice/hamming/.approaches/sum/snippet.txt | 4 ++++
exercises/practice/hamming/.approaches/zip/snippet.txt | 8 ++++++++
3 files changed, 20 insertions(+)
create mode 100644 exercises/practice/hamming/.approaches/range/snippet.txt
create mode 100644 exercises/practice/hamming/.approaches/sum/snippet.txt
create mode 100644 exercises/practice/hamming/.approaches/zip/snippet.txt
diff --git a/exercises/practice/hamming/.approaches/range/snippet.txt b/exercises/practice/hamming/.approaches/range/snippet.txt
new file mode 100644
index 00000000000..85caf5179bc
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/range/snippet.txt
@@ -0,0 +1,8 @@
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for index in range(len(strand_a)):
+ if strand_a[index] != strand_b[index]:
+ count += 1
+ return count
diff --git a/exercises/practice/hamming/.approaches/sum/snippet.txt b/exercises/practice/hamming/.approaches/sum/snippet.txt
new file mode 100644
index 00000000000..d8cc2f63933
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/sum/snippet.txt
@@ -0,0 +1,4 @@
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
diff --git a/exercises/practice/hamming/.approaches/zip/snippet.txt b/exercises/practice/hamming/.approaches/zip/snippet.txt
new file mode 100644
index 00000000000..3c310fe376b
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/zip/snippet.txt
@@ -0,0 +1,8 @@
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for nucleotide_a, nucleotide_b in zip(strand_a, strand_b):
+ if nucleotide_a != nucleotide_b:
+ count += 1
+ return count
From 774fa921dabe3e222ab47e004b7135710bdbe700 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 12:08:49 +0100
Subject: [PATCH 403/932] Fixes and added an explainer to why you might want to
use that method
---
.../hamming/.approaches/introduction.md | 24 ++++++++++++++++---
.../hamming/.approaches/sum/content.md | 2 +-
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index f6cd3f77cdc..7fa2fff95bd 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -3,6 +3,7 @@
There are various ways to solve Hamming.
One approach is to iterate over either a range of indexs or to use [zip][zip].
Another appraoch is to use the range of indexs.
+Some other approaches could be to use enumerate, or filter with lambda.
## General guidance
@@ -11,13 +12,19 @@ The most common way is to use some kind of loop to iterate over the two strands
## Approach: Iterating over a range of indexes
+Using range is an approach to iterate over a sequence.
+Although it is not the most pythonic way, it is a good way to start.
+The reasson to use range is that it is a built-in function and it is very fast.
+The downside is that it only works with iterators that can be indexed, like lists and strings.
+While a built in function like `enumerate()` can take any iterator.
+
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
count = 0
- for x in range(len(strand_a)):
- if strand_a[x] != strand_b[x]:
+ for index in range(len(strand_a)):
+ if strand_a[index] != strand_b[index]:
count += 1
return count
```
@@ -26,6 +33,11 @@ For more information, check the [range approach][approach-range].
## Approach: Iterating with zip
+The `zip()` function returns an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
+The approach to use `zip()` to iterate removes the need to index the iterators.
+The downside is that if you need to index the iterators, zip wont work.
+Although it is possible to use `enumerate()` with zip to get the index.
+
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
@@ -41,6 +53,11 @@ For more information, check the [zip approach][approach-zip].
## Approach: Using sum
+Using `sum()` makes it possible to remove the need for a counter variable.
+Since there is no need for a counter variable, the code is more concise.
+To make use of sum, so are the examples using a [list comprehension][list-comprehension], although it is not required.
+Using sum although requires a bit more knowledge of python compared to the other approaches.
+
With zip:
```python
@@ -61,7 +78,8 @@ def distance(strand_a, strand_b):
For more information, check the [sum approach][approach-sum].
-[zip]: https://docs.python.org/3/library/functions.html#zip
[approach-range]: https://exercism.org/tracks/python/exercises/hamming/approaches/range
[approach-sum]: https://exercism.org/tracks/python/exercises/hamming/approaches/sum
[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
+[list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
+[zip]: https://docs.python.org/3/library/functions.html#zip
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
index 7d03212c528..5254af969f0 100644
--- a/exercises/practice/hamming/.approaches/sum/content.md
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -47,9 +47,9 @@ def distance(strand_a, strand_b):
return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
```
+[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
[booleans]: https://realpython.com/python-boolean/#python-booleans-as-numbers
[len]: https://docs.python.org/3/library/functions.html?#len
[sum]: https://docs.python.org/3/library/functions.html?#sum
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
[zip]: https://docs.python.org/3.3/library/functions.html#zip
-[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
From f2716a18e05ac2fee59000ddf44c540da23ae0b4 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 23:21:40 +0100
Subject: [PATCH 404/932] spell fixes
---
exercises/practice/hamming/.approaches/introduction.md | 10 +++++-----
exercises/practice/hamming/.approaches/sum/content.md | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 7fa2fff95bd..9436d1cc9be 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -1,20 +1,20 @@
# Introduction
There are various ways to solve Hamming.
-One approach is to iterate over either a range of indexs or to use [zip][zip].
-Another appraoch is to use the range of indexs.
+One approach is to iterate over either a range of indexes or to use [zip][zip].
+Another approach is to use the range of indexes.
Some other approaches could be to use enumerate, or filter with lambda.
## General guidance
The goal of this exercise is to compare two DNA strands and count how many of the nucleotides are different from their equivalent in the other string.
-The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides which has the same index.
+The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides with the same index.
## Approach: Iterating over a range of indexes
Using range is an approach to iterate over a sequence.
Although it is not the most pythonic way, it is a good way to start.
-The reasson to use range is that it is a built-in function and it is very fast.
+The reason to use range is that it is a built-in function and it is very fast.
The downside is that it only works with iterators that can be indexed, like lists and strings.
While a built in function like `enumerate()` can take any iterator.
@@ -35,7 +35,7 @@ For more information, check the [range approach][approach-range].
The `zip()` function returns an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
The approach to use `zip()` to iterate removes the need to index the iterators.
-The downside is that if you need to index the iterators, zip wont work.
+The downside is that if you need to index the iterators, zip won't work.
Although it is possible to use `enumerate()` with zip to get the index.
```python
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
index 5254af969f0..f590f140484 100644
--- a/exercises/practice/hamming/.approaches/sum/content.md
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -7,7 +7,7 @@ Then that sum is returned.
This can make the code a bit more concise.
-Here is an example with using `sum()` and `zip()`:
+Here is an example using `sum()` and `zip()`:
```python
def distance(strand_a, strand_b):
From 85ef185a521e73b714add30e093f44226905f772 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 3 Jan 2023 16:31:47 -0800
Subject: [PATCH 405/932] Apply suggestions from code review
---
.../hamming/.approaches/introduction.md | 52 +++++++++++--------
.../hamming/.approaches/range/content.md | 25 +++++----
.../hamming/.approaches/sum/content.md | 36 ++++++-------
.../hamming/.approaches/zip/content.md | 18 +++----
4 files changed, 70 insertions(+), 61 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 9436d1cc9be..8cf91f229e3 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -3,20 +3,20 @@
There are various ways to solve Hamming.
One approach is to iterate over either a range of indexes or to use [zip][zip].
Another approach is to use the range of indexes.
-Some other approaches could be to use enumerate, or filter with lambda.
+Some other approaches include the use of [`enumerate`][enumerate], or [`filter`][filter] with a [`lambda`][lambda].
## General guidance
The goal of this exercise is to compare two DNA strands and count how many of the nucleotides are different from their equivalent in the other string.
-The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides with the same index.
+The most common solution uses some kind of loop to iterate over the two strands and compare nucleotides with the same index.
## Approach: Iterating over a range of indexes
-Using range is an approach to iterate over a sequence.
-Although it is not the most pythonic way, it is a good way to start.
-The reason to use range is that it is a built-in function and it is very fast.
-The downside is that it only works with iterators that can be indexed, like lists and strings.
-While a built in function like `enumerate()` can take any iterator.
+Using [`range`][range] is an approach to iterate over a sequence.
+Although it may not be the most _pythonic_ strategy, it is a good way to start.
+`range` is a [built-in function][built-in-functions] and it is very fast.
+The downside is that `range` only works with [iterators][iterators] that can be indexed, like [concept:python/lists](https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range) and [concept:python/strings](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str).
+While the built-in function `enumerate` can take any iterator.
```python
def distance(strand_a, strand_b):
@@ -33,10 +33,10 @@ For more information, check the [range approach][approach-range].
## Approach: Iterating with zip
-The `zip()` function returns an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
-The approach to use `zip()` to iterate removes the need to index the iterators.
-The downside is that if you need to index the iterators, zip won't work.
-Although it is possible to use `enumerate()` with zip to get the index.
+The built-in `zip` function returns an iterator of [concept:python/tuples](https://docs.python.org/3.10/tutorial/datastructures.html#tuples-and-sequences) where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together, and so on.
+Using `zip()` to iterate removes the need to index into the strands.
+The downside is that if you _need to_ index into your iterators, `zip` won't work.
+Although it is possible to combine `zip` with `enumerate` to generate indexes.
```python
def distance(strand_a, strand_b):
@@ -53,27 +53,29 @@ For more information, check the [zip approach][approach-zip].
## Approach: Using sum
-Using `sum()` makes it possible to remove the need for a counter variable.
-Since there is no need for a counter variable, the code is more concise.
-To make use of sum, so are the examples using a [list comprehension][list-comprehension], although it is not required.
-Using sum although requires a bit more knowledge of python compared to the other approaches.
+Using the built-in [`sum`][sum] removes the need for a counter variable.
+Removing the counter variable makes the code is more concise.
+The examples making use of `sum` also use a [generator expression][generator-expression], although that it is not required.
+Using `sum` in this fashion requires a bit more Python knowledge compared to the other approaches.
-With zip:
+With `zip`:
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
- return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
+ return sum(nucleotide_a != nucleotide_b for
+ nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
```
-With range:
+With `range`:
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
- return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
+ return sum(strand_a[index] != strand_b[index] for
+ index in range(len(strand_a)))
```
For more information, check the [sum approach][approach-sum].
@@ -81,5 +83,13 @@ For more information, check the [sum approach][approach-sum].
[approach-range]: https://exercism.org/tracks/python/exercises/hamming/approaches/range
[approach-sum]: https://exercism.org/tracks/python/exercises/hamming/approaches/sum
[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
-[list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
-[zip]: https://docs.python.org/3/library/functions.html#zip
+[built-in-functions]: https://docs.python.org/3.10/library/functions.html
+[enumerate]: https://docs.python.org/3.10/library/functions.html#enumerate
+[filter]: https://docs.python.org/3.10/library/functions.html#filter
+[generator-expression]: https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-generator_expression
+[iterators]: https://docs.python.org/3/glossary.html#term-iterator
+[lambda]: https://docs.python.org/3/glossary.html#term-lambda
+[range]: https://docs.python.org/3.10/library/functions.html#func-range
+[sum]: https://docs.python.org/3/library/functions.html#sum
+[zip]: https://docs.python.org/3.10/library/functions.html#zip
+
diff --git a/exercises/practice/hamming/.approaches/range/content.md b/exercises/practice/hamming/.approaches/range/content.md
index 00c633066fa..3f50c2622aa 100644
--- a/exercises/practice/hamming/.approaches/range/content.md
+++ b/exercises/practice/hamming/.approaches/range/content.md
@@ -14,24 +14,23 @@ def distance(strand_a, strand_b):
This approach starts by checking if the two strands are of equal length.
If not, a [`ValueError`][value-error] is raised.
-After that is checked, a variable `count` is initialized to 0.
+After that is checked, a `` variable is initialized to 0.
The count variable will be used to keep track of the number of differences between the two strands.
-[`range()`][range] in Python is a built-in function that returns a sequence of numbers.
-Range is an infinite sequence, but it can be limited by providing a `stop` argument.
-The `range()` function can also take a `start` argument and a `step` argument.
-The inputs are built up like this: `range(, stop, )`.
-Since we are only providing a `stop` argument, the `start` argument defaults to 0 and the `step` argument defaults to 1.
+[`range`][range] in Python is a built-in function that returns a sequence of numbers.
+`range` produces an infinite sequence, but it can be limited by providing a `` argument.
+The `range` function can also take a `` argument and a `` argument.
+The inputs are built up like this: `range(, , )`.
+When only a `` argument is provided, `` defaults to 0 and `` defaults to 1.
-We use range to iterate over the indexes of the `strand_a` string.
-We do that by passing the length of the string to the `range()` function by using [`len()`][len].
-The iteration gives us the index of the character in the string.
-We then use that index to access the character in the string.
+We use `range` to iterate over the indexes of the `strand_a` string.
+We do that by passing the length of the string to `range` by using the built-in function [`len`][len].
+Iterating over `range` gives us an index number we can use to access a character in the string.
-Then we compare the character at the index in `strand_a` to the character at the same index in `strand_b`.
-If they are not equal, we increment the `count` variable by 1.
+We can then compare the character at the index in `strand_a` to the character at the same index in `strand_b`.
+If the two values are not equal, we increment the `` variable by 1.
-After the loop is finished, we return the `count` variable.
+After the loop completes, we return the `` variable.
[len]: https://docs.python.org/3/library/functions.html?#len
[range]: https://docs.python.org/3/library/stdtypes.html?#range
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
index f590f140484..9123fa9257e 100644
--- a/exercises/practice/hamming/.approaches/sum/content.md
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -1,13 +1,13 @@
# sum
-The benefit of using [`sum()`][sum] is that we can use a list comprehension to create a list of booleans.
-Then we can pass that list to `sum()` and it will add up all the booleans.
+The benefit of using `sum` is that we can use a generator expression to create a list of booleans.
+We can then pass that generator to `sum` and it will iterate through and add up all the booleans.
Where `True` is treated as 1 and `False` is treated as 0.
-Then that sum is returned.
+Then that total is returned.
This can make the code a bit more concise.
-Here is an example using `sum()` and `zip()`:
+Here is an example using `sum` with `zip`:
```python
def distance(strand_a, strand_b):
@@ -16,29 +16,29 @@ def distance(strand_a, strand_b):
return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
```
-This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+This approach starts by checking if the two strands are of equal length by using [`len`][len].
If not, a [`ValueError`][value-error] is raised.
-After that is checked, a variable `count` is initialized to 0.
+After that is checked, a `` variable is initialized to 0.
The count variable will be used to keep track of the number of differences between the two strands.
-This approach uses the [`zip()`][zip] function to iterate over two strings.
-You can read more about how to solve this exercise with `zip()` in the [zip approach][approach-zip].
+This approach uses the [`zip`][zip] function to iterate over two strings.
+You can read more about how to solve this exercise with `zip` in the [zip approach][approach-zip].
-What differs in this approach is that we use a list comprehension to create a list of booleans.
-The list comprehension iterates over the tuples returned by `zip()`.
-Then under the iteration so are the tuples unpacked into two variables, `nucleotide_a` and `nucleotide_b`.
-We then compare the characters `nucleotide_a` and `nucleotide_b`.
-If they are not equal, we add `True` to the list.
-If they are equal, we add `False` to the list.
-The list comprehension is then passed to the `sum()` function.
+What differs in this approach is that we use a generator expression to create booleans.
+The generator expression returns an iterator over the tuples returned by `zip`.
+Within the iteration, the tuples are unpacked into two variables, `nucleotide_a` and `nucleotide_b`.
+We can then compare `nucleotide_a` and `nucleotide_b`.
+If they are **not** equal, `True` is produced.
+If they **are** equal, `False` is produced.
+The generator expression is then passed to the `sum` function.
-The [`sum()`][sum] function will add up all the booleans in the list.
+`sum` will then iterate over the generator expression and add up all the booleans.
Where `True` is treated as 1 and `False` is treated as 0.
You can read more about this behavior in [Boolean as numbers][booleans].
-Then that sum is returned.
+Finally the totaled booleans are returned.
-This approach is also doable with range but it is a bit more verbose:
+This approach is also doable with `range` but it is a bit more verbose:
```python
def distance(strand_a, strand_b):
diff --git a/exercises/practice/hamming/.approaches/zip/content.md b/exercises/practice/hamming/.approaches/zip/content.md
index e8bfab7d094..8b4bdd23bc5 100644
--- a/exercises/practice/hamming/.approaches/zip/content.md
+++ b/exercises/practice/hamming/.approaches/zip/content.md
@@ -11,21 +11,21 @@ def distance(strand_a, strand_b):
return count
```
-This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+This approach starts by checking if the two strands are of equal length by using [`len`][len].
If not, a [`ValueError`][value-error] is raised.
-After that is checked, a variable `count` is initialized to 0.
+After that is checked, a `` variable is initialized to 0.
The count variable will be used to keep track of the number of differences between the two strands.
-We use [`zip()`][zip] to iterate over the characters in `strand_a` and `strand_b` simultaneously.
-`zip()` is a built in function.
+We use [`zip`][zip] to iterate over the characters in `strand_a` and `strand_b` simultaneously.
+`zip` is a built in function.
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.
-In python are strings are iterables.
+In Python, strings are iterable.
-Here is an example of using `zip()` to iterate over two strings:
+Here is an example of using `zip` to iterate over two strings:
```python
>>> zipped = zip("GGACGG", "AGGACG")
@@ -33,14 +33,14 @@ Here is an example of using `zip()` to iterate over two strings:
[('G', 'A'), ('G', 'G'), ('A', 'G'), ('C', 'A'), ('G', 'C'), ('G', 'G')]
```
-We then use the `zip()` iterator to iterate over the tuples.
+We then use the `zip` iterator to iterate over the tuples.
We unpack the tuple into two variables, `nucleotide_a` and `nucleotide_b`.
You can read more about unpacking in the concept [concept:python/unpacking-and-multiple-assignment]().
We then compare the characters `nucleotide_a` and `nucleotide_b`.
-If they are not equal, we increment the `count` variable by 1.
+If they are not equal, we increment the count variable by 1.
-After the loop is finished, we return the `count` variable.
+After the loop is finished, we return the count variable.
[len]: https://docs.python.org/3/library/functions.html?#len
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
From 949d146f471e68ecfa75f5b36754b77eaa63fb94 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 3 Jan 2023 16:43:48 -0800
Subject: [PATCH 406/932] Update
exercises/practice/hamming/.approaches/introduction.md
Co-authored-by: meatball <69751659+meatball133@users.noreply.github.com>
---
exercises/practice/hamming/.approaches/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 8cf91f229e3..3272a8bd05a 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -54,7 +54,7 @@ For more information, check the [zip approach][approach-zip].
## Approach: Using sum
Using the built-in [`sum`][sum] removes the need for a counter variable.
-Removing the counter variable makes the code is more concise.
+Removing the counter variable makes the code more concise.
The examples making use of `sum` also use a [generator expression][generator-expression], although that it is not required.
Using `sum` in this fashion requires a bit more Python knowledge compared to the other approaches.
From e6c795657f01f5217693927fdfce33b859195145 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 29 Dec 2022 22:24:38 -0800
Subject: [PATCH 407/932] Add common exceptions to the TRACEBACK docs.
---
docs/TRACEBACKS.md | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index ddda2eb8bd3..05cdc2024c3 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -54,6 +54,32 @@ ValueError: not enough values to unpack (expected 2, got 1)
Working backwards from the bottom, we see that the call where the exception happened is on line 2 in `my_func`.
We got there by calling `my_func` on line 5.
+## Common Exceptions
+
+Python 3.11 defines 67 [built-in exception classes][exception-hierarchy].
+Here is a brief overview of some of the more common exceptions and what they indicate.
+
+* **SyntaxError**: Python is unable to understand the code as the code has invalid syntax.
+ For example, there may be an open parenthesis without a matching closing parenthesis.
+* **AssertionError**: An `assert` statement (see below) failed.
+* **AttributeError**: The code (or unit test!) tried to access the attribute of an object but that object has no attribute.
+ For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
+ This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
+ `"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
+* **ImportError**: The code tried to import something, but it wasn't there.
+ For example, a unit test does `from exercise import Thing` but the `exercise.py` file does not define a `Thing`.
+* **IndexError**: An invalid index was used to look up a value in a list.
+ This often indicates the index is not computed properly and is often an off-by-one error.
+ For example, given `numbers = [1, 2, 3]`, the code `print(numbers[len(numbers)])` will raise an IndexError since `len(numbers)` is 3 but the last valid index is 2.
+ `[1, 2, 3][3]` raises `IndexError: list index out of range`.
+* **KeyError**: Similar to IndexError, this exception is raised when using a key to look up a dictionary value but the key is not set in the dictionary.
+ For example, `{"Alice": 1}["Bob"]` raises `KeyError: 'Bob'`.
+* **TypeError**: Typically, this is raised when the wrong type of data is passed to a function or used in an operation.
+ For example, `"Hello" + 1` will raise `TypeError: can only concatenate str (not "int") to str.
+* **ValueError**: This is usually raised when an invalid value is passed to function.
+ For example, real square roots only exist for position numbers.
+ Calling `math.sqrt(-1)` will raise `ValueError: math domain error`.
+
## Using the `print` function
Sometimes an error is not being raised, but a value is not what is expected.
@@ -305,3 +331,4 @@ print(sum)
[logging]: https://docs.python.org/3/howto/logging.html
[print]: https://www.w3schools.com/python/ref_func_print.asp
[pdb]: https://www.geeksforgeeks.org/python-debugger-python-pdb/
+[exception-hierarchy]: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
From 11d4006f2904daedbaf4bbfe9438552732ad57f6 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 5 Jan 2023 19:52:08 -0800
Subject: [PATCH 408/932] Add examples to all the discussed exceptions
---
docs/TRACEBACKS.md | 348 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 326 insertions(+), 22 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 05cdc2024c3..7a2adbf6691 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -56,29 +56,333 @@ We got there by calling `my_func` on line 5.
## Common Exceptions
-Python 3.11 defines 67 [built-in exception classes][exception-hierarchy].
+Python defines over 60 [built-in exception classes][exception-hierarchy].
Here is a brief overview of some of the more common exceptions and what they indicate.
-* **SyntaxError**: Python is unable to understand the code as the code has invalid syntax.
- For example, there may be an open parenthesis without a matching closing parenthesis.
-* **AssertionError**: An `assert` statement (see below) failed.
-* **AttributeError**: The code (or unit test!) tried to access the attribute of an object but that object has no attribute.
- For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
- This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
- `"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
-* **ImportError**: The code tried to import something, but it wasn't there.
- For example, a unit test does `from exercise import Thing` but the `exercise.py` file does not define a `Thing`.
-* **IndexError**: An invalid index was used to look up a value in a list.
- This often indicates the index is not computed properly and is often an off-by-one error.
- For example, given `numbers = [1, 2, 3]`, the code `print(numbers[len(numbers)])` will raise an IndexError since `len(numbers)` is 3 but the last valid index is 2.
- `[1, 2, 3][3]` raises `IndexError: list index out of range`.
-* **KeyError**: Similar to IndexError, this exception is raised when using a key to look up a dictionary value but the key is not set in the dictionary.
- For example, `{"Alice": 1}["Bob"]` raises `KeyError: 'Bob'`.
-* **TypeError**: Typically, this is raised when the wrong type of data is passed to a function or used in an operation.
- For example, `"Hello" + 1` will raise `TypeError: can only concatenate str (not "int") to str.
-* **ValueError**: This is usually raised when an invalid value is passed to function.
- For example, real square roots only exist for position numbers.
- Calling `math.sqrt(-1)` will raise `ValueError: math domain error`.
+### **SyntaxError**
+
+Python raises a `SyntaxError` when it is unable to understand the code due to invalid syntax.
+For example, there may be an open parenthesis without a matching closing parenthesis.
+
+
+
+Click here for a code example.
+
+
+Running this code:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length." # This is missing the closing parenthesis
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+.usr.local.lib.python3.10.site-packages._pytest.python.py:608: in _importtestmodule
+ mod = import_path(self.path, mode=importmode, root=self.config.rootpath)
+.usr.local.lib.python3.10.site-packages._pytest.pathlib.py:533: in import_path
+ importlib.import_module(module_name)
+.usr.local.lib.python3.10.importlib.__init__.py:126: in import_module
+ return _bootstrap._gcd_import(name[level:], package, level)
+:1050: in _gcd_import ???
+:1027: in _find_and_load ???
+:1006: in _find_and_load_unlocked ???
+:688: in _load_unlocked ???
+.usr.local.lib.python3.10.site-packages._pytest.assertion.rewrite.py:168: in exec_module
+ exec(co, module.__dict__)
+.mnt.exercism-iteration.hamming_test.py:3: in
+ from hamming import (
+E File ".mnt.exercism-iteration.hamming.py", line 10
+E raise ValueError("Strands must be of equal length."
+E ^
+E SyntaxError: '(' was never closed
+```
+
+
+
+
+
+### **AssertionError**
+Python raises an `AssertionError` when an `assert` statement (see below) fails.
+
+
+
+Click here for a code example.
+
+
+Running this code:
+
+```python
+def distance(strand_a, strand_b):
+ assert len(strand_a) == len(strand_b)
+
+
+distance("ab", "abc")
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+hamming_test.py:3: in
+ from hamming import (
+hamming.py:5: in
+ distance("ab", "abc")
+hamming.py:2: in distance
+ assert len(strand_a) == len(strand_b)
+E AssertionError
+```
+
+
+
+
+
+### **AttributeError**
+An `AttributeError` is raised when code (or a unit test!) tries to access the attribute of an object but that object has no such attribute.
+For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
+
+This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
+`"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
+
+
+
+Click here for a Robot class example.
+
+
+Running this code:
+
+```python
+class Robot:
+ def __init__():
+ #note that there is no self.direction listed here
+ self.position = (0, 0)
+ self.orientation = 'SW'
+
+ def forward():
+ pass
+
+
+robby = Robot
+robby.direction
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+robot_simulator_test.py:3: in
+ from robot_simulator import (
+robot_simulator.py:12: in
+ robby.direction
+E AttributeError: type object 'Robot' has no attribute 'direction'
+```
+
+
+
+
+
+
+Click here for a string example.
+
+
+Running this code:
+
+```python
+def distance(strand_a, strand_b):
+ if strand_a.lowercase() == strand_b:
+ return 0
+
+
+distance("ab", "abc")
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+ def distance(strand_a, strand_b):
+> if strand_a.lowercase() == strand_b:
+E AttributeError: 'str' object has no attribute 'lowercase'
+```
+
+
+
+
+
+
+### **ImportError**
+An `ImportError` is raised when code tries to import something, but Python is unable to do so.
+For example, a unit test for `Guidos Gorgeous Lasagna` does `from lasagna import bake_time_remaining`, but the `lasgana.py` solution file might not define `bake_time_remaining`.
+
+
+
+Click here for code example
+
+
+Running the `lasgana.py` file without the defined function would result in the following error:
+
+```python
+We received the following error when we ran your code:
+
+ ImportError while importing test module '.mnt.exercism-iteration.lasagna_test.py'.
+Hint: make sure your test modules.packages have valid Python names.
+
+Traceback:
+.mnt.exercism-iteration.lasagna_test.py:6: in
+ from lasagna import (EXPECTED_BAKE_TIME,
+E ImportError: cannot import name 'bake_time_remaining' from 'lasagna' (.mnt.exercism-iteration.lasagna.py)
+
+During handling of the above exception, another exception occurred:
+.usr.local.lib.python3.10.importlib.__init__.py:126: in import_module
+ return _bootstrap._gcd_import(name[level:], package, level)
+.mnt.exercism-iteration.lasagna_test.py:23: in
+ raise ImportError("In your 'lasagna.py' file, we can not find or import the"
+
+E ImportError: In your 'lasagna.py' file, we can not find or import the function named 'bake_time_remaining()'. Did you mis-name or forget to define it?
+```
+
+### **IndexError**
+
+Python raises an `IndexError` when an invalid index is used to look up a value in a list.
+This often indicates the index is not computed properly and is often an off-by-one error.
+
+
+
+Click here for code example
+
+
+Consider the following code.
+
+```python
+def distance(strand_a, strand_b):
+ same = 0
+ for i in range(len(strand_a)):
+ if strand_a[i] == strand_b[i]:
+ same += 1
+ return same
+
+
+distance("abc", "ab") # Note the first strand is longer than the second strand.
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+hamming_test.py:3: in
+ from hamming import (
+hamming.py:9: in
+ distance("abc", "ab") # Note the first strand is longer than the second strand.
+hamming.py:4: in distance
+ if strand_a[i] == strand_b[i]:
+E IndexError: string index out of range
+```
+
+
+
+
+
+### **KeyError**
+Similar to `IndexError`, this exception is raised when using a key to look up a dictionary value but the key is not set in the dictionary.
+
+
+
+Click here for code example
+
+
+Consider the following code.
+
+```python
+def to_rna(dna_letter):
+ translation = {"G": "C", "C": "G", "A": "U", "T": "A"}
+ return translation[dna_letter]
+
+
+print(to_rna("Q")) # Note, "Q" is not in the translation.
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+rna_transcription_test.py:3: in
+ from rna_transcription import to_rna
+rna_transcription.py:6: in
+ print(to_rna("Q"))
+rna_transcription.py:3: in to_rna
+ return translation[dna_letter]
+E KeyError: 'Q'
+```
+
+
+
+
+### **TypeError**
+Typically, a `TypeError` is raised when the wrong type of data is passed to a function or used in an operation.
+
+
+
+
+Click here for code example
+
+
+Consider the following code.
+
+```python
+def hello(name): # This function expects a string.
+ return 'Hello, ' + name + '!'
+
+
+print(hello(100)) # 100 is not a string.
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+hello_world_test.py:3: in
+ import hello_world
+hello_world.py:5: in
+ print(hello(100))
+hello_world.py:2: in hello
+ return 'Hello, ' + name + '!'
+E TypeError: can only concatenate str (not "int") to str
+```
+
+
+
+
+
+### **ValueError**
+A `ValueError` is usually raised when an invalid value is passed to function.
+
+Click here for code example
+
+
+Note, real square roots only exist for position numbers.
+Calling `math.sqrt(-1)` will raise `ValueError: math domain error` since `-1` is not a valid value for a square root.
+In (mathematical) technical terms, -1 is not in the domain of square roots.
+
+
+```python
+import math
+
+math.sqrt(-1)
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+square_root_test.py:3: in
+ from square_root import (
+square_root.py:3: in
+ math.sqrt(-1)
+E ValueError: math domain error
+```
+
+
+
+
## Using the `print` function
@@ -302,7 +606,7 @@ Here is an example of how to use the above debugger commands based on the code e
...
```
-In Python 3.7+ there is an easier way to create breakpoints
+In Python 3.7+ there is an easier way to create breakpoints.
Simply writing `breakpoint()` where needed will create one.
```python
From 6bdb1aede8cf75c240c8d9bee2d0c5cf1b2ce4b0 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 5 Jan 2023 20:53:39 -0800
Subject: [PATCH 409/932] Apply suggestions from code review
Co-authored-by: BethanyG
---
docs/TRACEBACKS.md | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 7a2adbf6691..974b7753d4d 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -140,7 +140,7 @@ E AssertionError
### **AttributeError**
An `AttributeError` is raised when code (or a unit test!) tries to access the attribute of an object but that object has no such attribute.
-For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
+For example, a unit test expects a `Robot` object to have a `direction` attribute, but when it tried to access `robot.direction`, it does not exist.
This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
`"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
@@ -242,7 +242,7 @@ E ImportError: In your 'lasagna.py' file, we can not find or import the functi
### **IndexError**
-Python raises an `IndexError` when an invalid index is used to look up a value in a list.
+Python raises an `IndexError` when an invalid index is used to look up a value in a sequence.
This often indicates the index is not computed properly and is often an off-by-one error.
@@ -354,11 +354,13 @@ E TypeError: can only concatenate str (not "int") to str
### **ValueError**
A `ValueError` is usually raised when an invalid value is passed to function.
+
+
Click here for code example
-Note, real square roots only exist for position numbers.
+Note, real square roots only exist for positive numbers.
Calling `math.sqrt(-1)` will raise `ValueError: math domain error` since `-1` is not a valid value for a square root.
In (mathematical) technical terms, -1 is not in the domain of square roots.
From 5d9319abe322bfbeb2c70c617f67c6e6398d308f Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 5 Jan 2023 21:03:49 -0800
Subject: [PATCH 410/932] Update docs/TRACEBACKS.md
Co-authored-by: BethanyG
---
docs/TRACEBACKS.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 974b7753d4d..4bdf92cc67e 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -238,7 +238,6 @@ During handling of the above exception, another exception occurred:
raise ImportError("In your 'lasagna.py' file, we can not find or import the"
E ImportError: In your 'lasagna.py' file, we can not find or import the function named 'bake_time_remaining()'. Did you mis-name or forget to define it?
-```
### **IndexError**
From ddd0c0aad1f31b654511602ebabf717693ca4805 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sat, 31 Dec 2022 15:49:11 +0100
Subject: [PATCH 411/932] Started
---
.../.approaches/config.json | 22 ++++
.../.approaches/introduction.md | 120 +++++++++++++++++
.../nested-for-loop-optimized/content.md | 93 +++++++++++++
.../nested-for-loop-optimized/snippet.txt | 5 +
.../.approaches/nested-for-loop/content.md | 76 +++++++++++
.../.approaches/nested-for-loop/snippet.txt | 5 +
.../palindrome-products/.articles/config.json | 11 ++
.../.articles/performance/code/Benchmark.py | 123 ++++++++++++++++++
.../.articles/performance/content.md | 36 +++++
.../.articles/performance/snippet.md | 6 +
10 files changed, 497 insertions(+)
create mode 100644 exercises/practice/palindrome-products/.approaches/config.json
create mode 100644 exercises/practice/palindrome-products/.approaches/introduction.md
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt
create mode 100644 exercises/practice/palindrome-products/.articles/config.json
create mode 100644 exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
create mode 100644 exercises/practice/palindrome-products/.articles/performance/content.md
create mode 100644 exercises/practice/palindrome-products/.articles/performance/snippet.md
diff --git a/exercises/practice/palindrome-products/.approaches/config.json b/exercises/practice/palindrome-products/.approaches/config.json
new file mode 100644
index 00000000000..bcd2aca80d0
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/config.json
@@ -0,0 +1,22 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "7d26e763-daa0-4396-bc7b-7624fbc2ac5c",
+ "slug": "nested-for-loop",
+ "title": "Nested for loop",
+ "blurb": "Use a nested for loop",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "e6495842-a557-43db-b051-b99ffba03f00",
+ "slug": "nested-for-loop-optimized",
+ "title": "Nested for loop optimized",
+ "blurb": "Nested for loop optimized edition",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/palindrome-products/.approaches/introduction.md b/exercises/practice/palindrome-products/.approaches/introduction.md
new file mode 100644
index 00000000000..5622723d716
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/introduction.md
@@ -0,0 +1,120 @@
+# Introduction
+
+There are various approaches to solving this problem.
+These approaches show 2 common ways to solve this problem.
+With one being a lot more efficient than the other.
+Although with that being said there are other ways to solve this problem.
+
+## General guidance
+
+The goal of this exercise is to generate the largest and smallest palindromes from a given range of numbers.
+
+## Approach: Using a nested for loop
+
+A nested for loop is a good approach to this problem.
+It is simple and easy to understand and it works.
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+For more information, check the [Nested for loop approach][approach-nested-for-loop].
+
+## Approach: Using a nested for loop, optimized edition
+
+This approach is similar to the previous one, but what if I say that with adding a few lines of code, we can make it faster?
+Then you might say, how much faster?
+Well, for some inputs it reduces the time from 9 minutes to 0.01 seconds.
+You can read more about it in the [Performance article][article-performance].
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+You can read more about how to achieve this optimization in: [Nested for loop optimized][approach-nested-for-loop-optimized].
+
+## Benchmark
+
+For more information, check the [Performance article][article-performance].
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
+[article-performance]: https://exercism.org/tracks/python/exercises/palindrome-products/articles/performance
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
new file mode 100644
index 00000000000..9c28157db72
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
@@ -0,0 +1,93 @@
+# Nested For Loop Optimized
+
+The point of this approach is to show with just a few changes, the runtime can be improved by a lot.
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations that make it faster.
+To optimize the largest function, we have to start the inner loop from the maximum factor and go down to the minimum factor.
+This way, we can stop the inner loop earlier than before.
+We also in the inner loop set the minimum value to the current value of the outer loop.
+
+Here is an example of how the algorithm work and why we have to modify the loops.
+Say we take max to be 99 and min 10.
+It would go first round:
+
+```
+x => [99 , 98, 97 ...]
+y => [99]
+```
+
+And already here we have our result which is `9009[91,99]`
+Although the loops has to continue to make us sure there are no higher value.
+
+```
+x => [98, 97, 96 ...]
+y => [99, 98]
+...
+x => [90, 89, 88 ...]
+y => [99, 98,97,96,95,94,93,92,91,90]
+```
+
+Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
+Meaning running beyond this point wont give us any higher value than 9009.
+
+That is why we introduce the `was_bigger` variable.
+With that variable we can check the inner loop if it has been bigger than the current result.
+If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
+We do that by using the [`break`][break] statement.
+
+If we hadn't modified the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
+This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
+
+The smallest function is optimized in a similar way as largest function compared to the original approach.
+The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
+Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[break]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt
new file mode 100644
index 00000000000..bf91dd67539
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt
@@ -0,0 +1,5 @@
+for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
new file mode 100644
index 00000000000..b2fe86d0acb
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
@@ -0,0 +1,76 @@
+# Nested For Loop
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+The largest and smallest functions are very similar.
+Although there are some small differences.
+
+The largest function starts with checking if the min_factor is bigger than the max_factor.
+If it is, it raises a [`ValueError`][value-error].
+
+After that, it initializes the result to 0 and the answer to an empty list.
+Then it starts `for in range` loop.
+The first for loop iterates over the range of numbers from min_factor to max_factor.
+The second for loop iterates over the same range of numbers.
+Then it checks if the product of the two numbers is bigger than the result.
+If it is, it converts the product to a string and checks if it is a palindrome.
+
+We can check if a string is a palindrome by comparing it to its reverse.
+There are multiple ways to reverse a string.
+The most common way is to use the slice notation.
+The slice notation is `string[start:stop:step]`.
+You can read more about it in: [reverse string with slice][string-reverse].
+
+If it is, it checks if the product is bigger than the result.
+If it is, it clears the answer list and sets the result to the product.
+Then it appends the two numbers to the answer list.
+
+After the two for loops, it checks if the result is 0.
+If it is, it sets the result to [`None`][none].
+Then it returns the result and the answer.
+
+The smallest one is very similar.
+The differences are that it checks if the product is smaller than the result or if the result is 0.
+And that it reset result outside of the if statement.
+Instead of inside of it.
+
+[none]: https://realpython.com/null-in-python/
+[string-reverse]: https://realpython.com/reverse-string-python/#reversing-strings-through-slicing
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt b/exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt
new file mode 100644
index 00000000000..f678cd824a4
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt
@@ -0,0 +1,5 @@
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
diff --git a/exercises/practice/palindrome-products/.articles/config.json b/exercises/practice/palindrome-products/.articles/config.json
new file mode 100644
index 00000000000..2cf84af89d1
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "c90d5c22-3133-4f1b-88b6-3ea9b6df298e",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the performance between different approaches",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py b/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..9bfbbf35b31
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
@@ -0,0 +1,123 @@
+import timeit
+import sys
+
+
+print(sys.version)
+
+
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+def largest_optimized(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest_optimized(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+starttime = timeit.default_timer()
+largest(1, 1000)
+print("largest, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+largest_optimized(1, 1000)
+print("largest_optimized, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest(1, 1000)
+print("smallest, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest_optimized(1, 1000)
+print("smallest_optimized, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+
+
+starttime = timeit.default_timer()
+largest(100, 100000)
+print("largest, min=100, max=100000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+largest_optimized(100, 100000)
+print("largest_optimized, min=100, max=100000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest(100, 100000)
+print("smallest, min=100, max=100000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest_optimized(100, 100000)
+print("smallest_optimized, min=100, max=100000 :", timeit.default_timer() - starttime)
diff --git a/exercises/practice/palindrome-products/.articles/performance/content.md b/exercises/practice/palindrome-products/.articles/performance/content.md
new file mode 100644
index 00000000000..f0bce43e18a
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/performance/content.md
@@ -0,0 +1,36 @@
+# Performance
+
+In this approach, we'll find out the performance difference between approaches for palindrome product in Python.
+
+The [approaches page][approaches] lists two approaches to this exercise:
+
+1. [Using a nested for loop][approach-nested-for-loop]
+2. [Using a nested for loop, optimized edition][approach-nested-for-loop-optimized]
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+These test were run in windows 11, using Python 3.11.1.
+
+```
+largest, min=1, max=1000 : 0.05235219991300255
+largest_optimized, min=1, max=1000 : 0.000758000067435205
+smallest, min=1, max=1000 : 0.04553140001371503
+smallest_optimized, min=1, max=1000 : 0.00010269996710121632
+
+largest, min=100, max=100000 : 512.5731259000022
+largest_optimized, min=100, max=100000 : 0.013197900028899312
+smallest, min=100, max=100000 : 549.5989698000485
+smallest_optimized, min=100, max=100000 : 0.03933039994444698
+```
+
+## Conclusion
+
+As we can see, the optimized approach is much faster than the original approach.
+Although the difference becomes most noticeable when the range is large, the optimized approach is still faster in the small range.
+
+[approaches]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
diff --git a/exercises/practice/palindrome-products/.articles/performance/snippet.md b/exercises/practice/palindrome-products/.articles/performance/snippet.md
new file mode 100644
index 00000000000..da2648a6a9d
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/performance/snippet.md
@@ -0,0 +1,6 @@
+```
+largest, min=100, max=100000 : 512.5731259000022
+largest_optimized, min=100, max=100000 : 0.013197900028899312
+smallest, min=100, max=100000 : 549.5989698000485
+smallest_optimized, min=100, max=100000 : 0.03933039994444698
+```
From dfd47334201c64edee2540460fb711d356f58e22 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 6 Jan 2023 17:06:21 -0800
Subject: [PATCH 412/932] Apply suggestions from code review
Suggestions and nits.
---
.../.approaches/introduction.md | 13 +++---
.../nested-for-loop-optimized/content.md | 41 +++++++++----------
.../.approaches/nested-for-loop/content.md | 19 +++++----
.../.articles/performance/content.md | 5 ++-
4 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/exercises/practice/palindrome-products/.approaches/introduction.md b/exercises/practice/palindrome-products/.approaches/introduction.md
index 5622723d716..2637f1ae4c0 100644
--- a/exercises/practice/palindrome-products/.approaches/introduction.md
+++ b/exercises/practice/palindrome-products/.approaches/introduction.md
@@ -1,9 +1,8 @@
# Introduction
-There are various approaches to solving this problem.
-These approaches show 2 common ways to solve this problem.
-With one being a lot more efficient than the other.
-Although with that being said there are other ways to solve this problem.
+There are various ways to solve `palindrome-products`.
+This approaches document shows 2 _common_ strategies, with one being a lot more efficient than the other.
+That being said, neither approach here is considered canonical, and other "pythonic" approaches could be added/expanded on in the future.
## General guidance
@@ -57,9 +56,9 @@ For more information, check the [Nested for loop approach][approach-nested-for-l
## Approach: Using a nested for loop, optimized edition
-This approach is similar to the previous one, but what if I say that with adding a few lines of code, we can make it faster?
-Then you might say, how much faster?
-Well, for some inputs it reduces the time from 9 minutes to 0.01 seconds.
+This approach is similar to the previous one, but with the addition of a few lines of code we can make it much faster.
+The question then becomes _how much faster_?
+Well, for some input sizes it reduces the time from **9 minutes to 0.01 seconds**.
You can read more about it in the [Performance article][article-performance].
```python
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
index 9c28157db72..2bc2718391e 100644
--- a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
@@ -1,6 +1,6 @@
# Nested For Loop Optimized
-The point of this approach is to show with just a few changes, the runtime can be improved by a lot.
+This approach shows that just a few changes can improve the running time of the solution significantly.
```python
def largest(min_factor, max_factor):
@@ -49,43 +49,42 @@ def smallest(min_factor, max_factor):
return (result, answer)
```
-This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations that make it faster.
-To optimize the largest function, we have to start the inner loop from the maximum factor and go down to the minimum factor.
-This way, we can stop the inner loop earlier than before.
-We also in the inner loop set the minimum value to the current value of the outer loop.
+This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations.
+To optimize the `largest` function, we have to start the inner loop from the maximum factor and proceed down to the minimum factor.
+This allows us to stop the inner loop earlier than before.
+We also set the minimum value in the _inner_ loop to the current value of the _outer_ loop.
-Here is an example of how the algorithm work and why we have to modify the loops.
-Say we take max to be 99 and min 10.
-It would go first round:
+Here is an example of how the algorithm works and why the loops need to be modified.
+Say we take maximum to be 99 and the minimum 10.
+In the first round:
```
-x => [99 , 98, 97 ...]
-y => [99]
+x = [99 , 98, 97 ...]
+y = [99]
```
-And already here we have our result which is `9009[91,99]`
-Although the loops has to continue to make us sure there are no higher value.
+And already we have our result: `9009[91,99]`
+Although the loops have to continue to make us sure there are no higher values.
```
-x => [98, 97, 96 ...]
-y => [99, 98]
+x = [98, 97, 96 ...]
+y = [99, 98]
...
-x => [90, 89, 88 ...]
-y => [99, 98,97,96,95,94,93,92,91,90]
-```
+x = [90, 89, 88 ...]
+y = [99, 98,97,96,95,94,93,92,91,90]
Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
-Meaning running beyond this point wont give us any higher value than 9009.
+Meaning that running beyond this point won't give us any values higher than 9009.
That is why we introduce the `was_bigger` variable.
-With that variable we can check the inner loop if it has been bigger than the current result.
+With `was_bigger`, we can check if the inner loop has a bigger value than the current result.
If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
We do that by using the [`break`][break] statement.
-If we hadn't modified the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
+If we hadn't modified the direction of the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
-The smallest function is optimized in a similar way as largest function compared to the original approach.
+The `smallest` function is optimized in a similar way as `largest` function compared to the original approach.
The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
index b2fe86d0acb..5f5086f5b66 100644
--- a/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
@@ -39,38 +39,39 @@ def smallest(min_factor, max_factor):
return (result, answer)
```
-The largest and smallest functions are very similar.
+The `largest` and `smallest` functions are very similar here.
Although there are some small differences.
-The largest function starts with checking if the min_factor is bigger than the max_factor.
+The `largest` function starts with checking if the min_factor is bigger than the max_factor.
If it is, it raises a [`ValueError`][value-error].
After that, it initializes the result to 0 and the answer to an empty list.
-Then it starts `for in range` loop.
-The first for loop iterates over the range of numbers from min_factor to max_factor.
-The second for loop iterates over the same range of numbers.
+Then it starts a `for in range` loop.
+The outer `for` loop iterates over the range of numbers from min_factor to max_factor.
+The inner `for` loop iterates over the same range of numbers.
Then it checks if the product of the two numbers is bigger than the result.
If it is, it converts the product to a string and checks if it is a palindrome.
We can check if a string is a palindrome by comparing it to its reverse.
There are multiple ways to reverse a string.
-The most common way is to use the slice notation.
+The most common way is to use [slice notation][slice-notation].
The slice notation is `string[start:stop:step]`.
You can read more about it in: [reverse string with slice][string-reverse].
-If it is, it checks if the product is bigger than the result.
-If it is, it clears the answer list and sets the result to the product.
+If the string is a palindrome, the solution next checks if the product is bigger than the current result.
+If the product is bigger, the answer list is cleared, and the current result is set to the most recent product.
Then it appends the two numbers to the answer list.
After the two for loops, it checks if the result is 0.
If it is, it sets the result to [`None`][none].
Then it returns the result and the answer.
-The smallest one is very similar.
+The `smallest` one is very similar.
The differences are that it checks if the product is smaller than the result or if the result is 0.
And that it reset result outside of the if statement.
Instead of inside of it.
[none]: https://realpython.com/null-in-python/
+[slice-notation]: https://www.learnbyexample.org/python-list-slicing/
[string-reverse]: https://realpython.com/reverse-string-python/#reversing-strings-through-slicing
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/palindrome-products/.articles/performance/content.md b/exercises/practice/palindrome-products/.articles/performance/content.md
index f0bce43e18a..f052088335e 100644
--- a/exercises/practice/palindrome-products/.articles/performance/content.md
+++ b/exercises/practice/palindrome-products/.articles/performance/content.md
@@ -1,6 +1,6 @@
# Performance
-In this approach, we'll find out the performance difference between approaches for palindrome product in Python.
+In this article, we'll examine the performance difference between approaches for `palindrome-products` in Python.
The [approaches page][approaches] lists two approaches to this exercise:
@@ -10,7 +10,8 @@ The [approaches page][approaches] lists two approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These test were run in windows 11, using Python 3.11.1.
+These tests were run in windows 11, using Python 3.11.1.
+Your system results may vary.
```
largest, min=1, max=1000 : 0.05235219991300255
From 6d14e3c949aa6fbe739674baf838ec90d7118a16 Mon Sep 17 00:00:00 2001
From: Matthijs <19817960+MatthijsBlom@users.noreply.github.com>
Date: Sat, 7 Jan 2023 20:54:58 +0100
Subject: [PATCH 413/932] Clarify Currency Exchange instructions
---
exercises/concept/currency-exchange/.docs/instructions.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/currency-exchange/.docs/instructions.md b/exercises/concept/currency-exchange/.docs/instructions.md
index 03f7e6a1e3b..3bb6bff9719 100644
--- a/exercises/concept/currency-exchange/.docs/instructions.md
+++ b/exercises/concept/currency-exchange/.docs/instructions.md
@@ -52,8 +52,8 @@ Unfortunately, the booth gets to keep the remainder/change as an added bonus.
Create the `get_number_of_bills()` function, taking `budget` and `denomination`.
-This function should return the _number of new currency bills_ that you can receive within the given _budget_.
-In other words: How many _whole bills_ of new currency fit into the amount of old currency you have in your budget?
+This function should return the _number of currency bills_ that you can receive within the given _budget_.
+In other words: How many _whole bills_ of currency fit into the amount of currency you have in your budget?
Remember -- you can only receive _whole bills_, not fractions of bills, so remember to divide accordingly.
Effectively, you are rounding _down_ to the nearest whole bill/denomination.
From cac72fbb92f90087d8d87f2c40733c60796572d5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 18:55:52 +0100
Subject: [PATCH 414/932] Started
---
.../.approaches/config.json | 29 +++++++
.../.approaches/if-else/content.md | 32 ++++++++
.../.approaches/if-else/snippet.txt | 11 +++
.../.approaches/introduction.md | 82 +++++++++++++++++++
.../.approaches/recursion/content.md | 46 +++++++++++
.../.approaches/recursion/snippet.txt | 7 ++
.../.approaches/ternary-operator/content.md | 31 +++++++
.../.approaches/ternary-operator/snippet.txt | 8 ++
.../collatz-conjecture/.articles/config.json | 11 +++
.../.articles/performance/code/Benchmark.py | 43 ++++++++++
.../.articles/performance/content.md | 32 ++++++++
.../.articles/performance/snippet.md | 5 ++
12 files changed, 337 insertions(+)
create mode 100644 exercises/practice/collatz-conjecture/.approaches/config.json
create mode 100644 exercises/practice/collatz-conjecture/.approaches/if-else/content.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
create mode 100644 exercises/practice/collatz-conjecture/.approaches/introduction.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/recursion/content.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt
create mode 100644 exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
create mode 100644 exercises/practice/collatz-conjecture/.articles/config.json
create mode 100644 exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
create mode 100644 exercises/practice/collatz-conjecture/.articles/performance/content.md
create mode 100644 exercises/practice/collatz-conjecture/.articles/performance/snippet.md
diff --git a/exercises/practice/collatz-conjecture/.approaches/config.json b/exercises/practice/collatz-conjecture/.approaches/config.json
new file mode 100644
index 00000000000..2ba3bf4c5b8
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["bethanyg", "meatball133"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "7b3aeaef-11ef-427c-9086-d2c6d4c022f1",
+ "slug": "if-else",
+ "title": "If/Else",
+ "blurb": "Use if and else",
+ "authors": ["bethanyg", "meatball133"]
+ },
+ {
+ "uuid": "a2d8742b-9516-44c8-832d-111874430de0",
+ "slug": "ternary-operator",
+ "title": "Ternary operator",
+ "blurb": "Use a ternary operator",
+ "authors": ["bethanyg", "meatball133"]
+ },
+ {
+ "uuid": "01da60d9-c133-41de-90fd-afbba7df2980",
+ "slug": "recursion",
+ "title": "Recursion",
+ "blurb": "Use recursion",
+ "authors": ["bethanyg", "meatball133"]
+ }
+ ]
+}
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
new file mode 100644
index 00000000000..c78fc06a7e0
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
@@ -0,0 +1,32 @@
+# If / Else
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
+```
+
+This approach starts with checking if the number is less than or equal to zero.
+If it is, then it raises a [`ValueError`][value-error].
+After that we declare a counter variable and set it to zero.
+Then we start a [`while` loop][while-loop] that will run until the number is equal to one.
+Meaning the loop wont run if the number is already one.
+
+Inside the loop we check if the number is even.
+If it is, then we divide it by two.
+If it isn't, then we multiply it by three and add one.
+After that we increment the counter by one.
+After the loop is done, we return the counter variable.
+
+We have to use a `while` loop here because we don't know how many times the loop will run.
+
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
+[while-loop]: https://realpython.com/python-while-loop/
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
new file mode 100644
index 00000000000..eee0d4a3dc6
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
@@ -0,0 +1,11 @@
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
new file mode 100644
index 00000000000..20fbe6f90f8
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -0,0 +1,82 @@
+# Introduction
+
+There are various approaches to solving the Collatz Conjecture exercise in Python.
+You can for example use a while loop or a recursive function.
+You can use if and else statements or the ternary operator.
+
+## General guidance
+
+The key to this exercise is to check if the number and then do the correct operation.
+Under this process you are suppose to count how many steps it takes to get to one.
+
+## Approach: If/Else
+
+This is a good way to solve the exercise, it is easy to understand and it is very readable.
+The reason to why you might not want to use this approach is because it is longer than the other approaches.
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
+```
+
+For more information, check the [if/else approach][approach-if-else].
+
+## Approach: Ternary operator
+
+In this approach we use the ternary operator.
+It allows us to write a one-line if and else statement.
+This makes the code more concise.
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
+```
+
+For more information, check the [Ternary operator approach][approach-ternary-operator].
+
+## Approach: Recursive function
+
+In this approach we use a recursive function.
+A recursive function is a function that calls itself.
+The reason to why you want to use this approach is because it is more concise than the other approaches.
+Since it doesn't need a counter variable.
+
+The reason to why you might not want to use this approach is that python has a limit on how many times a function can call itself.
+This limit is 1000 times.
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps(number)
+```
+
+For more information, check the [Recursion approach][approach-recursion].
+
+## Benchmarks
+
+To get a better understanding of the performance of the different approaches, we have created benchmarks.
+For more information, check the [Performance article].
+
+[performance article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
+[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
new file mode 100644
index 00000000000..b452045ed8f
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
@@ -0,0 +1,46 @@
+# Recursion
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps(number)
+```
+
+This approach uses [concept:python/recursion]() to solve the problem.
+Recursion is a programming technique where a function calls itself.
+Recursion is a powerful technique, but can be more tricky.
+Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
+
+This approach starts with checking if the number is less than or equal to zero.
+If it is, then it raises a [`ValueError`][value-error].
+
+After that we check if the number is equal to one.
+If it is, then we return zero.
+
+Then we use the same ternary operator as in the [ternary operator][ternary-operator] approach.
+We declare that number is equal to the result of the ternary operator.
+The ternary operator checks if the number is even.
+If it is, then we divide it by two.
+If it isn't, then we multiply it by three and add one.
+
+After that we return one plus the result of calling the `steps` function with the new number.
+This is the recursion part.
+
+Doing this exercise with recursion removes the need for a "counter" variable.
+Since what we do is that if the number is not one we do `1 + steps(number)`.
+Then that function can do the same thing.
+Meaning we can get a long chain of `1 + steps(number)` until we reach one and add 0.
+That will translate to something like this: `1 + 1 + 1 + 1 + 0`.
+
+In python we can't have a function call itself more than 1000 times.
+
+[clojure]: https://exercism.org/tracks/clojure
+[elixir]: https://exercism.org/tracks/elixir
+[haskell]: https://exercism.org/tracks/haskell
+[recursion]: https://realpython.com/python-thinking-recursively/
+[ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt
new file mode 100644
index 00000000000..addae0aeaef
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt
@@ -0,0 +1,7 @@
+def steps(number):
+ if number < 1:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps(number)
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
new file mode 100644
index 00000000000..518455461ce
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
@@ -0,0 +1,31 @@
+# Ternary operator
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
+```
+
+If it is, then it raises a [`ValueError`][value-error].
+After that we declare a counter variable and set it to zero.
+
+Then we start a `while` loop that will run until the number is equal to one.
+Meaning the loop wont run if the number is already one.
+
+Inside of the loop we have a [ternary operator][ternary-operator].
+The ternary operator is a one-line `if` and `else` statement.
+That means that we can make the code more concise by using it.
+We declare that number is equal to the result of the ternary operator.
+The ternary operator checks if the number is even.
+If it is, then we divide it by two.
+If it isn't, then we multiply it by three and add one.
+After that we increment the counter by one.
+After the loop is done, we return the counter variable.
+
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
new file mode 100644
index 00000000000..5e6fcd8c036
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
@@ -0,0 +1,8 @@
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
diff --git a/exercises/practice/collatz-conjecture/.articles/config.json b/exercises/practice/collatz-conjecture/.articles/config.json
new file mode 100644
index 00000000000..afb7c6f9e64
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "c6435e29-fd40-42a4-95ba-7c06b9b48f7a",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach for collatz conjecture.",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py b/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..aea0162e59f
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
@@ -0,0 +1,43 @@
+import timeit
+
+def steps_if(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
+
+def steps_recursion(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps_recursion(number)
+
+def steps_ternary(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
+
+
+starttime = timeit.default_timer()
+steps_recursion(100000)
+print("Steps with recursion :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+steps_ternary(100000)
+print("Steps with ternary :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+steps_if(100000)
+print("Steps with if/else :", timeit.default_timer() - starttime)
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
new file mode 100644
index 00000000000..c5c62aacefc
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -0,0 +1,32 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently calculate Collatz Conjecture in Python.
+
+The [approaches page][approaches] lists three approaches to this exercise:
+
+1. [Using recursion][approach-recursion]
+2. [Using the ternary operator][approach-ternary-operator]
+3. [Using the if/else][approach-if-else]
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+These test where run in windows 11, using Python 3.11.1.
+
+```
+Steps with recursion : 0.00015059998258948326
+Steps with ternary : 1.8699909560382366e-05
+Steps with if/else : 1.8799910321831703e-05
+```
+
+## Conclusion
+
+The fastest approach is the one using the ternary operator or the if/else statement.
+The slowest approach is the one using recursion, that is because Python isn't optimized for recursion.
+
+[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
+[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/snippet.md b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
new file mode 100644
index 00000000000..d79a6762149
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
@@ -0,0 +1,5 @@
+```
+Steps with recursion : 0.00015059998258948326
+Steps with ternary : 1.8699909560382366e-05
+Steps with if/else : 1.8799910321831703e-05
+```
From 4771a453e84f0c756a150c7d64bd4be4b1742864 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 19:09:18 +0100
Subject: [PATCH 415/932] fix-snippet
---
.../.approaches/if-else/snippet.txt | 19 ++++++++-----------
.../.approaches/ternary-operator/snippet.txt | 2 +-
2 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
index eee0d4a3dc6..f16ae892107 100644
--- a/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
@@ -1,11 +1,8 @@
-def steps(number):
- if number <= 0:
- raise ValueError("Only positive integers are allowed")
- counter = 0
- while number != 1:
- if number % 2 == 0:
- number /= 2
- else:
- number = number * 3 + 1
- counter += 1
- return counter
+counter = 0
+while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+return counter
\ No newline at end of file
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
index 5e6fcd8c036..cc87d02e236 100644
--- a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
@@ -5,4 +5,4 @@ def steps(number):
while number != 1:
number = number / 2 if number % 2 == 0 else number * 3 + 1
counter += 1
- return counter
+ return counter
\ No newline at end of file
From 4e047cd722b1512722a01fc344dacf103982408f Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 20:08:15 +0100
Subject: [PATCH 416/932] fix
---
.../.approaches/if-else/content.md | 6 +++---
.../.approaches/introduction.md | 16 ++++++++--------
.../.approaches/recursion/content.md | 11 ++++++-----
.../.approaches/ternary-operator/content.md | 12 +++++++-----
.../.articles/performance/content.md | 2 +-
5 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
index c78fc06a7e0..040b6bccfc2 100644
--- a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
@@ -16,14 +16,14 @@ def steps(number):
This approach starts with checking if the number is less than or equal to zero.
If it is, then it raises a [`ValueError`][value-error].
-After that we declare a counter variable and set it to zero.
+After that, we declare a counter variable and set it to zero.
Then we start a [`while` loop][while-loop] that will run until the number is equal to one.
-Meaning the loop wont run if the number is already one.
+Meaning the loop won't run if the number is already one.
Inside the loop we check if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
-After that we increment the counter by one.
+After that, we increment the counter by one.
After the loop is done, we return the counter variable.
We have to use a `while` loop here because we don't know how many times the loop will run.
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
index 20fbe6f90f8..0ff3a741bd3 100644
--- a/exercises/practice/collatz-conjecture/.approaches/introduction.md
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -2,17 +2,17 @@
There are various approaches to solving the Collatz Conjecture exercise in Python.
You can for example use a while loop or a recursive function.
-You can use if and else statements or the ternary operator.
+You can also solve it by using if and else statements or the ternary operator.
## General guidance
The key to this exercise is to check if the number and then do the correct operation.
-Under this process you are suppose to count how many steps it takes to get to one.
+Under this process you are supposed to count how many steps it takes to get to one.
## Approach: If/Else
This is a good way to solve the exercise, it is easy to understand and it is very readable.
-The reason to why you might not want to use this approach is because it is longer than the other approaches.
+The reason why you might not want to use this approach is because it is longer than the other approaches.
```python
def steps(number):
@@ -53,10 +53,10 @@ For more information, check the [Ternary operator approach][approach-ternary-ope
In this approach we use a recursive function.
A recursive function is a function that calls itself.
-The reason to why you want to use this approach is because it is more concise than the other approaches.
+The reason why you want to use this approach is because it is more concise than the other approaches.
Since it doesn't need a counter variable.
-The reason to why you might not want to use this approach is that python has a limit on how many times a function can call itself.
+The reason why you might not want to use this approach is that python has a limit on how many times a function can call itself.
This limit is 1000 times.
```python
@@ -74,9 +74,9 @@ For more information, check the [Recursion approach][approach-recursion].
## Benchmarks
To get a better understanding of the performance of the different approaches, we have created benchmarks.
-For more information, check the [Performance article].
+For more information, check the [Performance article][performance-article].
-[performance article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
-[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[performance-article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
index b452045ed8f..feeb2fc8325 100644
--- a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
@@ -12,13 +12,13 @@ def steps(number):
This approach uses [concept:python/recursion]() to solve the problem.
Recursion is a programming technique where a function calls itself.
-Recursion is a powerful technique, but can be more tricky.
+It is a powerful technique, but can be more tricky to implement than a while loop.
Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
This approach starts with checking if the number is less than or equal to zero.
If it is, then it raises a [`ValueError`][value-error].
-After that we check if the number is equal to one.
+After that, we check if the number is equal to one.
If it is, then we return zero.
Then we use the same ternary operator as in the [ternary operator][ternary-operator] approach.
@@ -27,16 +27,17 @@ The ternary operator checks if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
-After that we return one plus the result of calling the `steps` function with the new number.
+After that, we return one plus the result of calling the `steps` function with the new number.
This is the recursion part.
Doing this exercise with recursion removes the need for a "counter" variable.
Since what we do is that if the number is not one we do `1 + steps(number)`.
-Then that function can do the same thing.
+Then that function can do the same thing again.
Meaning we can get a long chain of `1 + steps(number)` until we reach one and add 0.
That will translate to something like this: `1 + 1 + 1 + 1 + 0`.
-In python we can't have a function call itself more than 1000 times.
+In Python, we can't have a function call itself more than 1000 times.
+Which mean problems that require a lot of recursion will fail.
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
index 518455461ce..32f0ad8dd1a 100644
--- a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
@@ -11,20 +11,22 @@ def steps(number):
return counter
```
+This approach starts with checking if the number is less than or equal to zero.
If it is, then it raises a [`ValueError`][value-error].
-After that we declare a counter variable and set it to zero.
+After that, we declare a counter variable and set it to zero.
Then we start a `while` loop that will run until the number is equal to one.
-Meaning the loop wont run if the number is already one.
+Meaning the loop won't run if the number is already one.
-Inside of the loop we have a [ternary operator][ternary-operator].
-The ternary operator is a one-line `if` and `else` statement.
+Inside the loop we have a [ternary operator][ternary-operator].
+A ternary operator is a one-line `if` and `else` statement.
That means that we can make the code more concise by using it.
+
We declare that number is equal to the result of the ternary operator.
The ternary operator checks if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
-After that we increment the counter by one.
+After that, we increment the counter by one.
After the loop is done, we return the counter variable.
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index c5c62aacefc..eccade40cbb 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -26,7 +26,7 @@ The slowest approach is the one using recursion, that is because Python isn't op
[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
-[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
[timeit]: https://docs.python.org/3/library/timeit.html
From 85cd543f5f44f44a1ed3ea0aba970774eeb7849a Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 22:10:17 +0100
Subject: [PATCH 417/932] fix
---
.../collatz-conjecture/.articles/performance/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index eccade40cbb..b18aaba87af 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -11,7 +11,7 @@ The [approaches page][approaches] lists three approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These test where run in windows 11, using Python 3.11.1.
+These test were run in windows 11, using Python 3.11.1.
```
Steps with recursion : 0.00015059998258948326
From 9b7c79897d1fbb76f69bb7af8dd0a746b726a0dc Mon Sep 17 00:00:00 2001
From: Carl
Date: Sat, 31 Dec 2022 15:54:08 +0100
Subject: [PATCH 418/932] Re-run the test on python 3.11.1 instead of python
3.10
---
.../collatz-conjecture/.articles/performance/content.md | 8 ++++----
.../collatz-conjecture/.articles/performance/snippet.md | 6 +++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index b18aaba87af..7e683803917 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -14,15 +14,15 @@ To benchmark the approaches, we wrote a [small benchmark application][benchmark-
These test were run in windows 11, using Python 3.11.1.
```
-Steps with recursion : 0.00015059998258948326
-Steps with ternary : 1.8699909560382366e-05
-Steps with if/else : 1.8799910321831703e-05
+Steps with recursion : 4.1499966755509377e-05
+Steps with ternary : 2.1900050342082977e-05
+Steps with if/else : 2.0900042727589607e-05
```
## Conclusion
The fastest approach is the one using the ternary operator or the if/else statement.
-The slowest approach is the one using recursion, that is because Python isn't optimized for recursion.
+The slowest approach is the one using recursion, that is because Python isn't as optimized for recursion.
[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/snippet.md b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
index d79a6762149..b95418a5122 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
@@ -1,5 +1,5 @@
```
-Steps with recursion : 0.00015059998258948326
-Steps with ternary : 1.8699909560382366e-05
-Steps with if/else : 1.8799910321831703e-05
+Steps with recursion : 4.1499966755509377e-05
+Steps with ternary : 2.1900050342082977e-05
+Steps with if/else : 2.0900042727589607e-05
```
From 707bba1658f878daeb48d12bf84fbcf72a090670 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 10 Jan 2023 22:11:53 +0100
Subject: [PATCH 419/932] Apply suggestions from code review
Co-authored-by: BethanyG
---
.../.approaches/if-else/content.md | 4 +--
.../.approaches/introduction.md | 12 ++++----
.../.approaches/recursion/content.md | 30 +++++++++++--------
.../.approaches/ternary-operator/content.md | 20 ++++++-------
.../.articles/performance/content.md | 8 ++---
5 files changed, 38 insertions(+), 36 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
index 040b6bccfc2..56cc8e4257c 100644
--- a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
@@ -24,9 +24,9 @@ Inside the loop we check if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
After that, we increment the counter by one.
-After the loop is done, we return the counter variable.
+After the loop completes, we return the counter variable.
-We have to use a `while` loop here because we don't know how many times the loop will run.
+We use a `while` loop here because we don't know exactly how many times the loop will run.
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
[while-loop]: https://realpython.com/python-while-loop/
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
index 0ff3a741bd3..4e9c155c355 100644
--- a/exercises/practice/collatz-conjecture/.approaches/introduction.md
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -32,9 +32,8 @@ For more information, check the [if/else approach][approach-if-else].
## Approach: Ternary operator
-In this approach we use the ternary operator.
-It allows us to write a one-line if and else statement.
-This makes the code more concise.
+In this approach we replace the `if/else` multi-line construction with a [conditional expression][conditional-expression], sometimes called a _ternary operator_.
+This syntax allows us to write a one-line `if/ else` check, making the code more concise.
```python
def steps(number):
@@ -53,11 +52,9 @@ For more information, check the [Ternary operator approach][approach-ternary-ope
In this approach we use a recursive function.
A recursive function is a function that calls itself.
-The reason why you want to use this approach is because it is more concise than the other approaches.
-Since it doesn't need a counter variable.
+This approach can be more concise than other approaches, and may also be more readable for some audiences.
-The reason why you might not want to use this approach is that python has a limit on how many times a function can call itself.
-This limit is 1000 times.
+The reason why you might not want to use this approach is that Python has a [recursion limit][recursion-limit] with a default of 1000.
```python
def steps(number):
@@ -79,4 +76,5 @@ For more information, check the [Performance article][performance-article].
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions
[performance-article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
index feeb2fc8325..cdfa25335c4 100644
--- a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
@@ -21,27 +21,31 @@ If it is, then it raises a [`ValueError`][value-error].
After that, we check if the number is equal to one.
If it is, then we return zero.
-Then we use the same ternary operator as in the [ternary operator][ternary-operator] approach.
-We declare that number is equal to the result of the ternary operator.
-The ternary operator checks if the number is even.
-If it is, then we divide it by two.
-If it isn't, then we multiply it by three and add one.
+We then use the same conditional expression/ternary operator as the [ternary operator][ternary-operator] approach does.
+We assign **number** to the result of the conditional expression.
+The expression checks if the number is even.
+If the number is even, we divide it by two.
+If it isn't, we multiply it by three and add one.
-After that, we return one plus the result of calling the `steps` function with the new number.
+After that, we `return` one plus the result of calling the `steps` function with the new number value.
This is the recursion part.
-Doing this exercise with recursion removes the need for a "counter" variable.
-Since what we do is that if the number is not one we do `1 + steps(number)`.
-Then that function can do the same thing again.
-Meaning we can get a long chain of `1 + steps(number)` until we reach one and add 0.
-That will translate to something like this: `1 + 1 + 1 + 1 + 0`.
+Solving this exercise with recursion removes the need for a "counter" variable and the instantiation of a `loop`.
+If the number is not equal to one, we call `1 + steps(number)`.
+Then the `steps` function can execute the same code again with new values.
+Meaning we can get a long chain or stack of `1 + steps(number)` until the number reaches one and the code adds 0.
+That translates to something like this: `1 + 1 + 1 + 1 + 0`.
-In Python, we can't have a function call itself more than 1000 times.
-Which mean problems that require a lot of recursion will fail.
+In Python, we can't have a function call itself more than 1000 times by default.
+Code that exceeds this recursion limit will throw a [RecursionError][recursion-error].
+While it is possible to adjust the [recursion limit][recursion-limit], doing so risks crashing Python and may also crash your system.
+Casually raising the recursion limit is not recommended.
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
[haskell]: https://exercism.org/tracks/haskell
[recursion]: https://realpython.com/python-thinking-recursively/
+[recursion-error]: https://docs.python.org/3/library/exceptions.html#RecursionError
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
[ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
index 32f0ad8dd1a..7a1368cf6ec 100644
--- a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
@@ -12,22 +12,22 @@ def steps(number):
```
This approach starts with checking if the number is less than or equal to zero.
-If it is, then it raises a [`ValueError`][value-error].
+If it is, then a [`ValueError`][value-error] is raised.
-After that, we declare a counter variable and set it to zero.
+After that, a counter variable is assigned to zero.
Then we start a `while` loop that will run until the number is equal to one.
Meaning the loop won't run if the number is already one.
-Inside the loop we have a [ternary operator][ternary-operator].
-A ternary operator is a one-line `if` and `else` statement.
-That means that we can make the code more concise by using it.
+Inside the loop we have a [ternary operator][ternary-operator] or [conditional expression][conditional-expression].
+A ternary operator/conditional expression can be viewed as a one-line `if/else` statement.
+Using a one-line construct can make the code more concise.
-We declare that number is equal to the result of the ternary operator.
-The ternary operator checks if the number is even.
+We assign the number value to the result of the ternary operator.
+The ternary operator/conditional expression checks if the number is even.
If it is, then we divide it by two.
-If it isn't, then we multiply it by three and add one.
-After that, we increment the counter by one.
-After the loop is done, we return the counter variable.
+If the number is not even, we multiply by three and add one.
+Then the counter is incremented by one.
+When the loop completes, we return the counter value.
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index 7e683803917..5b1c3d43fd6 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -1,6 +1,6 @@
# Performance
-In this approach, we'll find out how to most efficiently calculate Collatz Conjecture in Python.
+In this approach, we'll find out how to most efficiently calculate the Collatz Conjecture in Python.
The [approaches page][approaches] lists three approaches to this exercise:
@@ -11,7 +11,7 @@ The [approaches page][approaches] lists three approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These test were run in windows 11, using Python 3.11.1.
+These tests were run in windows 11, using Python 3.11.1.
```
Steps with recursion : 4.1499966755509377e-05
@@ -21,8 +21,8 @@ Steps with if/else : 2.0900042727589607e-05
## Conclusion
-The fastest approach is the one using the ternary operator or the if/else statement.
-The slowest approach is the one using recursion, that is because Python isn't as optimized for recursion.
+The fastest approach is the one using the `if/else` statement, followed by the one using the ternary operator/conditional expression.
+The slowest approach is the one using recursion, probably because Python isn't as optimized for recursion as it is for iteration.
[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
From 957c260db2da549ec4af1191bf366f3b59a92edf Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 10 Jan 2023 22:16:49 +0100
Subject: [PATCH 420/932] Update
exercises/practice/collatz-conjecture/.approaches/introduction.md
Co-authored-by: BethanyG
---
.../practice/collatz-conjecture/.approaches/introduction.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
index 4e9c155c355..6a6d487f269 100644
--- a/exercises/practice/collatz-conjecture/.approaches/introduction.md
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -75,6 +75,7 @@ For more information, check the [Performance article][performance-article].
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions
[performance-article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
From 04edbac62469cf1ed3d4ab49bce356b522328d37 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 23:39:35 +0100
Subject: [PATCH 421/932] Fix uuid
---
.../practice/collatz-conjecture/.approaches/config.json | 6 +++---
exercises/practice/collatz-conjecture/.articles/config.json | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/config.json b/exercises/practice/collatz-conjecture/.approaches/config.json
index 2ba3bf4c5b8..5730c94d978 100644
--- a/exercises/practice/collatz-conjecture/.approaches/config.json
+++ b/exercises/practice/collatz-conjecture/.approaches/config.json
@@ -5,21 +5,21 @@
},
"approaches": [
{
- "uuid": "7b3aeaef-11ef-427c-9086-d2c6d4c022f1",
+ "uuid": "d92adc98-36fd-49bb-baf5-e4588387841c",
"slug": "if-else",
"title": "If/Else",
"blurb": "Use if and else",
"authors": ["bethanyg", "meatball133"]
},
{
- "uuid": "a2d8742b-9516-44c8-832d-111874430de0",
+ "uuid": "d7703aef-1510-4ec8-b6ce-ca608b5b8f70",
"slug": "ternary-operator",
"title": "Ternary operator",
"blurb": "Use a ternary operator",
"authors": ["bethanyg", "meatball133"]
},
{
- "uuid": "01da60d9-c133-41de-90fd-afbba7df2980",
+ "uuid": "b1220645-124a-4994-96c4-3b2b710fd562",
"slug": "recursion",
"title": "Recursion",
"blurb": "Use recursion",
diff --git a/exercises/practice/collatz-conjecture/.articles/config.json b/exercises/practice/collatz-conjecture/.articles/config.json
index afb7c6f9e64..1bab0021696 100644
--- a/exercises/practice/collatz-conjecture/.articles/config.json
+++ b/exercises/practice/collatz-conjecture/.articles/config.json
@@ -1,7 +1,7 @@
{
"articles": [
{
- "uuid": "c6435e29-fd40-42a4-95ba-7c06b9b48f7a",
+ "uuid": "c37be489b-791a-463a-94c0-564ca0277da2",
"slug": "performance",
"title": "Performance deep dive",
"blurb": "Deep dive to find out the most performant approach for collatz conjecture.",
From 759ec3f804a409abbafa5c0882d4fa4c25e5e45b Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 23:43:16 +0100
Subject: [PATCH 422/932] fix uuid
---
exercises/practice/collatz-conjecture/.articles/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/collatz-conjecture/.articles/config.json b/exercises/practice/collatz-conjecture/.articles/config.json
index 1bab0021696..a9341a5153b 100644
--- a/exercises/practice/collatz-conjecture/.articles/config.json
+++ b/exercises/practice/collatz-conjecture/.articles/config.json
@@ -1,7 +1,7 @@
{
"articles": [
{
- "uuid": "c37be489b-791a-463a-94c0-564ca0277da2",
+ "uuid": "f5071a22-f13a-4650-afea-b7aaee8f2b12",
"slug": "performance",
"title": "Performance deep dive",
"blurb": "Deep dive to find out the most performant approach for collatz conjecture.",
From d92309043536511b4fce63971f6aee2cc7fb8167 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 00:40:42 +0100
Subject: [PATCH 423/932] Started
---
.../.approaches/Dictionary/content.md | 51 ++++++++
.../.approaches/Dictionary/snippet.txt | 2 +
.../.approaches/Enum/content.md | 44 +++++++
.../.approaches/Enum/snippet.txt | 8 ++
.../scrabble-score/.approaches/config.json | 36 ++++++
.../.approaches/introduction.md | 115 ++++++++++++++++++
.../.approaches/nested-tuple/content.md | 32 +++++
.../.approaches/nested-tuple/snippet.txt | 8 ++
.../.approaches/two-sequences/content.md | 25 ++++
.../.approaches/two-sequences/snippet.txt | 5 +
10 files changed, 326 insertions(+)
create mode 100644 exercises/practice/scrabble-score/.approaches/Dictionary/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
create mode 100644 exercises/practice/scrabble-score/.approaches/Enum/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
create mode 100644 exercises/practice/scrabble-score/.approaches/config.json
create mode 100644 exercises/practice/scrabble-score/.approaches/introduction.md
create mode 100644 exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt
create mode 100644 exercises/practice/scrabble-score/.approaches/two-sequences/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/content.md b/exercises/practice/scrabble-score/.approaches/Dictionary/content.md
new file mode 100644
index 00000000000..55e7b1357fe
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Dictionary/content.md
@@ -0,0 +1,51 @@
+# Dictionary
+
+```python
+LETTER_SCORES = {
+ 'A': 1,
+ 'E': 1,
+ 'I': 1,
+ 'O': 1,
+ 'U': 1,
+ 'L': 1,
+ 'N': 1,
+ 'R': 1,
+ 'S': 1,
+ 'T': 1,
+ 'D': 2,
+ 'G': 2,
+ 'B': 3,
+ 'C': 3,
+ 'M': 3,
+ 'P': 3,
+ 'F': 4,
+ 'H': 4,
+ 'V': 4,
+ 'W': 4,
+ 'Y': 4,
+ 'K': 5,
+ 'J': 8,
+ 'X': 8,
+ 'Q': 10,
+ 'Z': 10
+}
+
+def score(word):
+ return sum(LETTER_SCORES[letter.upper()] for letter in word)
+```
+
+The code starts with initializing a constant that is a [dictionary][dictionary] that holds all the letters as different key and their respective score as a value.
+Then it defines a function that takes a word as an argument.
+
+The function returns the built in function [`sum`][sum] that takes a [generator expression][generator-expersion] that iterates over the letters in the word.
+What a generator expression does is that it generates the values on the fly.
+Meaning that it doesn't have to use a lot of memory since it uses the last value and generates the next value.
+
+Under the generation a letter is given from the string, then it is converted to upcase, and then being looked up at inside of the dictionary and the value is returned.
+
+There is also a very similar approach that uses a dictionary transposition.
+Although that approach requires more computational calculation therefore is this approach more efficient.
+
+[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[generator-expersion]: https://peps.python.org/pep-0289/
+[sum]: https://docs.python.org/3/library/functions.html#sum
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt b/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
new file mode 100644
index 00000000000..c0b4b3f9702
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
@@ -0,0 +1,2 @@
+def score(word):
+ return sum(LETTER_SCORES[letter.upper()] for letter in word)
\ No newline at end of file
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/content.md b/exercises/practice/scrabble-score/.approaches/Enum/content.md
new file mode 100644
index 00000000000..a9fd97b97ce
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Enum/content.md
@@ -0,0 +1,44 @@
+# Enum
+
+```python
+from enum import IntEnum
+
+class Scrabble(IntEnum):
+ A = E = I = O = U = L = N = R = S = T = 1
+ D = G = 2
+ B = C = M = P = 3
+ F = H = V = W = Y = 4
+ K = 5
+ J = X = 8
+ Q = Z = 10
+
+def score(word):
+ return sum(Scrabble[char.upper()] for char in word)
+```
+
+This approach uses an [`enum`][enum] to define the score of each letter.
+An `enum` or known as a enumerations is sets of named constant and is immutable.
+`enum` was added to python standard library also known as stdlib in python 3.4.
+
+This approach uses an [`intEnum`][int-enum] it works very similar to a normal `enum` but it has the added benefit that the values are integers.
+Thereby acts like integers.
+
+To use an `intEnum` you need to [import][import] it using: `from enum import IntEnum`.
+Then you can define the `enum` class.
+
+The `enum` class is defined by using the [`class`][classes] keyword.
+Then you need to specify the name of the class.
+
+After that is constant declared by giving the constant capital letters and the value is assigned by using the `=` operator.
+This approach works by giving all the letters as constants and then value of the constant is the score of the letter.
+After the `enum` is defined, the `score` function is defined.
+
+The `score` function takes a word as a parameter.
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
+But instead of looking up the value in a dictionary it looks it up in the `enum` class.
+
+[classes]: https://docs.python.org/3/tutorial/classes.html
+[enum]: https://docs.python.org/3/library/enum.html
+[generator-expersion]: https://peps.python.org/pep-0289/
+[int-enum]: https://docs.python.org/3/library/enum.html#enum.IntEnum
+[import]: https://docs.python.org/3/reference/import.html
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt b/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
new file mode 100644
index 00000000000..155b62b9c3e
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
@@ -0,0 +1,8 @@
+class Scrabble(IntEnum):
+ A = E = I = O = U = L = N = R = S = T = 1
+ D = G = 2
+ B = C = M = P = 3
+ F = H = V = W = Y = 4
+ K = 5
+ J = X = 8
+ Q = Z = 10
\ No newline at end of file
diff --git a/exercises/practice/scrabble-score/.approaches/config.json b/exercises/practice/scrabble-score/.approaches/config.json
new file mode 100644
index 00000000000..0fef67cf03c
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/config.json
@@ -0,0 +1,36 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "62f556c0-27a6-43e5-80ea-d7abd921f0e7",
+ "slug": "enum",
+ "title": "Enum",
+ "blurb": "Define a enum to solve the problem",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "21c0fe14-585f-4e14-9f31-1d294a8a622c",
+ "slug": "dictionary",
+ "title": "Dictionary",
+ "blurb": "Dictionary approach",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "34911050-b424-4289-9d3f-aa5f0e8f1630",
+ "slug": "nested-tuple",
+ "title": "Nested Tuple",
+ "blurb": "Nested Tuple approach",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "e9b8e10b-23c7-4c2a-afbe-3c3499468919",
+ "slug": "two-sequences",
+ "title": "Two Sequences",
+ "blurb": "Two Sequences approach",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
new file mode 100644
index 00000000000..4a294408274
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -0,0 +1,115 @@
+# Introduction
+
+There are various ways to solve `scrabble-score`.
+This approaches document shows different strategies to solve this exercise
+
+## General guidance
+
+The goal of this exercise is to write a function that calculates the scrabble score for a given word.
+The problem is that
+
+## Approach: Using a single dictionary
+
+Using a single dictionary is an approach, it is simple and fast.
+It is also very pythonic.
+
+```python
+LETTER_SCORES = {
+ 'A': 1,
+ 'E': 1,
+ 'I': 1,
+ 'O': 1,
+ 'U': 1,
+ 'L': 1,
+ 'N': 1,
+ 'R': 1,
+ 'S': 1,
+ 'T': 1,
+ 'D': 2,
+ 'G': 2,
+ 'B': 3,
+ 'C': 3,
+ 'M': 3,
+ 'P': 3,
+ 'F': 4,
+ 'H': 4,
+ 'V': 4,
+ 'W': 4,
+ 'Y': 4,
+ 'K': 5,
+ 'J': 8,
+ 'X': 8,
+ 'Q': 10,
+ 'Z': 10
+}
+
+def score(word):
+ return sum(LETTER_SCORES[letter.upper()] for letter in word)
+```
+
+For more information, check the [Dictionary Approach][dictionary-approach].
+
+## Approach: Using two sequences
+
+Using two sequences is an approach, it is fast.
+Although the reason you might not want to do this is that it is hard to read.
+
+```python
+KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
+SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
+
+def score(word):
+ return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
+```
+
+For more information, check the [Two Sequences Approach][two-sequences-approach].
+
+## Approach: Enum
+
+Using an `enum` is an approach, it is short and easy to read.
+Although it is more complicated since it uses a class.
+
+```python
+from enum import IntEnum
+
+class Scrabble(IntEnum):
+ A = E = I = O = U = L = N = R = S = T = 1
+ D = G = 2
+ B = C = M = P = 3
+ F = H = V = W = Y = 4
+ K = 5
+ J = X = 8
+ Q = Z = 10
+
+def score(word):
+ return sum(Scrabble[char.upper()] for char in word)
+```
+
+You can read more about how to achieve this optimization in: [Enum Approach][enum-approach].
+
+## Approach: Using a nested tuple
+
+Tuples in python is more memory efficent than using a dictonary in python.
+Although this solution since it is iterating over a tuple for every letter so is it slower.
+
+```python
+LETTERS_OF_SCORE = (
+ ("AEIOULNRST", 1),
+ ("DG", 2),
+ ("BCMP", 3),
+ ("FHVWY", 4),
+ ("K", 5),
+ ("JX", 8),
+ ("QZ", 10),
+)
+
+def score(word):
+ return sum(for character in word for letters, score in LETTERS_OF_SCORE if character in letters)
+```
+
+For more information, check the [Nested Tuple Approach][nested-tuple-approach].
+
+[dictionary-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/dictionary
+[enum-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/enum
+[nested-tuple-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/nested-tuple
+[two-sequences-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/two-sequences
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
new file mode 100644
index 00000000000..ff926817386
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -0,0 +1,32 @@
+# Nested Tuple
+
+```python
+LETTERS_OF_SCORE = (
+ ("AEIOULNRST", 1),
+ ("DG", 2),
+ ("BCMP", 3),
+ ("FHVWY", 4),
+ ("K", 5),
+ ("JX", 8),
+ ("QZ", 10),
+)
+
+def score(word):
+ return sum(score for character in word for letters, score in LETTERS_OF_SCORE if character.upper() in letters)
+```
+
+The code starts with initializing a constant with a [tuple][tuple] of tuples.
+Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score of the letters.
+
+Then it defines a function that takes a word as an argument.
+The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach].
+The difference is that it uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
+There we iterate over the characters in the word and then iterate over the tuples.
+There the tuple is unpacked into the letters and the score.
+You can read more about unpacking in the [concept:python/unpacking-and-multiple-assignment]().
+
+Then we check if the character is in the letters and if it is we return the score.
+
+[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
+[generator-expersion]: https://peps.python.org/pep-0289/
+[for-loop]: https://realpython.com/python-for-loop/
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt b/exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt
new file mode 100644
index 00000000000..d2b19ebbbfc
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt
@@ -0,0 +1,8 @@
+LETTERS_OF_SCORE = (
+ ("AEIOULNRST", 1),
+ ("DG", 2),
+ ("BCMP", 3),
+ ("FHVWY", 4),
+ ("K", 5),
+ ("JX", 8),
+ ("QZ", 10),)
\ No newline at end of file
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
new file mode 100644
index 00000000000..f677bb4b43c
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -0,0 +1,25 @@
+# Two sequences
+
+```python
+KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
+SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 + [10] * 2
+
+def score(word):
+ return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
+```
+
+This approach uses a string and a [list][list], both of these data types belongs to the data type [sequences][sequence].
+It has a constant with a string with letters and then it has a constant of a list with corresponding score for the same index as the string.
+
+The `score` function takes a word as a parameter.
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
+
+The difference is that instead of using a [dictionary][dictionary] and looked up the score inside.
+This approach gets the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list.
+Then takes that value and return that to the generator expression.
+
+[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[dictionary-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/dictionary
+[list]: https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
+[sequence]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
+[generator-expersion]: https://peps.python.org/pep-0289/
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt b/exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt
new file mode 100644
index 00000000000..fd37416b390
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt
@@ -0,0 +1,5 @@
+KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
+SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
+
+def score(word):
+ return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
From 85865aa8929a50c52b8fbecd7097ccd0aae20c6f Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 00:44:01 +0100
Subject: [PATCH 424/932] Fix dictonary name
---
.../.approaches/{Dictionary => dictionary}/content.md | 0
.../.approaches/{Dictionary => dictionary}/snippet.txt | 0
.../practice/scrabble-score/.approaches/{Enum => enum}/content.md | 0
.../scrabble-score/.approaches/{Enum => enum}/snippet.txt | 0
4 files changed, 0 insertions(+), 0 deletions(-)
rename exercises/practice/scrabble-score/.approaches/{Dictionary => dictionary}/content.md (100%)
rename exercises/practice/scrabble-score/.approaches/{Dictionary => dictionary}/snippet.txt (100%)
rename exercises/practice/scrabble-score/.approaches/{Enum => enum}/content.md (100%)
rename exercises/practice/scrabble-score/.approaches/{Enum => enum}/snippet.txt (100%)
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Dictionary/content.md
rename to exercises/practice/scrabble-score/.approaches/dictionary/content.md
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt b/exercises/practice/scrabble-score/.approaches/dictionary/snippet.txt
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
rename to exercises/practice/scrabble-score/.approaches/dictionary/snippet.txt
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Enum/content.md
rename to exercises/practice/scrabble-score/.approaches/enum/content.md
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt b/exercises/practice/scrabble-score/.approaches/enum/snippet.txt
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
rename to exercises/practice/scrabble-score/.approaches/enum/snippet.txt
From 12e692f032154f13fc610549dd7396807b41b42e Mon Sep 17 00:00:00 2001
From: meatball
Date: Tue, 10 Jan 2023 10:10:16 +0100
Subject: [PATCH 425/932] Fixes
---
.../.approaches/dictionary/content.md | 4 ++--
.../scrabble-score/.approaches/enum/content.md | 12 ++++++------
.../scrabble-score/.approaches/introduction.md | 12 ++++++------
.../.approaches/nested-tuple/content.md | 14 ++++++++------
.../.approaches/two-sequences/content.md | 9 +++++----
5 files changed, 27 insertions(+), 24 deletions(-)
diff --git a/exercises/practice/scrabble-score/.approaches/dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
index 55e7b1357fe..b0464818046 100644
--- a/exercises/practice/scrabble-score/.approaches/dictionary/content.md
+++ b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
@@ -34,8 +34,8 @@ def score(word):
return sum(LETTER_SCORES[letter.upper()] for letter in word)
```
-The code starts with initializing a constant that is a [dictionary][dictionary] that holds all the letters as different key and their respective score as a value.
-Then it defines a function that takes a word as an argument.
+The code starts with initializing a constant that is a [dictionary][dictionary] there each letter is a key and their respective score as a value.
+Then a function is defined that takes a word as an argument.
The function returns the built in function [`sum`][sum] that takes a [generator expression][generator-expersion] that iterates over the letters in the word.
What a generator expression does is that it generates the values on the fly.
diff --git a/exercises/practice/scrabble-score/.approaches/enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
index a9fd97b97ce..cad5b6bdec6 100644
--- a/exercises/practice/scrabble-score/.approaches/enum/content.md
+++ b/exercises/practice/scrabble-score/.approaches/enum/content.md
@@ -13,12 +13,12 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[char.upper()] for char in word)
+ return sum(Scrabble[character.upper()] for character in word)
```
This approach uses an [`enum`][enum] to define the score of each letter.
-An `enum` or known as a enumerations is sets of named constant and is immutable.
-`enum` was added to python standard library also known as stdlib in python 3.4.
+An `enum` or known as an **enumeration** is sets of named constant and is immutable.
+`enum` was added to python standard library (_also known as stdlib_) in python 3.4.
This approach uses an [`intEnum`][int-enum] it works very similar to a normal `enum` but it has the added benefit that the values are integers.
Thereby acts like integers.
@@ -29,13 +29,13 @@ Then you can define the `enum` class.
The `enum` class is defined by using the [`class`][classes] keyword.
Then you need to specify the name of the class.
-After that is constant declared by giving the constant capital letters and the value is assigned by using the `=` operator.
+After that is the constant in the enum declared by giving the constant capital letters and the value is assigned by using the `=` operator.
This approach works by giving all the letters as constants and then value of the constant is the score of the letter.
After the `enum` is defined, the `score` function is defined.
The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
-But instead of looking up the value in a dictionary it looks it up in the `enum` class.
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] but with a slight modification.
+Which is that instead of looking up the value in a dictionary it looks it up in the `enum` class.
[classes]: https://docs.python.org/3/tutorial/classes.html
[enum]: https://docs.python.org/3/library/enum.html
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
index 4a294408274..2891e55a6df 100644
--- a/exercises/practice/scrabble-score/.approaches/introduction.md
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -1,12 +1,12 @@
# Introduction
There are various ways to solve `scrabble-score`.
-This approaches document shows different strategies to solve this exercise
+This approache document shows different strategies to solve this exercise.
## General guidance
The goal of this exercise is to write a function that calculates the scrabble score for a given word.
-The problem is that
+The problem is that the scrabble score is calculated by the sum of the scores of each letter in the word.
## Approach: Using a single dictionary
@@ -51,7 +51,7 @@ For more information, check the [Dictionary Approach][dictionary-approach].
## Approach: Using two sequences
-Using two sequences is an approach, it is fast.
+Using two sequences is an approach, it removes the need of using a nested data structure or a dictonary.
Although the reason you might not want to do this is that it is hard to read.
```python
@@ -67,7 +67,7 @@ For more information, check the [Two Sequences Approach][two-sequences-approach]
## Approach: Enum
Using an `enum` is an approach, it is short and easy to read.
-Although it is more complicated since it uses a class.
+Although it is more complicated since it uses a oop (object oriented programmering) elements.
```python
from enum import IntEnum
@@ -82,10 +82,10 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[char.upper()] for char in word)
+ return sum(Scrabble[letter.upper()] for letter in word)
```
-You can read more about how to achieve this optimization in: [Enum Approach][enum-approach].
+For more information, check the [Enum Approach][enum-approach].
## Approach: Using a nested tuple
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
index ff926817386..4498d1841f0 100644
--- a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -15,13 +15,15 @@ def score(word):
return sum(score for character in word for letters, score in LETTERS_OF_SCORE if character.upper() in letters)
```
-The code starts with initializing a constant with a [tuple][tuple] of tuples.
-Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score of the letters.
+The code starts with initializing a constant with a [tuple][tuple] of tuples (_also known as a nested tuple_).
+Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score for the letters.
-Then it defines a function that takes a word as an argument.
-The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach].
-The difference is that it uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
-There we iterate over the characters in the word and then iterate over the tuples.
+Then a function is defined that takes a word as an argument.
+The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach] but has some slight modifcations.
+
+The difference is that this one uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
+We first iterate over the characters in the word and then iterate over the tuples.
+Which means that for each letter are we iterating over all of the tuples.
There the tuple is unpacked into the letters and the score.
You can read more about unpacking in the [concept:python/unpacking-and-multiple-assignment]().
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
index f677bb4b43c..944da042983 100644
--- a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -8,13 +8,14 @@ def score(word):
return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
```
-This approach uses a string and a [list][list], both of these data types belongs to the data type [sequences][sequence].
-It has a constant with a string with letters and then it has a constant of a list with corresponding score for the same index as the string.
+This approach uses a string and a [list][list], both of these data types belongs to the parent data type [sequences][sequence].
+The code starts with defining a string constant with letters.
+Then another constant is definded which is a list with corresponding score for the same index as the string.
The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] with some slight modifications.
-The difference is that instead of using a [dictionary][dictionary] and looked up the score inside.
+The difference is that instead of using a [dictionary][dictionary] and looking up the score inside of a dictonary.
This approach gets the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list.
Then takes that value and return that to the generator expression.
From 906676a8d06090b7bcb6222603c7b6ae456044d9 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Wed, 11 Jan 2023 21:42:05 +0100
Subject: [PATCH 426/932] Apply suggestions from code review
Co-authored-by: BethanyG
---
.../.approaches/dictionary/content.md | 76 ++++++++++---------
.../.approaches/enum/content.md | 44 ++++++-----
.../.approaches/introduction.md | 64 ++++++----------
.../.approaches/nested-tuple/content.md | 24 +++---
.../.approaches/two-sequences/content.md | 18 ++---
5 files changed, 107 insertions(+), 119 deletions(-)
diff --git a/exercises/practice/scrabble-score/.approaches/dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
index b0464818046..d220150eb38 100644
--- a/exercises/practice/scrabble-score/.approaches/dictionary/content.md
+++ b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
@@ -2,50 +2,52 @@
```python
LETTER_SCORES = {
- 'A': 1,
- 'E': 1,
- 'I': 1,
- 'O': 1,
- 'U': 1,
- 'L': 1,
- 'N': 1,
- 'R': 1,
- 'S': 1,
- 'T': 1,
- 'D': 2,
- 'G': 2,
- 'B': 3,
- 'C': 3,
- 'M': 3,
- 'P': 3,
- 'F': 4,
- 'H': 4,
- 'V': 4,
- 'W': 4,
- 'Y': 4,
- 'K': 5,
- 'J': 8,
- 'X': 8,
- 'Q': 10,
- 'Z': 10
+ 'A': 1, 'E': 1, 'I': 1, 'O': 1, 'U': 1,
+ 'L': 1, 'N': 1, 'R': 1, 'S': 1, 'T': 1,
+ 'D': 2, 'G': 2, 'B': 3, 'C': 3, 'M': 3,
+ 'P': 3, 'F': 4, 'H': 4, 'V': 4, 'W': 4,
+ 'Y': 4, 'K': 5, 'J': 8, 'X': 8, 'Q': 10, 'Z': 10
}
-
def score(word):
- return sum(LETTER_SCORES[letter.upper()] for letter in word)
+ return sum(LETTER_SCORES[letter] for letter in word.upper())
```
-The code starts with initializing a constant that is a [dictionary][dictionary] there each letter is a key and their respective score as a value.
-Then a function is defined that takes a word as an argument.
+This code starts with defining a constant LETTER_SCORES as a dictionary ([concept:python/dicts](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict)) where each letter is a key and the corresponding score is a value.
+Then the `score` function is defined, which takes a `` as an argument.
+
+The function returns the total score for the word using the built-in function [`sum`][sum].
+ Sum is passed a [generator expression][generator-expression] that iterates over the letters in the word, looking up each score in LETTER_SCORES.
+The generator expression produces the score values on the fly.
+This means that it doesn't use memory to store all the values from LETTER_SCORES.
+Instead, each value is looked up as needed by `sum`.
+
+Within the generator expression, the word is converted from lower to uppercase.
+Each letter of the word is looked up in LETTER_SCORES, and the score value is yielded to `sum` as `sum` iterates over the expression.
+This is almost exactly the same process as using a `list comprehension`.
+However, a `list comprehension` would look up the values and save them into a `list` in memory.
+`sum` would then "unpack" or iterate over the `list`.
+
+A variation on this dictionary approach is to use a dictionary transposition.
+
+```python
+LETTER_SCORES = {
+ 1: {'A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T'},
+ 2: {'D', 'G'},
+ 3: {'B', 'C', 'M', 'P'},
+ 4: {'F', 'H', 'V', 'W', 'Y'},
+ 5: {'K'},
+ 8: {'J', 'X'},
+ 10: {'Q', 'Z'}
+}
+
+def score(word):
+ return sum(next(score for score, letters in LETTER_SCORES.items() if character in letters) for character in word.upper())
-The function returns the built in function [`sum`][sum] that takes a [generator expression][generator-expersion] that iterates over the letters in the word.
-What a generator expression does is that it generates the values on the fly.
-Meaning that it doesn't have to use a lot of memory since it uses the last value and generates the next value.
-Under the generation a letter is given from the string, then it is converted to upcase, and then being looked up at inside of the dictionary and the value is returned.
+However, transposing the dictionary so that the keys are the score and the values are the letters requires more computational calculation (_a loop within a loop_) and is harder to read.
+ Therefore, arranging the dictionary by letter is both more efficient and easier to understand.
-There is also a very similar approach that uses a dictionary transposition.
-Although that approach requires more computational calculation therefore is this approach more efficient.
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
-[generator-expersion]: https://peps.python.org/pep-0289/
+[generator-expression]: https://peps.python.org/pep-0289/
[sum]: https://docs.python.org/3/library/functions.html#sum
diff --git a/exercises/practice/scrabble-score/.approaches/enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
index cad5b6bdec6..188f54000a1 100644
--- a/exercises/practice/scrabble-score/.approaches/enum/content.md
+++ b/exercises/practice/scrabble-score/.approaches/enum/content.md
@@ -13,32 +13,40 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[character.upper()] for character in word)
-```
+ return sum(Scrabble[character] for character in word.upper())
-This approach uses an [`enum`][enum] to define the score of each letter.
-An `enum` or known as an **enumeration** is sets of named constant and is immutable.
-`enum` was added to python standard library (_also known as stdlib_) in python 3.4.
+This approach uses an [`Enum`][enum] to define the score of each letter.
+An [`Enum`][enum] (_also known as an **enumeration**_) is an object with named attributes assigned unique values.
+These attributes are referred to as the enumeration _members_.
+`Enum`s can be iterated over to return their members in definition order.
+Values can be accessed via index syntax using the member name (_similar to how a dictionary lookup works_) .
+`Enum`s are immutable, and their members function as constants.
+The `enum` module was added to python standard library (_also known as stdlib_) in Python 3.4.
-This approach uses an [`intEnum`][int-enum] it works very similar to a normal `enum` but it has the added benefit that the values are integers.
-Thereby acts like integers.
+This approach uses an [`IntEnum`][int-enum].
+An `IntEnum` is very similar to an `Enum`, but restricts assigned values to `int`s.
+This allows the `IntEnum` to act as a collection of integers.
+In fact, `IntEnum`s are considered subclasses of `int`s.
-To use an `intEnum` you need to [import][import] it using: `from enum import IntEnum`.
-Then you can define the `enum` class.
+To use an `IntEnum` you need to first [import][import] it using: `from enum import IntEnum`.
+Then you can define your `IntEnum` subclass.
-The `enum` class is defined by using the [`class`][classes] keyword.
-Then you need to specify the name of the class.
+The `IntEnum` subclass is defined by using the [`class`][classes] keyword, followed by the name you are using for the class, and then the `IntEnum` class you are subclassing in parenthesis:
-After that is the constant in the enum declared by giving the constant capital letters and the value is assigned by using the `=` operator.
-This approach works by giving all the letters as constants and then value of the constant is the score of the letter.
-After the `enum` is defined, the `score` function is defined.
+```python
+class ClassName(IntEnum):
+
+Member names are declared as constants (ALL CAPS) and assigned values using the `=` operator.
+
+This approach works by creating all the uppercase letters as members with their values being the score.
+After the `IntEnum` is defined, the `score` function is defined.
-The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] but with a slight modification.
-Which is that instead of looking up the value in a dictionary it looks it up in the `enum` class.
+The `score` function takes a word as an argument.
+The `score` function uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach], but with a slight modification.
+Instead of looking up the value in a _dictionary_, it looks up the `InEnum` class member value.
[classes]: https://docs.python.org/3/tutorial/classes.html
[enum]: https://docs.python.org/3/library/enum.html
-[generator-expersion]: https://peps.python.org/pep-0289/
+[generator-expression]: https://peps.python.org/pep-0289/
[int-enum]: https://docs.python.org/3/library/enum.html#enum.IntEnum
[import]: https://docs.python.org/3/reference/import.html
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
index 2891e55a6df..1b94ede3feb 100644
--- a/exercises/practice/scrabble-score/.approaches/introduction.md
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -1,73 +1,52 @@
# Introduction
There are various ways to solve `scrabble-score`.
-This approache document shows different strategies to solve this exercise.
+This approaches document shows different strategies to solve this exercise.
## General guidance
The goal of this exercise is to write a function that calculates the scrabble score for a given word.
-The problem is that the scrabble score is calculated by the sum of the scores of each letter in the word.
+The challenge is that the scrabble score is calculated by summing the scores of individual letters in a word.
+The student needs to find an efficient and easily accessed way to store individual letter scores for lookup when processing different words.
## Approach: Using a single dictionary
-Using a single dictionary is an approach, it is simple and fast.
-It is also very pythonic.
+Using a single dictionary for letter lookup is simple and fast.
+It is also very pythonic, and could be considered the canonical approach to this exercise.
```python
LETTER_SCORES = {
- 'A': 1,
- 'E': 1,
- 'I': 1,
- 'O': 1,
- 'U': 1,
- 'L': 1,
- 'N': 1,
- 'R': 1,
- 'S': 1,
- 'T': 1,
- 'D': 2,
- 'G': 2,
- 'B': 3,
- 'C': 3,
- 'M': 3,
- 'P': 3,
- 'F': 4,
- 'H': 4,
- 'V': 4,
- 'W': 4,
- 'Y': 4,
- 'K': 5,
- 'J': 8,
- 'X': 8,
- 'Q': 10,
- 'Z': 10
+ 'A': 1, 'E': 1, 'I': 1, 'O': 1, 'U': 1,
+ 'L': 1, 'N': 1, 'R': 1, 'S': 1, 'T': 1,
+ 'D': 2, 'G': 2, 'B': 3, 'C': 3, 'M': 3,
+ 'P': 3, 'F': 4, 'H': 4, 'V': 4, 'W': 4,
+ 'Y': 4, 'K': 5, 'J': 8, 'X': 8, 'Q': 10, 'Z': 10
}
def score(word):
- return sum(LETTER_SCORES[letter.upper()] for letter in word)
-```
+ return sum(LETTER_SCORES[letter] for letter in word.upper())
For more information, check the [Dictionary Approach][dictionary-approach].
## Approach: Using two sequences
-Using two sequences is an approach, it removes the need of using a nested data structure or a dictonary.
-Although the reason you might not want to do this is that it is hard to read.
+Using two sequences removes the need to use a nested data structure or a dictionary.
+Although you might not want to use this approach because it is hard to read and maintain.
```python
KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
def score(word):
- return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
+ return sum(SCORES[KEYS.index(letter] for letter in word.upper())
```
For more information, check the [Two Sequences Approach][two-sequences-approach].
## Approach: Enum
-Using an `enum` is an approach, it is short and easy to read.
-Although it is more complicated since it uses a oop (object oriented programmering) elements.
+Using an `Enum` is is short and easy to read.
+Although creating an `Enum` can be more complicated since it uses OOP (object oriented programming).
```python
from enum import IntEnum
@@ -82,15 +61,16 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[letter.upper()] for letter in word)
+ return sum(Scrabble[letter] for letter in word.upper())
```
For more information, check the [Enum Approach][enum-approach].
## Approach: Using a nested tuple
-Tuples in python is more memory efficent than using a dictonary in python.
-Although this solution since it is iterating over a tuple for every letter so is it slower.
+Using a tuple in Python is generally more memory efficient than using a dictionary.
+However, this solution requires iterating over the entire `tuple` for every letter in order to score a full word.
+This makes the solution slower than the dictionary approach.
```python
LETTERS_OF_SCORE = (
@@ -104,8 +84,8 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(for character in word for letters, score in LETTERS_OF_SCORE if character in letters)
-```
+ return sum(score for character in word.upper() for
+ letters, score in LETTERS_OF_SCORE if character in letters)
For more information, check the [Nested Tuple Approach][nested-tuple-approach].
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
index 4498d1841f0..113c385f0aa 100644
--- a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -12,23 +12,23 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(score for character in word for letters, score in LETTERS_OF_SCORE if character.upper() in letters)
-```
+ return sum(score for character in word.upper() for
+ letters, score in LETTERS_OF_SCORE if character in letters)
-The code starts with initializing a constant with a [tuple][tuple] of tuples (_also known as a nested tuple_).
-Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score for the letters.
+The code starts with defining a constant, LETTERS_OF_SCORE as a [`tuple`][tuple] of tuples (_also known as a nested tuple_).
+Inside of the inner tuples are 2 values, the first value is a string of letters and the second value is the score for those letters.
-Then a function is defined that takes a word as an argument.
-The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach] but has some slight modifcations.
+Next, the `score` function is defined, taking a word as an argument.
+The `score` function uses a [generator expression][generator-expression] similar to the [dictionary approach][dictionary-approach] with some slight modifications.
-The difference is that this one uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
-We first iterate over the characters in the word and then iterate over the tuples.
-Which means that for each letter are we iterating over all of the tuples.
-There the tuple is unpacked into the letters and the score.
+This particular approach uses a _nested_ [for loop][for-loop] to iterate over the letters and the tuples.
+We first iterate over the characters in the word and then the tuples.
+Which means that for **_each letter_** we iterate over **all** of the tuples.
+Each iteration, the tuple is unpacked into the letters and their corresponding score.
You can read more about unpacking in the [concept:python/unpacking-and-multiple-assignment]().
-Then we check if the character is in the letters and if it is we return the score.
+Then the code checks if the character is in the unpacked letters and if it is we return its score.
[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
-[generator-expersion]: https://peps.python.org/pep-0289/
+[generator-expression]: https://peps.python.org/pep-0289/
[for-loop]: https://realpython.com/python-for-loop/
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
index 944da042983..83f0ea13599 100644
--- a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -5,19 +5,17 @@ KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 + [10] * 2
def score(word):
- return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
-```
+ return sum(SCORES[KEYS.index(letter)] for letter in word.upper())
-This approach uses a string and a [list][list], both of these data types belongs to the parent data type [sequences][sequence].
-The code starts with defining a string constant with letters.
-Then another constant is definded which is a list with corresponding score for the same index as the string.
+This approach uses a string and a [list][list], both of which are [sequence][sequence] types.
+The code begins by defining a string constant with letters.
+Then another constant is defined as a list with the corresponding letter score at the same index as the letter in the string.
-The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] with some slight modifications.
+The `score` function takes a word as an argument.
+And uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach] with some slight modifications.
-The difference is that instead of using a [dictionary][dictionary] and looking up the score inside of a dictonary.
-This approach gets the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list.
-Then takes that value and return that to the generator expression.
+Instead of using a [dictionary][dictionary] and looking up the score, this approach looks up the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list within the generator expression.
+These values are then added up by `sum`.
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[dictionary-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/dictionary
From b3f23ce0ae879ce3fc4710ee864322ae2c514101 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 11 Jan 2023 21:48:49 +0100
Subject: [PATCH 427/932] Fixed spacing
---
.../scrabble-score/.approaches/dictionary/content.md | 9 ++++-----
.../scrabble-score/.approaches/enum/content.md | 8 +++++---
.../scrabble-score/.approaches/introduction.md | 10 ++++++----
.../scrabble-score/.approaches/nested-tuple/content.md | 7 ++++---
.../.approaches/two-sequences/content.md | 1 +
5 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/exercises/practice/scrabble-score/.approaches/dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
index d220150eb38..f87cce0ec15 100644
--- a/exercises/practice/scrabble-score/.approaches/dictionary/content.md
+++ b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
@@ -16,10 +16,10 @@ This code starts with defining a constant LETTER_SCORES as a dictionary ([concep
Then the `score` function is defined, which takes a `` as an argument.
The function returns the total score for the word using the built-in function [`sum`][sum].
- Sum is passed a [generator expression][generator-expression] that iterates over the letters in the word, looking up each score in LETTER_SCORES.
+Sum is passed a [generator expression][generator-expression] that iterates over the letters in the word, looking up each score in LETTER_SCORES.
The generator expression produces the score values on the fly.
This means that it doesn't use memory to store all the values from LETTER_SCORES.
-Instead, each value is looked up as needed by `sum`.
+Instead, each value is looked up as needed by `sum`.
Within the generator expression, the word is converted from lower to uppercase.
Each letter of the word is looked up in LETTER_SCORES, and the score value is yielded to `sum` as `sum` iterates over the expression.
@@ -42,11 +42,10 @@ LETTER_SCORES = {
def score(word):
return sum(next(score for score, letters in LETTER_SCORES.items() if character in letters) for character in word.upper())
-
+```
However, transposing the dictionary so that the keys are the score and the values are the letters requires more computational calculation (_a loop within a loop_) and is harder to read.
- Therefore, arranging the dictionary by letter is both more efficient and easier to understand.
-
+Therefore, arranging the dictionary by letter is both more efficient and easier to understand.
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[generator-expression]: https://peps.python.org/pep-0289/
diff --git a/exercises/practice/scrabble-score/.approaches/enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
index 188f54000a1..5c2ad3a18ae 100644
--- a/exercises/practice/scrabble-score/.approaches/enum/content.md
+++ b/exercises/practice/scrabble-score/.approaches/enum/content.md
@@ -14,6 +14,7 @@ class Scrabble(IntEnum):
def score(word):
return sum(Scrabble[character] for character in word.upper())
+```
This approach uses an [`Enum`][enum] to define the score of each letter.
An [`Enum`][enum] (_also known as an **enumeration**_) is an object with named attributes assigned unique values.
@@ -24,7 +25,7 @@ Values can be accessed via index syntax using the member name (_similar to how a
The `enum` module was added to python standard library (_also known as stdlib_) in Python 3.4.
This approach uses an [`IntEnum`][int-enum].
-An `IntEnum` is very similar to an `Enum`, but restricts assigned values to `int`s.
+An `IntEnum` is very similar to an `Enum`, but restricts assigned values to `int`s.
This allows the `IntEnum` to act as a collection of integers.
In fact, `IntEnum`s are considered subclasses of `int`s.
@@ -35,15 +36,16 @@ The `IntEnum` subclass is defined by using the [`class`][classes] keyword, follo
```python
class ClassName(IntEnum):
+```
Member names are declared as constants (ALL CAPS) and assigned values using the `=` operator.
-This approach works by creating all the uppercase letters as members with their values being the score.
+This approach works by creating all the uppercase letters as members with their values being the score.
After the `IntEnum` is defined, the `score` function is defined.
The `score` function takes a word as an argument.
The `score` function uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach], but with a slight modification.
-Instead of looking up the value in a _dictionary_, it looks up the `InEnum` class member value.
+Instead of looking up the value in a _dictionary_, it looks up the `InEnum` class member value.
[classes]: https://docs.python.org/3/tutorial/classes.html
[enum]: https://docs.python.org/3/library/enum.html
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
index 1b94ede3feb..8539d59d43a 100644
--- a/exercises/practice/scrabble-score/.approaches/introduction.md
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -25,6 +25,7 @@ LETTER_SCORES = {
def score(word):
return sum(LETTER_SCORES[letter] for letter in word.upper())
+```
For more information, check the [Dictionary Approach][dictionary-approach].
@@ -38,7 +39,7 @@ KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
def score(word):
- return sum(SCORES[KEYS.index(letter] for letter in word.upper())
+ return sum(SCORES[KEYS.index(letter)] for letter in word.upper())
```
For more information, check the [Two Sequences Approach][two-sequences-approach].
@@ -46,7 +47,7 @@ For more information, check the [Two Sequences Approach][two-sequences-approach]
## Approach: Enum
Using an `Enum` is is short and easy to read.
-Although creating an `Enum` can be more complicated since it uses OOP (object oriented programming).
+Although creating an `Enum` can be more complicated since it uses OOP (object oriented programming).
```python
from enum import IntEnum
@@ -84,8 +85,9 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(score for character in word.upper() for
- letters, score in LETTERS_OF_SCORE if character in letters)
+ return sum(score for character in word.upper() for
+ letters, score in LETTERS_OF_SCORE if character in letters)
+```
For more information, check the [Nested Tuple Approach][nested-tuple-approach].
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
index 113c385f0aa..70dc860a0a4 100644
--- a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -12,10 +12,11 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(score for character in word.upper() for
+ return sum(score for character in word.upper() for
letters, score in LETTERS_OF_SCORE if character in letters)
+```
-The code starts with defining a constant, LETTERS_OF_SCORE as a [`tuple`][tuple] of tuples (_also known as a nested tuple_).
+The code starts with defining a constant, `LETTERS_OF_SCORE` as a [`tuple`][tuple] of tuples (_also known as a nested tuple_).
Inside of the inner tuples are 2 values, the first value is a string of letters and the second value is the score for those letters.
Next, the `score` function is defined, taking a word as an argument.
@@ -29,6 +30,6 @@ You can read more about unpacking in the [concept:python/unpacking-and-multiple-
Then the code checks if the character is in the unpacked letters and if it is we return its score.
-[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
[generator-expression]: https://peps.python.org/pep-0289/
[for-loop]: https://realpython.com/python-for-loop/
+[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
index 83f0ea13599..6cbb6c7694e 100644
--- a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -6,6 +6,7 @@ SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 + [10] * 2
def score(word):
return sum(SCORES[KEYS.index(letter)] for letter in word.upper())
+```
This approach uses a string and a [list][list], both of which are [sequence][sequence] types.
The code begins by defining a string constant with letters.
From 220fce386d72aea29a4813268ce72ec9b75676d4 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 26 Jan 2023 15:23:04 -0800
Subject: [PATCH 428/932] Updated Exercise docs, toml files, test files and
example solutions for: (#3329)
* Palindrom Products (toml and tests)
* Roman Numerals (tests)
* Phone Number (toml, tests, and example solution)
* Resistor Color Trio (intro, toml, tests, and example solution)
---
.../palindrome-products/.meta/tests.toml | 24 +++++++++----
.../palindrome_products_test.py | 5 +++
.../phone-number/.docs/instructions.append.md | 4 +--
.../practice/phone-number/.meta/example.py | 4 +--
.../practice/phone-number/.meta/tests.toml | 10 ++++++
.../phone-number/phone_number_test.py | 4 +--
.../resistor-color-trio/.docs/instructions.md | 8 +++--
.../resistor-color-trio/.meta/example.py | 36 +++++++++++++------
.../resistor-color-trio/.meta/tests.toml | 15 ++++++++
.../resistor_color_trio_test.py | 15 ++++++++
.../roman-numerals/roman_numerals_test.py | 30 ++++++++--------
11 files changed, 113 insertions(+), 42 deletions(-)
diff --git a/exercises/practice/palindrome-products/.meta/tests.toml b/exercises/practice/palindrome-products/.meta/tests.toml
index b34cb0d4755..a3bc41750a7 100644
--- a/exercises/practice/palindrome-products/.meta/tests.toml
+++ b/exercises/practice/palindrome-products/.meta/tests.toml
@@ -1,12 +1,19 @@
-# 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.
[5cff78fe-cf02-459d-85c2-ce584679f887]
-description = "finds the smallest palindrome from single digit factors"
+description = "find the smallest palindrome from single digit factors"
[0853f82c-5fc4-44ae-be38-fadb2cced92d]
-description = "finds the largest palindrome from single digit factors"
+description = "find the largest palindrome from single digit factors"
[66c3b496-bdec-4103-9129-3fcb5a9063e1]
description = "find the smallest palindrome from double digit factors"
@@ -15,13 +22,13 @@ description = "find the smallest palindrome from double digit factors"
description = "find the largest palindrome from double digit factors"
[cecb5a35-46d1-4666-9719-fa2c3af7499d]
-description = "find smallest palindrome from triple digit factors"
+description = "find the smallest palindrome from triple digit factors"
[edab43e1-c35f-4ea3-8c55-2f31dddd92e5]
description = "find the largest palindrome from triple digit factors"
[4f802b5a-9d74-4026-a70f-b53ff9234e4e]
-description = "find smallest palindrome from four digit factors"
+description = "find the smallest palindrome from four digit factors"
[787525e0-a5f9-40f3-8cb2-23b52cf5d0be]
description = "find the largest palindrome from four digit factors"
@@ -37,3 +44,6 @@ description = "error result for smallest if min is more than max"
[eeeb5bff-3f47-4b1e-892f-05829277bd74]
description = "error result for largest if min is more than max"
+
+[16481711-26c4-42e0-9180-e2e4e8b29c23]
+description = "smallest product does not use the smallest factor"
diff --git a/exercises/practice/palindrome-products/palindrome_products_test.py b/exercises/practice/palindrome-products/palindrome_products_test.py
index 2ff69adc2d7..0fd7eaece32 100644
--- a/exercises/practice/palindrome-products/palindrome_products_test.py
+++ b/exercises/practice/palindrome-products/palindrome_products_test.py
@@ -71,5 +71,10 @@ def test_error_result_for_largest_if_min_is_more_than_max(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "min must be <= max")
+ def test_smallest_product_does_not_use_the_smallest_factor(self):
+ value, factors = smallest(min_factor=3215, max_factor=4000)
+ self.assertEqual(value, 10988901)
+ self.assertFactorsEqual(factors, [[3297, 3333]])
+
def assertFactorsEqual(self, actual, expected):
self.assertEqual(set(map(frozenset, actual)), set(map(frozenset, expected)))
diff --git a/exercises/practice/phone-number/.docs/instructions.append.md b/exercises/practice/phone-number/.docs/instructions.append.md
index 1001c9dc7e1..662c8c8762e 100644
--- a/exercises/practice/phone-number/.docs/instructions.append.md
+++ b/exercises/practice/phone-number/.docs/instructions.append.md
@@ -10,10 +10,10 @@ To raise a `ValueError` with a message, write the message as an argument to the
```python
# if a phone number has less than 10 digits.
-raise ValueError("incorrect number of digits")
+raise ValueError("must not be fewer than 10 digits")
# if a phone number has more than 11 digits.
-raise ValueError("more than 11 digits")
+raise ValueError("must not be greater than 11 digits")
# if a phone number has 11 digits, but starts with a number other than 1.
raise ValueError("11 digits must start with 1")
diff --git a/exercises/practice/phone-number/.meta/example.py b/exercises/practice/phone-number/.meta/example.py
index 02b8e13b8b9..d23102a01ed 100644
--- a/exercises/practice/phone-number/.meta/example.py
+++ b/exercises/practice/phone-number/.meta/example.py
@@ -25,10 +25,10 @@ def _clean(self, number):
def _normalize(self, number):
if len(number) < 10:
- raise ValueError('incorrect number of digits')
+ raise ValueError('must not be fewer than 10 digits')
if len(number) > 11:
- raise ValueError('more than 11 digits')
+ raise ValueError('must not be greater than 11 digits')
if len(number) == 10 or len(number) == 11 and number.startswith('1'):
if number[-10] == '0':
diff --git a/exercises/practice/phone-number/.meta/tests.toml b/exercises/practice/phone-number/.meta/tests.toml
index ee308c3e597..24dbf07a767 100644
--- a/exercises/practice/phone-number/.meta/tests.toml
+++ b/exercises/practice/phone-number/.meta/tests.toml
@@ -20,6 +20,11 @@ description = "cleans numbers with multiple spaces"
[598d8432-0659-4019-a78b-1c6a73691d21]
description = "invalid when 9 digits"
+include = false
+
+[2de74156-f646-42b5-8638-0ef1d8b58bc2]
+description = "invalid when 9 digits"
+reimplements = "598d8432-0659-4019-a78b-1c6a73691d21"
[57061c72-07b5-431f-9766-d97da7c4399d]
description = "invalid when 11 digits does not start with a 1"
@@ -32,6 +37,11 @@ description = "valid when 11 digits and starting with 1 even with punctuation"
[c6a5f007-895a-4fc5-90bc-a7e70f9b5cad]
description = "invalid when more than 11 digits"
+include = false
+
+[4a1509b7-8953-4eec-981b-c483358ff531]
+description = "invalid when more than 11 digits"
+reimplements = "c6a5f007-895a-4fc5-90bc-a7e70f9b5cad"
[63f38f37-53f6-4a5f-bd86-e9b404f10a60]
description = "invalid with letters"
diff --git a/exercises/practice/phone-number/phone_number_test.py b/exercises/practice/phone-number/phone_number_test.py
index dfbb9f851a3..72ff3b099ac 100644
--- a/exercises/practice/phone-number/phone_number_test.py
+++ b/exercises/practice/phone-number/phone_number_test.py
@@ -24,7 +24,7 @@ def test_invalid_when_9_digits(self):
with self.assertRaises(ValueError) as err:
PhoneNumber("123456789")
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "incorrect number of digits")
+ self.assertEqual(err.exception.args[0], "must not be fewer than 10 digits")
def test_invalid_when_11_digits_does_not_start_with_a_1(self):
with self.assertRaises(ValueError) as err:
@@ -44,7 +44,7 @@ def test_invalid_when_more_than_11_digits(self):
with self.assertRaises(ValueError) as err:
PhoneNumber("321234567890")
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "more than 11 digits")
+ self.assertEqual(err.exception.args[0], "must not be greater than 11 digits")
def test_invalid_with_letters(self):
with self.assertRaises(ValueError) as err:
diff --git a/exercises/practice/resistor-color-trio/.docs/instructions.md b/exercises/practice/resistor-color-trio/.docs/instructions.md
index fcc76958a5f..4ad2aede378 100644
--- a/exercises/practice/resistor-color-trio/.docs/instructions.md
+++ b/exercises/practice/resistor-color-trio/.docs/instructions.md
@@ -46,9 +46,11 @@ 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.
+When we get to larger resistors, a [metric prefix][metric-prefix] is used to indicate a larger magnitude of ohms, such as "kiloohms".
+That is similar to saying "2 kilometers" instead of "2000 meters", or "2 kilograms" for "2000 grams".
-So an input of `"orange", "orange", "orange"` should return:
+For example, an input of `"orange", "orange", "orange"` should return:
> "33 kiloohms"
+
+[metric-prefix]: https://en.wikipedia.org/wiki/Metric_prefix
diff --git a/exercises/practice/resistor-color-trio/.meta/example.py b/exercises/practice/resistor-color-trio/.meta/example.py
index a6588ea5a6d..69554592d06 100644
--- a/exercises/practice/resistor-color-trio/.meta/example.py
+++ b/exercises/practice/resistor-color-trio/.meta/example.py
@@ -1,18 +1,32 @@
COLORS = [
- 'black',
- 'brown',
- 'red',
- 'orange',
- 'yellow',
- 'green',
- 'blue',
- 'violet',
- 'grey',
- 'white'
+ 'black',
+ 'brown',
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'blue',
+ 'violet',
+ 'grey',
+ 'white'
]
def label(colors):
value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
value *= 10 ** COLORS.index(colors[2])
- return str(value) + ' ohms' if value < 1000 else str(value // 1000) + ' kiloohms'
+ label = str(value)
+
+ if len(label) < 4 :
+ unit = 'ohms'
+ elif len(label) < 7:
+ label = str(value//1000)
+ unit = 'kiloohms'
+ elif len(label) <= 8 :
+ label = str(value//1000000)
+ unit = 'megaohms'
+ elif len(label) >= 9:
+ label = str(value//1000000000)
+ unit = 'gigaohms'
+
+ return f'{value if value < 1000 else label} {unit}'
diff --git a/exercises/practice/resistor-color-trio/.meta/tests.toml b/exercises/practice/resistor-color-trio/.meta/tests.toml
index dc6077e54fc..b7d45fa5d55 100644
--- a/exercises/practice/resistor-color-trio/.meta/tests.toml
+++ b/exercises/practice/resistor-color-trio/.meta/tests.toml
@@ -23,3 +23,18 @@ description = "Green and brown and orange"
[f5d37ef9-1919-4719-a90d-a33c5a6934c9]
description = "Yellow and violet and yellow"
+
+[5f6404a7-5bb3-4283-877d-3d39bcc33854]
+description = "Blue and violet and blue"
+
+[7d3a6ab8-e40e-46c3-98b1-91639fff2344]
+description = "Minimum possible value"
+
+[ca0aa0ac-3825-42de-9f07-dac68cc580fd]
+description = "Maximum possible value"
+
+[0061a76c-903a-4714-8ce2-f26ce23b0e09]
+description = "First two colors make an invalid octal number"
+
+[30872c92-f567-4b69-a105-8455611c10c4]
+description = "Ignore extra colors"
diff --git a/exercises/practice/resistor-color-trio/resistor_color_trio_test.py b/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
index 302df179efb..ddfdfb6930c 100644
--- a/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
+++ b/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
@@ -22,3 +22,18 @@ def test_green_and_brown_and_orange(self):
def test_yellow_and_violet_and_yellow(self):
self.assertEqual(label(["yellow", "violet", "yellow"]), "470 kiloohms")
+
+ def test_blue_and_violet_and_blue(self):
+ self.assertEqual(label(["blue", "violet", "blue"]), "67 megaohms")
+
+ def test_minimum_possible_value(self):
+ self.assertEqual(label(["black", "black", "black"]), "0 ohms")
+
+ def test_maximum_possible_value(self):
+ self.assertEqual(label(["white", "white", "white"]), "99 gigaohms")
+
+ def test_first_two_colors_make_an_invalid_octal_number(self):
+ self.assertEqual(label(["black", "grey", "black"]), "8 ohms")
+
+ def test_ignore_extra_colors(self):
+ self.assertEqual(label(["blue", "green", "yellow", "orange"]), "650 kiloohms")
diff --git a/exercises/practice/roman-numerals/roman_numerals_test.py b/exercises/practice/roman-numerals/roman_numerals_test.py
index f85422cc5e6..59e5e697fa2 100644
--- a/exercises/practice/roman-numerals/roman_numerals_test.py
+++ b/exercises/practice/roman-numerals/roman_numerals_test.py
@@ -27,6 +27,9 @@ def test_6_is_vi(self):
def test_9_is_ix(self):
self.assertEqual(roman(9), "IX")
+ def test_16_is_xvi(self):
+ self.assertEqual(roman(16), "XVI")
+
def test_27_is_xxvii(self):
self.assertEqual(roman(27), "XXVII")
@@ -39,6 +42,9 @@ def test_49_is_xlix(self):
def test_59_is_lix(self):
self.assertEqual(roman(59), "LIX")
+ def test_66_is_lxvi(self):
+ self.assertEqual(roman(66), "LXVI")
+
def test_93_is_xciii(self):
self.assertEqual(roman(93), "XCIII")
@@ -48,36 +54,30 @@ def test_141_is_cxli(self):
def test_163_is_clxiii(self):
self.assertEqual(roman(163), "CLXIII")
+ def test_166_is_clxvi(self):
+ self.assertEqual(roman(166), "CLXVI")
+
def test_402_is_cdii(self):
self.assertEqual(roman(402), "CDII")
def test_575_is_dlxxv(self):
self.assertEqual(roman(575), "DLXXV")
+ def test_666_is_dclxvi(self):
+ self.assertEqual(roman(666), "DCLXVI")
+
def test_911_is_cmxi(self):
self.assertEqual(roman(911), "CMXI")
def test_1024_is_mxxiv(self):
self.assertEqual(roman(1024), "MXXIV")
- def test_3000_is_mmm(self):
- self.assertEqual(roman(3000), "MMM")
-
- def test_16_is_xvi(self):
- self.assertEqual(roman(16), "XVI")
-
- def test_66_is_lxvi(self):
- self.assertEqual(roman(66), "LXVI")
-
- def test_166_is_clxvi(self):
- self.assertEqual(roman(166), "CLXVI")
-
- def test_666_is_dclxvi(self):
- self.assertEqual(roman(666), "DCLXVI")
-
def test_1666_is_mdclxvi(self):
self.assertEqual(roman(1666), "MDCLXVI")
+ def test_3000_is_mmm(self):
+ self.assertEqual(roman(3000), "MMM")
+
def test_3001_is_mmmi(self):
self.assertEqual(roman(3001), "MMMI")
From afc8e4f025774027a6668b3be12e1435e77dbd35 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 26 Jan 2023 06:55:54 -0600
Subject: [PATCH 429/932] Update introduction.md
---
exercises/practice/grains/.approaches/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/grains/.approaches/introduction.md b/exercises/practice/grains/.approaches/introduction.md
index 13f56d33be8..c02d5b8ba6b 100644
--- a/exercises/practice/grains/.approaches/introduction.md
+++ b/exercises/practice/grains/.approaches/introduction.md
@@ -19,7 +19,7 @@ You can see that the exponent, or power, that `2` is raised by is always one les
| 1 | 0 | 2 to the power of 0 = 1 |
| 2 | 1 | 2 to the power of 1 = 2 |
| 3 | 2 | 2 to the power of 2 = 4 |
-| 4 | 3 | 2 to the power of 4 = 8 |
+| 4 | 3 | 2 to the power of 3 = 8 |
## Approach: Bit-shifting
From bf818b5b1c4705903a9e0fa69c891b936e077204 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 11 Jan 2023 21:44:03 +0100
Subject: [PATCH 430/932] start
---
.../.approaches/ascii-values/content.md | 45 ++++++++
.../.approaches/ascii-values/snippet.txt | 5 +
.../rotational-cipher/.approaches/config.json | 22 ++++
.../.approaches/introduction.md | 109 ++++++++++++++++++
.../nested-for-loop-optimized/content.md | 92 +++++++++++++++
.../nested-for-loop-optimized/snippet.txt | 5 +
6 files changed, 278 insertions(+)
create mode 100644 exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.approaches/config.json
create mode 100644 exercises/practice/rotational-cipher/.approaches/introduction.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
new file mode 100644
index 00000000000..0daf501c6a9
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -0,0 +1,45 @@
+# Ascii
+
+```python
+def rotate(text, key)
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
+ return result
+```
+
+This approach uses [ascii values][ascii], ascii stands for American Standard Code for Information Interchange.
+It is a character encoding standard for electronic communication.
+It is a 7-bit code, which means that it can represent 128 different characters.
+The system uses numbers to represent various characters, symbols, and other entities.
+
+In ascii can you find all the downcased letter in the range between 97 and 123.
+While the upcased letters are in the range between 65 and 91.
+
+The reason why you might not want to do this approach is that it only supports the english alphabet.
+
+The approach starts with defining the function `rotate`, then a variable `result` is defined with the value of an empty string.
+Then is all the letters from the text iterated over through a `for loop`.
+Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+If it is a uppercased letter then is `ord` used which converts a letter to an ascii and is then added with the key and the ascii value of the letter subtracted with 65.
+Then is the result of that modulo 26 added with 65.
+
+That is because we want to know which index in the alphabet the letter is.
+And if the number is over 26 we want to make sure that it is in the range of 0-26.
+So we use modulo to make sure it is in that range.
+To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the ascii value of the letter with 65.
+After that to get the back to a letter we add 65 and use the `chr` method which converts an ascii value to a letter.
+After that is the new letter added to the result.
+
+If the letter is a lowercased letter then is the same done but with the ascii value of 97 subtracted with the letter.
+
+If the letter is not a letter then is the letter added to the result.
+When the loop is finished we return the result.
+
+[ascii]: https://en.wikipedia.org/wiki/ASCII
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
new file mode 100644
index 00000000000..f678cd824a4
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
@@ -0,0 +1,5 @@
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
diff --git a/exercises/practice/rotational-cipher/.approaches/config.json b/exercises/practice/rotational-cipher/.approaches/config.json
new file mode 100644
index 00000000000..3882c1cb97f
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/config.json
@@ -0,0 +1,22 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "ec09a4e1-6bc3-465b-a366-8ccdd2dbe093",
+ "slug": "ascii-values",
+ "title": "ASCII values",
+ "blurb": "Use letters ascii value to rotate the alphabet",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "6eb99523-b12b-4e72-8ed8-3444b635b9e5",
+ "slug": "nested-for-loop-optimized",
+ "title": "Nested for loop optimized",
+ "blurb": "Nested for loop optimized edition",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
new file mode 100644
index 00000000000..5c63d423039
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -0,0 +1,109 @@
+# Introduction
+
+There are various ways to solve `palindrome-products`.
+This approaches document shows 2 _common_ strategies, with one being a lot more efficient than the other.
+That being said, neither approach here is considered canonical, and other "pythonic" approaches could be added/expanded on in the future.
+
+## General guidance
+
+The goal of this exercise is to generate the largest and smallest palindromes from a given range of numbers.
+
+## Approach: Using ascii values
+
+This approach is very simple and easy to understand.
+it uses the ascii value of the letters to rotate them.
+There the numbers 65-91 in the ascii range represent downcased letters.
+While 97-123 represent upcased letters.
+
+The reason why you might not want to do this approach is that it only supports the english alphabet.
+Say we want to use the scandivanian letter: **ä**, then this approach will not work.
+Since **ä** has the ascii value of 132.
+
+```python
+def rotate(text, key)
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
+ return result
+```
+
+## Approach: Alphabet
+
+This approach is similar to the previous one, but instead of using the ascii values, it uses the index of the letter in the alphabet.
+It requires the storing of a string and unless you are using two strings you have to convert the letters from upper to lower case.
+
+What this approach although give is the posiblity to use any alphabet.
+Say we want to use the scandivanian letter: **ä**, then we just add it where we want it:
+`abcdefghijklmnopqrstuvwxyzä` and it will rotate correctley around that.
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
+ return result
+```
+
+For more information, check the [Nested for loop approach][approach-nested-for-loop].
+
+## Approach: Str translate
+
+This approach is similar to the previous one, but instead of using the index of the letter in the alphabet, it uses the `str.translate` method.
+The benefit of this approach is that it has no visable loop, thereby the code becomes more concise.
+What to note is that the `str.translate` still loops over the `string` thereby even if it is no visable loop, it doesn't mean that a method is not looping.
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
+```
+
+You can read more about how to achieve this optimization in: [Nested for loop optimized][approach-nested-for-loop-optimized].
+
+## Approach: Recursion
+
+In this approach we use a recursive function.
+A recursive function is a function that calls itself.
+This approach can be more concise than other approaches, and may also be more readable for some audiences.
+
+The reason why you might not want to use this approach is that Python has a [recursion limit][recursion-limit] with a default of 1000.
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ if text == "":
+ return ""
+ first_letter, rest = text[0], text[1:]
+ if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key)
+ else:
+ return first_letter + rotate(rest, key)
+```
+
+## Benchmark
+
+For more information, check the [Performance article][article-performance].
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
+[article-performance]: https://exercism.org/tracks/python/exercises/palindrome-products/articles/performance
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
new file mode 100644
index 00000000000..2bc2718391e
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
@@ -0,0 +1,92 @@
+# Nested For Loop Optimized
+
+This approach shows that just a few changes can improve the running time of the solution significantly.
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations.
+To optimize the `largest` function, we have to start the inner loop from the maximum factor and proceed down to the minimum factor.
+This allows us to stop the inner loop earlier than before.
+We also set the minimum value in the _inner_ loop to the current value of the _outer_ loop.
+
+Here is an example of how the algorithm works and why the loops need to be modified.
+Say we take maximum to be 99 and the minimum 10.
+In the first round:
+
+```
+x = [99 , 98, 97 ...]
+y = [99]
+```
+
+And already we have our result: `9009[91,99]`
+Although the loops have to continue to make us sure there are no higher values.
+
+```
+x = [98, 97, 96 ...]
+y = [99, 98]
+...
+x = [90, 89, 88 ...]
+y = [99, 98,97,96,95,94,93,92,91,90]
+
+Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
+Meaning that running beyond this point won't give us any values higher than 9009.
+
+That is why we introduce the `was_bigger` variable.
+With `was_bigger`, we can check if the inner loop has a bigger value than the current result.
+If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
+We do that by using the [`break`][break] statement.
+
+If we hadn't modified the direction of the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
+This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
+
+The `smallest` function is optimized in a similar way as `largest` function compared to the original approach.
+The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
+Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[break]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
new file mode 100644
index 00000000000..bf91dd67539
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
@@ -0,0 +1,5 @@
+for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
From 9580dd8ad684b8ff705a7e98e637862f679ac812 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 12 Jan 2023 22:21:29 +0100
Subject: [PATCH 431/932] Added preformance article and a bunch of content to
approaches
---
.../.approaches/alphabet/content.md | 46 ++++++++++
.../.approaches/alphabet/snippet.txt | 8 ++
.../.approaches/ascii-values/content.md | 21 +++--
.../.approaches/ascii-values/snippet.txt | 13 ++-
.../rotational-cipher/.approaches/config.json | 20 +++-
.../.approaches/introduction.md | 41 +++++----
.../nested-for-loop-optimized/content.md | 92 -------------------
.../nested-for-loop-optimized/snippet.txt | 5 -
.../.approaches/recursion/content.md | 42 +++++++++
.../.approaches/recursion/snippet.txt | 8 ++
.../.approaches/str-translate/content.md | 54 +++++++++++
.../.approaches/str-translate/snippet.txt | 5 +
.../rotational-cipher/.articles/config.json | 11 +++
.../.articles/performance/code/Benchmark.py | 90 ++++++++++++++++++
.../.articles/performance/content.md | 43 +++++++++
.../.articles/performance/snippet.md | 6 ++
16 files changed, 377 insertions(+), 128 deletions(-)
create mode 100644 exercises/practice/rotational-cipher/.approaches/alphabet/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt
delete mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
delete mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.approaches/recursion/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.approaches/str-translate/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.articles/config.json
create mode 100644 exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
create mode 100644 exercises/practice/rotational-cipher/.articles/performance/content.md
create mode 100644 exercises/practice/rotational-cipher/.articles/performance/snippet.md
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
new file mode 100644
index 00000000000..75d85c531e2
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
@@ -0,0 +1,46 @@
+# Alphabet
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
+ return result
+```
+
+The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
+After that the function `rotate` is declared, then a variable `result` is defined with the value of an empty string.
+
+Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
+Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+
+If it is a uppercased letter then it is converted to lowe case and finds its index in the `AlPHABET` constant.
+Then is the key added to the index and [modulo (`%`)][modulo] 26 is used on the result.
+Then is the letter at the index found in the `AlPHABET` constant and the letter is converted to upcase.
+
+If the letter is a lowercased letter then it does the same process but don't convert the letter to downcase and then to uppercase.
+
+If the letter is not a letter then is the letter added to the result.
+When the loop is finished we return the result.
+
+If you only want to use english letters so could you import the alphabet instead of defining it yourself.
+Since in the [`string`][string] module there is a constant called [`ascii_lowercase`][ascii_lowercase] which holds the lowercased alphabet.
+
+```python
+import string
+
+AlPHABET = string.ascii_lowercase
+```
+
+[ascii_lowercase]: https://docs.python.org/3/library/string.html#string.ascii_letters
+[for-loop]: https://realpython.com/python-for-loop/
+[modulo]: https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
+[string]: https://docs.python.org/3/library/string.html
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt b/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt
new file mode 100644
index 00000000000..ade372000b6
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt
@@ -0,0 +1,8 @@
+for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 0daf501c6a9..cb637198a9c 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -1,7 +1,7 @@
# Ascii
```python
-def rotate(text, key)
+def rotate(text, key):
result = ""
for letter in text:
if letter.isalpha():
@@ -25,16 +25,20 @@ While the upcased letters are in the range between 65 and 91.
The reason why you might not want to do this approach is that it only supports the english alphabet.
The approach starts with defining the function `rotate`, then a variable `result` is defined with the value of an empty string.
-Then is all the letters from the text iterated over through a `for loop`.
+Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
-If it is a uppercased letter then is `ord` used which converts a letter to an ascii and is then added with the key and the ascii value of the letter subtracted with 65.
-Then is the result of that modulo 26 added with 65.
+
+Python has a built in function called `ord` that converts a [unicode][unicode] symbol to an integer.
+The unicode's first 128 characters are the same as ascii.
+
+If it is a uppercased letter then is [`ord`][ord] used to convert the letters to an integer and is then added with the key and then subtracted with 65.
+Then is the result of that [modulo (`%`)][modulo] 26 added with 65.
That is because we want to know which index in the alphabet the letter is.
And if the number is over 26 we want to make sure that it is in the range of 0-26.
So we use modulo to make sure it is in that range.
-To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the ascii value of the letter with 65.
-After that to get the back to a letter we add 65 and use the `chr` method which converts an ascii value to a letter.
+To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the integer value of the letter with 65.
+After that to get the back to a letter we add 65 and use the [`chr`][chr] method which converts an an unicode value to a letter.
After that is the new letter added to the result.
If the letter is a lowercased letter then is the same done but with the ascii value of 97 subtracted with the letter.
@@ -43,3 +47,8 @@ If the letter is not a letter then is the letter added to the result.
When the loop is finished we return the result.
[ascii]: https://en.wikipedia.org/wiki/ASCII
+[chr]: https://docs.python.org/3/library/functions.html#chr
+[for-loop]: https://realpython.com/python-for-loop/
+[modulo]: https://realpython.com/python-modulo-operator/
+[ord]: https://docs.python.org/3/library/functions.html#ord
+[unicode]: https://en.wikipedia.org/wiki/Unicode
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
index f678cd824a4..ede3b5c9897 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
@@ -1,5 +1,8 @@
- for number_a in range(min_factor, max_factor+1):
- for number_b in range(min_factor, max_factor+1):
- if number_a * number_b >= result:
- test_value = str(number_a * number_b)
- if test_value == test_value[::-1]:
+for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.approaches/config.json b/exercises/practice/rotational-cipher/.approaches/config.json
index 3882c1cb97f..5cf51697a64 100644
--- a/exercises/practice/rotational-cipher/.approaches/config.json
+++ b/exercises/practice/rotational-cipher/.approaches/config.json
@@ -13,9 +13,23 @@
},
{
"uuid": "6eb99523-b12b-4e72-8ed8-3444b635b9e5",
- "slug": "nested-for-loop-optimized",
- "title": "Nested for loop optimized",
- "blurb": "Nested for loop optimized edition",
+ "slug": "alphabet",
+ "title": "Alphabet",
+ "blurb": "Using the alphabet to rotate the alphabet",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "e539d1a5-f497-402b-a232-7e889f4323c1",
+ "slug": "str-translate",
+ "title": "Str Translate",
+ "blurb": "Using str.translate to rotate the alphabet",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "0c74890e-d96e-47a2-a8bf-93c45dd67f94",
+ "slug": "recursion",
+ "title": "Recursion",
+ "blurb": "Using Recursion to rotate the alphabet",
"authors": ["meatball133", "bethanyg"]
}
]
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 5c63d423039..90b8d6fa8ad 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -1,12 +1,11 @@
# Introduction
-There are various ways to solve `palindrome-products`.
-This approaches document shows 2 _common_ strategies, with one being a lot more efficient than the other.
-That being said, neither approach here is considered canonical, and other "pythonic" approaches could be added/expanded on in the future.
+There are various ways to solve `rotational-cipher`.
+You can for example use a [ascii values][ascii], alphabet, recursion, and `str.translate`.
## General guidance
-The goal of this exercise is to generate the largest and smallest palindromes from a given range of numbers.
+The goal of this exercise is to rotate the letters in a string by a given key.
## Approach: Using ascii values
@@ -16,11 +15,11 @@ There the numbers 65-91 in the ascii range represent downcased letters.
While 97-123 represent upcased letters.
The reason why you might not want to do this approach is that it only supports the english alphabet.
-Say we want to use the scandivanian letter: **ä**, then this approach will not work.
-Since **ä** has the ascii value of 132.
+Say we want to use the scandinavian letter: **å**, then this approach will not work.
+Since **å** has the ascii value of 132.
```python
-def rotate(text, key)
+def rotate(text, key):
result = ""
for letter in text:
if letter.isalpha():
@@ -33,14 +32,16 @@ def rotate(text, key)
return result
```
+For more information, check the [ascii values approach][approach-ascii-values].
+
## Approach: Alphabet
This approach is similar to the previous one, but instead of using the ascii values, it uses the index of the letter in the alphabet.
It requires the storing of a string and unless you are using two strings you have to convert the letters from upper to lower case.
-What this approach although give is the posiblity to use any alphabet.
-Say we want to use the scandivanian letter: **ä**, then we just add it where we want it:
-`abcdefghijklmnopqrstuvwxyzä` and it will rotate correctley around that.
+What this approach although give is the possibility to use any alphabet.
+Say we want to use the scandinavian letter: **å**, then we just add it where we want it:
+`abcdefghijklmnopqrstuvwxyzå` and it will rotate correctly around that.
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -58,13 +59,13 @@ def rotate(text, key):
return result
```
-For more information, check the [Nested for loop approach][approach-nested-for-loop].
+For more information, check the [Alphabet approach][approach-alphabet].
## Approach: Str translate
This approach is similar to the previous one, but instead of using the index of the letter in the alphabet, it uses the `str.translate` method.
-The benefit of this approach is that it has no visable loop, thereby the code becomes more concise.
-What to note is that the `str.translate` still loops over the `string` thereby even if it is no visable loop, it doesn't mean that a method is not looping.
+The benefit of this approach is that it has no visible loop, thereby the code becomes more concise.
+What to note is that the `str.translate` still loops over the `string` thereby even if it is no visible loop, it doesn't mean that a method is not looping.
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -74,7 +75,7 @@ def rotate(text, key):
return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
```
-You can read more about how to achieve this optimization in: [Nested for loop optimized][approach-nested-for-loop-optimized].
+For more information, check the [Str translate approach][approach-str-translate].
## Approach: Recursion
@@ -100,10 +101,16 @@ def rotate(text, key):
return first_letter + rotate(rest, key)
```
+For more information, check the [Recursion approach][approach-recursion].
+
## Benchmark
For more information, check the [Performance article][article-performance].
-[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
-[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
-[article-performance]: https://exercism.org/tracks/python/exercises/palindrome-products/articles/performance
+[ascii]: https://en.wikipedia.org/wiki/ASCII
+[approach-recursion]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/recursion
+[approach-str-translate]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/str-translate
+[approach-ascii-values]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/ascii-values
+[approach-alphabet]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/alphabet
+[article-performance]: https://exercism.org/tracks/python/exercises/rotational-cipher/articles/performance
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
deleted file mode 100644
index 2bc2718391e..00000000000
--- a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Nested For Loop Optimized
-
-This approach shows that just a few changes can improve the running time of the solution significantly.
-
-```python
-def largest(min_factor, max_factor):
- if min_factor > max_factor:
- raise ValueError("min must be <= max")
- result = 0
- answer = []
- for number_a in range(max_factor, min_factor - 1,-1):
- was_bigger = False
- for number_b in range(max_factor, number_a - 1, -1):
- if number_a * number_b >= result:
- was_bigger = True
- test_value = str(number_a * number_b)
- if test_value == test_value[::-1]:
- if number_a * number_b > result:
- answer = []
- result = int(test_value)
- answer.append([number_a, number_b])
- if not was_bigger:
- break
- if result == 0:
- result = None
- return (result, answer)
-
-
-def smallest(min_factor, max_factor):
- if min_factor > max_factor:
- raise ValueError("min must be <= max")
- result = 0
- answer = []
- for number_a in range(min_factor, max_factor+1):
- was_smaller = False
- for number_b in range(min_factor, max_factor+1):
- if number_a * number_b <= result or result == 0:
- was_smaller = True
- test_value = str(number_a * number_b)
- if test_value == test_value[::-1]:
- if number_a * number_b < result:
- answer = []
- result = int(test_value)
- answer.append([number_a, number_b])
- if not was_smaller:
- break
- if result == 0:
- result = None
- return (result, answer)
-```
-
-This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations.
-To optimize the `largest` function, we have to start the inner loop from the maximum factor and proceed down to the minimum factor.
-This allows us to stop the inner loop earlier than before.
-We also set the minimum value in the _inner_ loop to the current value of the _outer_ loop.
-
-Here is an example of how the algorithm works and why the loops need to be modified.
-Say we take maximum to be 99 and the minimum 10.
-In the first round:
-
-```
-x = [99 , 98, 97 ...]
-y = [99]
-```
-
-And already we have our result: `9009[91,99]`
-Although the loops have to continue to make us sure there are no higher values.
-
-```
-x = [98, 97, 96 ...]
-y = [99, 98]
-...
-x = [90, 89, 88 ...]
-y = [99, 98,97,96,95,94,93,92,91,90]
-
-Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
-Meaning that running beyond this point won't give us any values higher than 9009.
-
-That is why we introduce the `was_bigger` variable.
-With `was_bigger`, we can check if the inner loop has a bigger value than the current result.
-If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
-We do that by using the [`break`][break] statement.
-
-If we hadn't modified the direction of the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
-This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
-
-The `smallest` function is optimized in a similar way as `largest` function compared to the original approach.
-The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
-Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
-
-[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
-[break]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
deleted file mode 100644
index bf91dd67539..00000000000
--- a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-for number_a in range(max_factor, min_factor - 1,-1):
- was_bigger = False
- for number_b in range(max_factor, number_a - 1, -1):
- if number_a * number_b >= result:
- was_bigger = True
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
new file mode 100644
index 00000000000..d4e4e019084
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -0,0 +1,42 @@
+# Recursion
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ if text == "":
+ return ""
+ first_letter, rest = text[0], text[1:]
+ if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key)
+ else:
+ return first_letter + rotate(rest, key)
+```
+
+This approach uses a very similar approach to alphabet.
+And uses the same logic but instead of a loop so does this approach use [concept:python/recursion]() to solve the problem.
+
+Recursion is a programming technique where a function calls itself.
+It is a powerful technique, but can be more tricky to implement than a while loop.
+Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
+
+Solving this exercise with recursion removes the need for a "result" variable and the instantiation of a `loop`.
+If the number is not equal to one, we call ` + rotate(rest)`.
+Then the `rotate` function can execute the same code again with new values.
+Meaning we can get a long chain or stack of ` + rotate(rest)` until the rest variable is exhausted and then the code adds `""`.
+That translates to something like this: ` + + + + ""`.
+
+In Python, we can't have a function call itself more than 1000 times by default.
+Code that exceeds this recursion limit will throw a [RecursionError][recursion-error].
+While it is possible to adjust the [recursion limit][recursion-limit], doing so risks crashing Python and may also crash your system.
+Casually raising the recursion limit is not recommended.
+
+[clojure]: https://exercism.org/tracks/clojure
+[elixir]: https://exercism.org/tracks/elixir
+[haskell]: https://exercism.org/tracks/haskell
+[recursion]: https://realpython.com/python-thinking-recursively/
+[recursion-error]: https://docs.python.org/3/library/exceptions.html#RecursionError
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt b/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt
new file mode 100644
index 00000000000..098c419fe7b
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt
@@ -0,0 +1,8 @@
+first_letter, rest = text[0], text[1:]
+if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key)
+else:
+ return first_letter + rotate(rest, key)
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
new file mode 100644
index 00000000000..e38f8359c76
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -0,0 +1,54 @@
+# Str Translate
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
+```
+
+This approach uses the [`.translate`][translate] method to solve the problem.
+`translate` takes a translation table as an argument.
+To create a translation table we use [str.`makestrans`][maketrans].
+
+The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
+Then the function `rotate` is declared.
+Then is the `translator` variable defined with the value of the `AlPHABET` constant [sliced][slicing] from the key to the end and then the `AlPHABET` constant sliced from the start to the key.
+
+This is done so we have 2 strings which are the same but shifted by the key.
+Say we have the `AlPHABET` constant with the value of `abcdefghijklmnopqrstuvwxyz` and the key is 3.
+Then the `translator` variable will have the value of `defghijklmnopqrstuvwxyzabc`.
+
+Then is the `translate` method called on the `text` argument.
+The `translate` method takes a translation table as an argument.
+To create a translation table we use str.`makestrans`maketrans.
+
+The `makestrans` method takes 2 arguments.
+The first argument is the string which holds the characters which should be translated.
+The second argument is the string which holds the characters which the characters from the first argument should be translated to.
+
+The first argument is the `AlPHABET` constant and the `AlPHABET` constant uppercased.
+The second argument is the `translator` variable and the `translator` variable uppercased.
+
+What the `makestrans` does is that it takes the first argument and maps it to the second argument.
+It does that by creating a [dictionary], and converting the letter to [unicode][unicode].
+
+```python
+>>> str.maketrans("abc", "def")
+{97: 100, 98: 101, 99: 102}
+```
+
+The `translate` method takes the dictionary created by the `makestrans` method and uses it to translate the characters in the `text` argument.
+
+```python
+>>> "abc".translate({97: 100, 98: 101, 99: 102})
+'def'
+```
+
+When the loop is finished we return the result.
+
+[maketrans]: https://docs.python.org/3/library/stdtypes.html#str.maketrans
+[slicing]: https://www.w3schools.com/python/python_strings_slicing.asp
+[translate]: https://docs.python.org/3/library/stdtypes.html#str.translate
+[unicode]: https://en.wikipedia.org/wiki/Unicode
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt b/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt
new file mode 100644
index 00000000000..75350ae4063
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt
@@ -0,0 +1,5 @@
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.articles/config.json b/exercises/practice/rotational-cipher/.articles/config.json
new file mode 100644
index 00000000000..fe3d6dc2a27
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "8ab0e53c-0e9f-4525-a8ad-dea838e17d8c",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the performance between different approaches",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py b/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
new file mode 100644
index 00000000000..2919024a1f2
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
@@ -0,0 +1,90 @@
+import timeit
+import sys
+import itertools
+
+
+print(sys.version)
+
+
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+COMBINATIONS = itertools.combinations_with_replacement(f"{AlPHABET[:13]}{AlPHABET[:13].upper()} 12,", 2)
+TEST_TEST = "".join([element for sublist in COMBINATIONS for element in sublist])
+
+def rotate_ascii(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
+ return result
+
+
+def rotate_alphabet(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
+ return result
+
+
+def rotate_translate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
+
+
+def rotate_recursion(text, key):
+ if text == "":
+ return ""
+ first_letter, rest = text[0], text[1:]
+ if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate_recursion(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate_recursion(rest, key)
+ else:
+ return first_letter + rotate_recursion(rest, key)
+
+
+
+start_time = timeit.default_timer()
+rotate_ascii(TEST_TEST, 25)
+print("rotate ascii long :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_alphabet(TEST_TEST, 25)
+print("rotate alphabet long :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_translate(TEST_TEST, 25)
+print("rotate translate long :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_recursion(TEST_TEST, 25)
+print("rotate recursion long :", timeit.default_timer() - start_time)
+
+
+
+start_time = timeit.default_timer()
+rotate_ascii("abcABC -12", 11)
+print("rotate ascii short :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_alphabet("abcABC -12", 11)
+print("rotate alphabet short :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_translate("abcABC -12", 11)
+print("rotate translate short :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_recursion("abcABC -12", 11)
+print("rotate recursion short :", timeit.default_timer() - start_time)
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
new file mode 100644
index 00000000000..851180f56f1
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -0,0 +1,43 @@
+# Performance
+
+In this article, we'll examine the performance difference between approaches for `rotational cipher` in Python.
+
+The [approaches page][approaches] lists two approaches to this exercise:
+
+1. [Using recursion][approach-recursion]
+2. [Using `str.translate`][approach-str-translate]
+3. [Using ascii values][approach-ascii-values]
+4. [Using the alphabet][approach-alphabet]
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+These tests were run in windows 11, using Python 3.11.1.
+Your system results may vary.
+
+```
+rotate ascii long : 0.000189200000022538
+rotate alphabet long : 0.0002604000037536025
+rotate translate long : 1.3999990187585354e-05
+rotate recursion long : 0.001309900006162934
+
+rotate ascii short : 4.999994416721165e-06
+rotate alphabet short : 3.6999990697950125e-06
+rotate translate short : 1.0200004908256233e-05
+rotate recursion short : 5.4000120144337416e-06
+```
+
+## Conclusion
+
+For a long string as input, is the translate approach the fastest, followed by ascii, alphabet and last recursion.
+For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and last translate.
+
+This means that if you know the input is a short string, the fastest approach is to use the alphabet approach.
+On the other hand, if you know the input is a long string, the fastest approach is to use the translate approach.
+
+[approach-recursion]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/recursion
+[approach-str-translate]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/str-translate
+[approach-ascii-values]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/ascii-values
+[approach-alphabet]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/alphabet
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
diff --git a/exercises/practice/rotational-cipher/.articles/performance/snippet.md b/exercises/practice/rotational-cipher/.articles/performance/snippet.md
new file mode 100644
index 00000000000..0d4f9baba46
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/performance/snippet.md
@@ -0,0 +1,6 @@
+```
+rotate ascii long : 0.000189200000022538
+rotate alphabet long : 0.0002604000037536025
+rotate translate long : 1.3999990187585354e-05
+rotate recursion long : 0.001309900006162934
+```
From b6185ec43ced29cdabd9bdaa7ee84945c9f6864b Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 6 Feb 2023 10:17:58 -0800
Subject: [PATCH 432/932] Changes and Corrections for rotational-cipher
Edits, spelling, and grammer nits.
---
.../.approaches/alphabet/content.md | 28 ++++----
.../.approaches/ascii-values/content.md | 53 ++++++++-------
.../.approaches/introduction.md | 65 +++++++++++++------
.../.approaches/recursion/content.md | 20 +++---
.../.approaches/str-translate/content.md | 31 ++++-----
.../.articles/performance/content.md | 12 ++--
6 files changed, 120 insertions(+), 89 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
index 75d85c531e2..601a3afc2dc 100644
--- a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
@@ -16,28 +16,28 @@ def rotate(text, key):
return result
```
-The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
-After that the function `rotate` is declared, then a variable `result` is defined with the value of an empty string.
+The approach starts with defining the constant `ALPHABET` which is a string of all lowercase letters.
+The function `rotate()` is then declared, and a variable `result` is defined as an empty string.
-Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
-Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+The text argument is then iterated over via a [`for loop`][for-loop].
+Each element is checked to make sure it is a letter, and subsequently checked if it is uppercase or lowercase.
+Uppercase letters are converted to lowercase.
+Then index of each letter is found in the `AlPHABET` constant.
+The numeric key value is added to the letter index and [modulo (`%`)][modulo] 26 is used on the result.
+Finally, the new number is used as an index into the `AlPHABET` constant, and the resulting letter is converted back to uppercase.
-If it is a uppercased letter then it is converted to lowe case and finds its index in the `AlPHABET` constant.
-Then is the key added to the index and [modulo (`%`)][modulo] 26 is used on the result.
-Then is the letter at the index found in the `AlPHABET` constant and the letter is converted to upcase.
+Lowercase letters follow the same process without the conversion steps.
-If the letter is a lowercased letter then it does the same process but don't convert the letter to downcase and then to uppercase.
+If the element is not a letter (for example, space or punctuation) then it is added directly to the result string.
+The result string is returned once the loop finishes.
-If the letter is not a letter then is the letter added to the result.
-When the loop is finished we return the result.
+If only English letters are needed, the constant [`string.ascii_lowercase`][ascii_lowercase] can be imported from the [`string`][string] module.
-If you only want to use english letters so could you import the alphabet instead of defining it yourself.
-Since in the [`string`][string] module there is a constant called [`ascii_lowercase`][ascii_lowercase] which holds the lowercased alphabet.
```python
-import string
+from string import ascii_lowercase
-AlPHABET = string.ascii_lowercase
+AlPHABET = ascii_lowercase
```
[ascii_lowercase]: https://docs.python.org/3/library/string.html#string.ascii_letters
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index cb637198a9c..5b846ac8d41 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -14,37 +14,44 @@ def rotate(text, key):
return result
```
-This approach uses [ascii values][ascii], ascii stands for American Standard Code for Information Interchange.
-It is a character encoding standard for electronic communication.
-It is a 7-bit code, which means that it can represent 128 different characters.
-The system uses numbers to represent various characters, symbols, and other entities.
+This approach uses [ascii values][ascii].
+ASCII stands for **A**merican **S**tandard **C**ode for **I**nformation **I**nterchange.
+It is a 7-bit character encoding standard for electronic communication first described in 1969, becoming a formal standard in 2015.
+It uses numbers to represent 128 different entities including carriage returns, whitespace characters, box characters, alphabetic characters, punctuation, and the numbers 0-9.
-In ascii can you find all the downcased letter in the range between 97 and 123.
-While the upcased letters are in the range between 65 and 91.
+In ascii, all the lowercase English letters appear between 97 and 123.
+While the uppercase letters are in the range between 65 and 91.
-The reason why you might not want to do this approach is that it only supports the english alphabet.
+~~~~exercism/caution
-The approach starts with defining the function `rotate`, then a variable `result` is defined with the value of an empty string.
-Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
-Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+This approach only supports the English alphabet.
+Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
+For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
+This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-Python has a built in function called `ord` that converts a [unicode][unicode] symbol to an integer.
-The unicode's first 128 characters are the same as ascii.
+~~~~
-If it is a uppercased letter then is [`ord`][ord] used to convert the letters to an integer and is then added with the key and then subtracted with 65.
-Then is the result of that [modulo (`%`)][modulo] 26 added with 65.
-That is because we want to know which index in the alphabet the letter is.
-And if the number is over 26 we want to make sure that it is in the range of 0-26.
-So we use modulo to make sure it is in that range.
-To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the integer value of the letter with 65.
-After that to get the back to a letter we add 65 and use the [`chr`][chr] method which converts an an unicode value to a letter.
-After that is the new letter added to the result.
+The approach starts with defining the function `rotate()`, with a variable `result` is assigned to an empty string.
+The elements of the text argument are then iterated over using a [`for loop`][for-loop].
+Each element is checked to see if it is a letter, and then is checked if it is an uppercase letter.
-If the letter is a lowercased letter then is the same done but with the ascii value of 97 subtracted with the letter.
+Python has a builtin function called `ord` that converts a [unicode][unicode] symbol to an integer representation.
+Unicode's first 128 code points have the same numbers as their ascii counterparts.
-If the letter is not a letter then is the letter added to the result.
-When the loop is finished we return the result.
+If the element is an uppercase letter, [`ord`][ord] is used to convert the letter to an integer.
+The integer is added to the numeric key and then 65 is subtracted from the total.
+Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2) and 65 is added back.
+
+This is because we want to know which letter of the alphabet the number will become.
+And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
+To properly use modulo for a range we have to make sure that it starts at zero, so we subtract 65.
+To get back to a letter in the asciii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
+
+The process is the same for a lowercase letter, but with 97 subtracted to put the letter in the lowercase ascii range of 97 - 123.
+
+Any element that is not a letter (_whitespace or punctuation_) is added directly to the result string.
+When all the elements have been looped over, the full result is returned.
[ascii]: https://en.wikipedia.org/wiki/ASCII
[chr]: https://docs.python.org/3/library/functions.html#chr
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 90b8d6fa8ad..4b3e1889bf8 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -1,22 +1,30 @@
# Introduction
There are various ways to solve `rotational-cipher`.
-You can for example use a [ascii values][ascii], alphabet, recursion, and `str.translate`.
+You can for example use [ascii values][ascii], an alphabet `str` or `list`, recursion, or `str.translate`.
+
## General guidance
-The goal of this exercise is to rotate the letters in a string by a given key.
+The goal of this exercise is to shift the letters in a string by a given integer key between 0 and 26.
+The letter in the encrypted string is shifted for as many values (or "positions") as the value of the key.
## Approach: Using ascii values
-This approach is very simple and easy to understand.
-it uses the ascii value of the letters to rotate them.
-There the numbers 65-91 in the ascii range represent downcased letters.
-While 97-123 represent upcased letters.
+This approach is straightforward to understand.
+It uses the ascii value of the letters to rotate them within the message.
+The numbers 65-91 in the ascii range represent lowercase Latin letters, while 97-123 represent uppercase Latin letters.
+
+
+~~~~exercism/caution
+
+This approach only supports the English alphabet.
+Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
+For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
+This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
+
+~~~~
-The reason why you might not want to do this approach is that it only supports the english alphabet.
-Say we want to use the scandinavian letter: **å**, then this approach will not work.
-Since **å** has the ascii value of 132.
```python
def rotate(text, key):
@@ -34,16 +42,20 @@ def rotate(text, key):
For more information, check the [ascii values approach][approach-ascii-values].
+
## Approach: Alphabet
-This approach is similar to the previous one, but instead of using the ascii values, it uses the index of the letter in the alphabet.
-It requires the storing of a string and unless you are using two strings you have to convert the letters from upper to lower case.
+This approach is similar to the ascii one, but it uses the index number of each letter in an alphabet string.
+It requires making a string for all the letters in an alphabet.
+And unless two strings are used, you will have to convert individual letters from lower to upper case (or vice-versa).
+
+The big advantage of this approach is the ability to use any alphabet (_although there are some issues with combining characters in Unicode._).
+Here, if we want to use the scandinavian letter: **å**, we can simply insert it into our string where we want it:
+`abcdefghijklmnopqrstuvwxyzå` and the rotation will work correctly.
-What this approach although give is the possibility to use any alphabet.
-Say we want to use the scandinavian letter: **å**, then we just add it where we want it:
-`abcdefghijklmnopqrstuvwxyzå` and it will rotate correctly around that.
```python
+# This only uses English characters
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
def rotate(text, key):
@@ -61,14 +73,19 @@ def rotate(text, key):
For more information, check the [Alphabet approach][approach-alphabet].
+
## Approach: Str translate
-This approach is similar to the previous one, but instead of using the index of the letter in the alphabet, it uses the `str.translate` method.
-The benefit of this approach is that it has no visible loop, thereby the code becomes more concise.
-What to note is that the `str.translate` still loops over the `string` thereby even if it is no visible loop, it doesn't mean that a method is not looping.
+This approach uses the [`str.translate`][str-translate] method to create a mapping from input to shifted string instead of using the index of an alphabet string to calculate the shift.
+The benefit of this approach is that it has no visible loop, making the code more concise.
+
+~~~~exercism/note
+`str.translate` **still loops over the `string`** even if it is not visibly doing so.
+~~~~
+
```python
-AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+AlPHABET = "abcdefghijklmnopqrstuvwxyz
def rotate(text, key):
translator = AlPHABET[key:] + AlPHABET[:key]
@@ -77,13 +94,19 @@ def rotate(text, key):
For more information, check the [Str translate approach][approach-str-translate].
+
## Approach: Recursion
-In this approach we use a recursive function.
+This approach uses a recursive function.
A recursive function is a function that calls itself.
This approach can be more concise than other approaches, and may also be more readable for some audiences.
-The reason why you might not want to use this approach is that Python has a [recursion limit][recursion-limit] with a default of 1000.
+
+~~~~exercism/caution
+Python does not have any tail-call optimization and has a default [recursion limit][recursion-limit] of 1000 calls on the stack.
+Calculate your base case carefully to avoid errors.
+~~~~
+
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -103,6 +126,7 @@ def rotate(text, key):
For more information, check the [Recursion approach][approach-recursion].
+
## Benchmark
For more information, check the [Performance article][article-performance].
@@ -114,3 +138,4 @@ For more information, check the [Performance article][article-performance].
[approach-alphabet]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/alphabet
[article-performance]: https://exercism.org/tracks/python/exercises/rotational-cipher/articles/performance
[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
+[str-translate]: https://docs.python.org/3/library/stdtypes.html?highlight=str%20translate#str.translate
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
index d4e4e019084..ca7bf2393da 100644
--- a/exercises/practice/rotational-cipher/.approaches/recursion/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -16,23 +16,25 @@ def rotate(text, key):
return first_letter + rotate(rest, key)
```
-This approach uses a very similar approach to alphabet.
-And uses the same logic but instead of a loop so does this approach use [concept:python/recursion]() to solve the problem.
+This approach uses the same logic as that of the `alphabet` approach, but instead of a `loop`, [concept:python/recursion]() is used to parse the text and solve the problem.
Recursion is a programming technique where a function calls itself.
-It is a powerful technique, but can be more tricky to implement than a while loop.
-Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
+It is powerful, but can be more tricky to implement than a `while loop` or `for loop`.
+Recursive techniques are more common in functional programming languages like [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure] than they are in Python.
-Solving this exercise with recursion removes the need for a "result" variable and the instantiation of a `loop`.
+Solving this exercise with recursion removes the need for a `result` variable and the instantiation of a `loop`.
If the number is not equal to one, we call ` + rotate(rest)`.
Then the `rotate` function can execute the same code again with new values.
-Meaning we can get a long chain or stack of ` + rotate(rest)` until the rest variable is exhausted and then the code adds `""`.
+We can build a long chain or "stack" of ` + rotate(rest)` calls until the `rest` variable is exhausted and the code adds `""`.
That translates to something like this: ` + + + + ""`.
-In Python, we can't have a function call itself more than 1000 times by default.
-Code that exceeds this recursion limit will throw a [RecursionError][recursion-error].
-While it is possible to adjust the [recursion limit][recursion-limit], doing so risks crashing Python and may also crash your system.
+
+~~~~exercism/note
+By default, we can't have a function call itself more than 1000 times.
+Code that exceeds this recursion limit will throw a RecursionError.
+While it is possible to adjust the recursion limit, doing so risks crashing Python and may also crash your system.
Casually raising the recursion limit is not recommended.
+~~~~
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
index e38f8359c76..b95f5139b19 100644
--- a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -8,45 +8,42 @@ def rotate(text, key):
return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
```
-This approach uses the [`.translate`][translate] method to solve the problem.
+This approach uses the [`.translate`][translate] method.
`translate` takes a translation table as an argument.
To create a translation table we use [str.`makestrans`][maketrans].
-The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
-Then the function `rotate` is declared.
-Then is the `translator` variable defined with the value of the `AlPHABET` constant [sliced][slicing] from the key to the end and then the `AlPHABET` constant sliced from the start to the key.
+This approach starts with defining a constant of all the lowercase letters in the alphabet.
+Then the function `rotate()` is declared.
+A `translator` variable defined with the value of the `AlPHABET` constant [sliced][slicing] from the key to the end and then sliced from the start to the key.
-This is done so we have 2 strings which are the same but shifted by the key.
+This is done so we have 2 strings which are the same but shifted by the key value.
Say we have the `AlPHABET` constant with the value of `abcdefghijklmnopqrstuvwxyz` and the key is 3.
Then the `translator` variable will have the value of `defghijklmnopqrstuvwxyzabc`.
-Then is the `translate` method called on the `text` argument.
-The `translate` method takes a translation table as an argument.
-To create a translation table we use str.`makestrans`maketrans.
+`str.translate` is then called on the `text` argument.
+`str.translate` takes a translation table mapping start values to transformed values as an argument.
+To create a translation table, `str.makestrans` is used.
+`makestrans` takes 2 arguments: the first is the string to be translated, and the second is the string the first argument should be translated to.
-The `makestrans` method takes 2 arguments.
-The first argument is the string which holds the characters which should be translated.
-The second argument is the string which holds the characters which the characters from the first argument should be translated to.
+For our solution, the first argument is the `AlPHABET` constant + the `AlPHABET` constant in uppercase.
+The second argument is the `translator` variable + uppercase `translator` variable.
-The first argument is the `AlPHABET` constant and the `AlPHABET` constant uppercased.
-The second argument is the `translator` variable and the `translator` variable uppercased.
+`makestrans` does is that it takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
-What the `makestrans` does is that it takes the first argument and maps it to the second argument.
-It does that by creating a [dictionary], and converting the letter to [unicode][unicode].
```python
>>> str.maketrans("abc", "def")
{97: 100, 98: 101, 99: 102}
```
-The `translate` method takes the dictionary created by the `makestrans` method and uses it to translate the characters in the `text` argument.
+`str.translate` takes the `dict` created by `str.makestrans` and uses it to translate the characters in the `text` argument.
```python
>>> "abc".translate({97: 100, 98: 101, 99: 102})
'def'
```
-When the loop is finished we return the result.
+Once the `str.translate` loop completes, we return the `result`.
[maketrans]: https://docs.python.org/3/library/stdtypes.html#str.maketrans
[slicing]: https://www.w3schools.com/python/python_strings_slicing.asp
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 851180f56f1..98334011550 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -2,7 +2,7 @@
In this article, we'll examine the performance difference between approaches for `rotational cipher` in Python.
-The [approaches page][approaches] lists two approaches to this exercise:
+The [approaches page][approaches] lists four approaches to this exercise:
1. [Using recursion][approach-recursion]
2. [Using `str.translate`][approach-str-translate]
@@ -12,7 +12,7 @@ The [approaches page][approaches] lists two approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These tests were run in windows 11, using Python 3.11.1.
+These tests were run in `Windows 11`, using Python `3.11.1`.
Your system results may vary.
```
@@ -29,11 +29,11 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
-For a long string as input, is the translate approach the fastest, followed by ascii, alphabet and last recursion.
-For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and last translate.
+For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
+For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
-This means that if you know the input is a short string, the fastest approach is to use the alphabet approach.
-On the other hand, if you know the input is a long string, the fastest approach is to use the translate approach.
+This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
+On the other hand, if the input is a long string, the overhead of making a dictionary is amortized over the length of the text to be translated, and the fastest approach becomes `str.translate`.
[approach-recursion]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/recursion
[approach-str-translate]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/str-translate
From 2f190cd045a8a7023c93cb1e4a95e005a955c04e Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:36:14 +0100
Subject: [PATCH 433/932] Updated formating
---
.../.approaches/alphabet/content.md | 1 -
.../.approaches/ascii-values/content.md | 7 +++---
.../.approaches/introduction.md | 23 +++++--------------
.../.approaches/recursion/content.md | 5 ++--
.../.approaches/str-translate/content.md | 3 +--
.../.articles/performance/content.md | 2 +-
6 files changed, 13 insertions(+), 28 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
index 601a3afc2dc..e79625dbdf3 100644
--- a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
@@ -33,7 +33,6 @@ The result string is returned once the loop finishes.
If only English letters are needed, the constant [`string.ascii_lowercase`][ascii_lowercase] can be imported from the [`string`][string] module.
-
```python
from string import ascii_lowercase
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 5b846ac8d41..0b9fb077181 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -22,15 +22,14 @@ It uses numbers to represent 128 different entities including carriage returns,
In ascii, all the lowercase English letters appear between 97 and 123.
While the uppercase letters are in the range between 65 and 91.
-~~~~exercism/caution
+```exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-~~~~
-
+```
The approach starts with defining the function `rotate()`, with a variable `result` is assigned to an empty string.
The elements of the text argument are then iterated over using a [`for loop`][for-loop].
@@ -41,7 +40,7 @@ Unicode's first 128 code points have the same numbers as their ascii counterpart
If the element is an uppercase letter, [`ord`][ord] is used to convert the letter to an integer.
The integer is added to the numeric key and then 65 is subtracted from the total.
-Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2) and 65 is added back.
+Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2_) and 65 is added back.
This is because we want to know which letter of the alphabet the number will become.
And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 4b3e1889bf8..67a6264dfda 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -3,7 +3,6 @@
There are various ways to solve `rotational-cipher`.
You can for example use [ascii values][ascii], an alphabet `str` or `list`, recursion, or `str.translate`.
-
## General guidance
The goal of this exercise is to shift the letters in a string by a given integer key between 0 and 26.
@@ -15,16 +14,14 @@ This approach is straightforward to understand.
It uses the ascii value of the letters to rotate them within the message.
The numbers 65-91 in the ascii range represent lowercase Latin letters, while 97-123 represent uppercase Latin letters.
-
-~~~~exercism/caution
+```exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-~~~~
-
+```
```python
def rotate(text, key):
@@ -42,7 +39,6 @@ def rotate(text, key):
For more information, check the [ascii values approach][approach-ascii-values].
-
## Approach: Alphabet
This approach is similar to the ascii one, but it uses the index number of each letter in an alphabet string.
@@ -53,7 +49,6 @@ The big advantage of this approach is the ability to use any alphabet (_although
Here, if we want to use the scandinavian letter: **å**, we can simply insert it into our string where we want it:
`abcdefghijklmnopqrstuvwxyzå` and the rotation will work correctly.
-
```python
# This only uses English characters
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -73,16 +68,14 @@ def rotate(text, key):
For more information, check the [Alphabet approach][approach-alphabet].
-
## Approach: Str translate
This approach uses the [`str.translate`][str-translate] method to create a mapping from input to shifted string instead of using the index of an alphabet string to calculate the shift.
The benefit of this approach is that it has no visible loop, making the code more concise.
-~~~~exercism/note
+```exercism/note
`str.translate` **still loops over the `string`** even if it is not visibly doing so.
-~~~~
-
+```
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz
@@ -94,19 +87,16 @@ def rotate(text, key):
For more information, check the [Str translate approach][approach-str-translate].
-
## Approach: Recursion
This approach uses a recursive function.
A recursive function is a function that calls itself.
This approach can be more concise than other approaches, and may also be more readable for some audiences.
-
-~~~~exercism/caution
+```exercism/caution
Python does not have any tail-call optimization and has a default [recursion limit][recursion-limit] of 1000 calls on the stack.
Calculate your base case carefully to avoid errors.
-~~~~
-
+```
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -126,7 +116,6 @@ def rotate(text, key):
For more information, check the [Recursion approach][approach-recursion].
-
## Benchmark
For more information, check the [Performance article][article-performance].
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
index ca7bf2393da..1addd0849f7 100644
--- a/exercises/practice/rotational-cipher/.approaches/recursion/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -28,13 +28,12 @@ Then the `rotate` function can execute the same code again with new values.
We can build a long chain or "stack" of ` + rotate(rest)` calls until the `rest` variable is exhausted and the code adds `""`.
That translates to something like this: ` + + + + ""`.
-
-~~~~exercism/note
+```exercism/note
By default, we can't have a function call itself more than 1000 times.
Code that exceeds this recursion limit will throw a RecursionError.
While it is possible to adjust the recursion limit, doing so risks crashing Python and may also crash your system.
Casually raising the recursion limit is not recommended.
-~~~~
+```
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
index b95f5139b19..bee4885a7b1 100644
--- a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -30,13 +30,12 @@ The second argument is the `translator` variable + uppercase `translator` variab
`makestrans` does is that it takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
-
```python
>>> str.maketrans("abc", "def")
{97: 100, 98: 101, 99: 102}
```
-`str.translate` takes the `dict` created by `str.makestrans` and uses it to translate the characters in the `text` argument.
+`str.translate` takes the `dict` created by `str.makestrans` and uses it to translate the characters in the `text` argument.
```python
>>> "abc".translate({97: 100, 98: 101, 99: 102})
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 98334011550..92d6c1f14d4 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -30,7 +30,7 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
-For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
+For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
On the other hand, if the input is a long string, the overhead of making a dictionary is amortized over the length of the text to be translated, and the fastest approach becomes `str.translate`.
From 3d714863bf0deb53d18885b9b84eecd14895d37c Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:45:50 +0100
Subject: [PATCH 434/932] Added back tilda
---
.../.approaches/ascii-values/content.md | 6 +++---
.../rotational-cipher/.approaches/introduction.md | 12 ++++++------
.../.approaches/recursion/content.md | 4 ++--
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 0b9fb077181..5b37134ed00 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -22,14 +22,14 @@ It uses numbers to represent 128 different entities including carriage returns,
In ascii, all the lowercase English letters appear between 97 and 123.
While the uppercase letters are in the range between 65 and 91.
-```exercism/caution
+~~~~exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-```
+~~~~
The approach starts with defining the function `rotate()`, with a variable `result` is assigned to an empty string.
The elements of the text argument are then iterated over using a [`for loop`][for-loop].
@@ -40,7 +40,7 @@ Unicode's first 128 code points have the same numbers as their ascii counterpart
If the element is an uppercase letter, [`ord`][ord] is used to convert the letter to an integer.
The integer is added to the numeric key and then 65 is subtracted from the total.
-Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2_) and 65 is added back.
+Finally, the result is [modulo (%)][modulo] 26 (_to put the value within the 0-26 range_) and 65 is added back.
This is because we want to know which letter of the alphabet the number will become.
And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 67a6264dfda..f8c5e95ed0f 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -14,14 +14,14 @@ This approach is straightforward to understand.
It uses the ascii value of the letters to rotate them within the message.
The numbers 65-91 in the ascii range represent lowercase Latin letters, while 97-123 represent uppercase Latin letters.
-```exercism/caution
+~~~~exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-```
+~~~~
```python
def rotate(text, key):
@@ -73,9 +73,9 @@ For more information, check the [Alphabet approach][approach-alphabet].
This approach uses the [`str.translate`][str-translate] method to create a mapping from input to shifted string instead of using the index of an alphabet string to calculate the shift.
The benefit of this approach is that it has no visible loop, making the code more concise.
-```exercism/note
+~~~~exercism/note
`str.translate` **still loops over the `string`** even if it is not visibly doing so.
-```
+~~~~
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz
@@ -93,10 +93,10 @@ This approach uses a recursive function.
A recursive function is a function that calls itself.
This approach can be more concise than other approaches, and may also be more readable for some audiences.
-```exercism/caution
+~~~~exercism/caution
Python does not have any tail-call optimization and has a default [recursion limit][recursion-limit] of 1000 calls on the stack.
Calculate your base case carefully to avoid errors.
-```
+~~~~
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
index 1addd0849f7..689f8aaed7e 100644
--- a/exercises/practice/rotational-cipher/.approaches/recursion/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -28,12 +28,12 @@ Then the `rotate` function can execute the same code again with new values.
We can build a long chain or "stack" of ` + rotate(rest)` calls until the `rest` variable is exhausted and the code adds `""`.
That translates to something like this: ` + + + + ""`.
-```exercism/note
+~~~~exercism/note
By default, we can't have a function call itself more than 1000 times.
Code that exceeds this recursion limit will throw a RecursionError.
While it is possible to adjust the recursion limit, doing so risks crashing Python and may also crash your system.
Casually raising the recursion limit is not recommended.
-```
+~~~~
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
From 3c2f97b27cd43e99cd082ec164cebeaa992375cf Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:51:32 +0100
Subject: [PATCH 435/932] fix
---
.../practice/rotational-cipher/.articles/performance/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 92d6c1f14d4..0b40eb7cded 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -30,7 +30,7 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
-For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
+For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and finally `str.translate`.
This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
On the other hand, if the input is a long string, the overhead of making a dictionary is amortized over the length of the text to be translated, and the fastest approach becomes `str.translate`.
From c63546060c307e160a8a0d9b2e6a3643bc6e368d Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:56:53 +0100
Subject: [PATCH 436/932] fix
---
.../rotational-cipher/.approaches/ascii-values/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 5b37134ed00..a1268985264 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -45,7 +45,7 @@ Finally, the result is [modulo (%)][modulo] 26 (_to put the value within the 0-2
This is because we want to know which letter of the alphabet the number will become.
And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
To properly use modulo for a range we have to make sure that it starts at zero, so we subtract 65.
-To get back to a letter in the asciii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
+To get back to a letter in the ascii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
The process is the same for a lowercase letter, but with 97 subtracted to put the letter in the lowercase ascii range of 97 - 123.
From 588058183e1c976982e458344351fcf7d5c14ded Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 6 Feb 2023 11:02:42 -0800
Subject: [PATCH 437/932] Apply suggestions from code review
Two final nits...
---
.../rotational-cipher/.approaches/ascii-values/content.md | 2 +-
.../rotational-cipher/.approaches/str-translate/content.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index a1268985264..c3fba5945aa 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -47,7 +47,7 @@ And if the new number is over 26 we want to make sure that it "wraps around" to
To properly use modulo for a range we have to make sure that it starts at zero, so we subtract 65.
To get back to a letter in the ascii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
-The process is the same for a lowercase letter, but with 97 subtracted to put the letter in the lowercase ascii range of 97 - 123.
+The process is the same for a lowercase letter, but with 97 subtracted/added to put the letter in the lowercase ascii range of 97 - 123.
Any element that is not a letter (_whitespace or punctuation_) is added directly to the result string.
When all the elements have been looped over, the full result is returned.
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
index bee4885a7b1..b80da8d2ec2 100644
--- a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -28,7 +28,7 @@ To create a translation table, `str.makestrans` is used.
For our solution, the first argument is the `AlPHABET` constant + the `AlPHABET` constant in uppercase.
The second argument is the `translator` variable + uppercase `translator` variable.
-`makestrans` does is that it takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
+`str.makestrans` takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
```python
>>> str.maketrans("abc", "def")
From c8967079f25f9864d9eef143a77554180c1322be Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 6 Feb 2023 11:04:42 -0800
Subject: [PATCH 438/932] Update
exercises/practice/rotational-cipher/.articles/performance/content.md
---
.../practice/rotational-cipher/.articles/performance/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 0b40eb7cded..8401b40e255 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -29,7 +29,7 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
-For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
+For a long string as input, the `str.translate` approach is the fastest, followed by ascii, alphabet, and finally recursion.
For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and finally `str.translate`.
This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
From 5e8951a574473436bad39e9f10ebf6ee9fe45b2d Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 9 Feb 2023 16:41:40 -0800
Subject: [PATCH 439/932] Addressed issues 3181 and 3161 for lasagna test file.
---
.../guidos-gorgeous-lasagna/lasagna_test.py | 24 +++++++++++++------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
index 3a10c9db6b1..aeacc53b07f 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
+++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
@@ -1,29 +1,32 @@
import unittest
import pytest
-
+# For this first exercise, it is really important to be clear about how we are importing names for tests.
+# To that end, we are putting a try/catch around imports and throwing specific messages to help students
+# decode that they need to create and title their constants and functions in a specific way.
try:
from lasagna import (EXPECTED_BAKE_TIME,
bake_time_remaining,
preparation_time_in_minutes,
elapsed_time_in_minutes)
-
+# Here, we are separating the constant import errors from the function name import errors
except ImportError as import_fail:
message = import_fail.args[0].split('(', maxsplit=1)
item_name = import_fail.args[0].split()[3]
- if 'EXPECTED_BAKE_TIME' in message:
+ if 'EXPECTED_BAKE_TIME' in item_name:
# pylint: disable=raise-missing-from
- raise ImportError(f'We can not find or import the constant {item_name} in your'
- " 'lasagna.py' file. Did you mis-name or forget to define it?")
+ raise ImportError(f'\n\nMISSING CONSTANT --> \nWe can not find or import the constant {item_name} in your'
+ " 'lasagna.py' file.\nDid you mis-name or forget to define it?") from None
else:
item_name = item_name[:-1] + "()'"
# pylint: disable=raise-missing-from
- raise ImportError("In your 'lasagna.py' file, we can not find or import the"
- f' function named {item_name}. Did you mis-name or forget to define it?')
+ raise ImportError("\n\nMISSING FUNCTION --> In your 'lasagna.py' file, we can not find or import the"
+ f' function named {item_name}. \nDid you mis-name or forget to define it?') from None
+# Here begins the formal test cases for the exercise.
class LasagnaTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
@@ -64,9 +67,16 @@ def test_elapsed_time_in_minutes(self):
@pytest.mark.task(taskno=5)
def test_docstrings_were_written(self):
+ """Validate function.__doc__ exists for each function.
+ Check the attribute dictionary of each listed function
+ for the presence of a __doc__ key.
+
+ :return: unexpectedly None error when __doc__ key is missing.
+ """
functions = [bake_time_remaining, preparation_time_in_minutes, elapsed_time_in_minutes]
for variant, function in enumerate(functions, start=1):
with self.subTest(f'variation #{variant}', function=function):
failure_msg = f'Expected a docstring for `{function.__name__}`, but received `None` instead.'
+ # Check that the __doc__ key is populated for the function.
self.assertIsNotNone(function.__doc__, msg=failure_msg)
From ba8dbecf2acdb42cc8697443a1ce3bf499bc2a4d Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 22 Feb 2023 18:41:49 -0800
Subject: [PATCH 440/932] Apply suggestions from code review
Co-authored-by: Victor Goff
---
exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
index aeacc53b07f..d200cb926a1 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
+++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
@@ -18,7 +18,7 @@
if 'EXPECTED_BAKE_TIME' in item_name:
# pylint: disable=raise-missing-from
raise ImportError(f'\n\nMISSING CONSTANT --> \nWe can not find or import the constant {item_name} in your'
- " 'lasagna.py' file.\nDid you mis-name or forget to define it?") from None
+ " 'lasagna.py' file.\nDid you misname or forget to define it?") from None
else:
item_name = item_name[:-1] + "()'"
# pylint: disable=raise-missing-from
From 27fa70430bc15ae8966b3d6cc48a7c4eee65e771 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 22 Feb 2023 18:42:16 -0800
Subject: [PATCH 441/932] Apply suggestions from code review
Co-authored-by: Victor Goff
---
exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
index d200cb926a1..7d0a7d9f1bc 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
+++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
@@ -23,7 +23,7 @@
item_name = item_name[:-1] + "()'"
# pylint: disable=raise-missing-from
raise ImportError("\n\nMISSING FUNCTION --> In your 'lasagna.py' file, we can not find or import the"
- f' function named {item_name}. \nDid you mis-name or forget to define it?') from None
+ f' function named {item_name}. \nDid you misname or forget to define it?') from None
# Here begins the formal test cases for the exercise.
From 2f73fb34ce49d2e1814c134fb41ec395c9ca001d Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 18 Feb 2023 01:05:28 -0800
Subject: [PATCH 442/932] Additional edits and adjustments to introduction and
related files for clarity.
---
.../guidos-gorgeous-lasagna/.docs/hints.md | 15 +-
.../.docs/instructions.md | 2 +-
.../.docs/introduction.md | 189 +++++++++++-------
.../guidos-gorgeous-lasagna/lasagna.py | 46 ++++-
4 files changed, 165 insertions(+), 87 deletions(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md
index 41e3c669f00..351daef986a 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md
@@ -4,6 +4,7 @@
- [The Python Tutorial][the python tutorial] can be a great introduction.
- [Numbers][numbers] in Python can be integers, floats, or complex.
+- [PEP 8][PEP8] is the Python code style guide.
## 1. Define expected bake time in minutes
@@ -33,13 +34,13 @@
- Clearly [commenting][comments] and [documenting][docstrings] your code according to [PEP257][PEP257] is always recommended.
-[the python tutorial]: https://docs.python.org/3/tutorial/introduction.html
-[numbers]: https://docs.python.org/3/tutorial/introduction.html#numbers
-[naming]: https://realpython.com/python-variables/
+[PEP257]: https://www.python.org/dev/peps/pep-0257/
[assignment]: https://docs.python.org/3/reference/simple_stmts.html#grammar-token-assignment-stmt
-[defining functions]: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
-[return]: https://docs.python.org/3/reference/simple_stmts.html#return
-[python as a calculator]: https://docs.python.org/3/tutorial/introduction.html#using-python-as-a-calculator
[comments]: https://realpython.com/python-comments-guide/
+[defining functions]: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
[docstrings]: https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings
-[PEP257]: https://www.python.org/dev/peps/pep-0257/
\ No newline at end of file
+[naming]: https://realpython.com/python-variables/
+[numbers]: https://docs.python.org/3/tutorial/introduction.html#numbers
+[python as a calculator]: https://docs.python.org/3/tutorial/introduction.html#using-python-as-a-calculator
+[return]: https://docs.python.org/3/reference/simple_stmts.html#return
+[the python tutorial]: https://docs.python.org/3/tutorial/introduction.html
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
index 321e77761b4..f4b9480e502 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
@@ -49,7 +49,7 @@ This function should return the total number of minutes you've been cooking, or
## 5. Update the recipe with notes
-Go back through the recipe, adding notes and documentation.
+Go back through the recipe, adding notes in the form of function docstrings.
```python
def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time):
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
index 1703d430829..e9d5b96db64 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
@@ -1,29 +1,59 @@
# Introduction
-[Python][python docs] is a [dynamic and strongly][dynamic typing in python] typed [object-oriented][object oriented programming] programming language.
+Python is a [dynamic and strongly][dynamic typing in python] typed [object-oriented][object oriented programming] programming language.
It employs both [duck typing][duck typing] and [gradual typing][gradual typing], via [type hints][type hints].
-Programming across paradigms is fully _supported_ -- but internally, [everything in Python is an object][everythings an object].
+Python supports Imperative, declarative (e.g. functional), and object oriented programming _styles_, but internally [everything in Python is an object][everythings an object].
-Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] for function, method, and class definitions.
-[The Zen of Python (PEP 20)][the zen of python] and [_What is Pythonic?_][what is pythonic] lay out additional philosophies.
+This exercise introduces 4 major Python language features: Names (_variables and constants_), Functions (_and the return keyword_), Comments, and Docstrings.
-Objects are [assigned][assignment statements] to [names][naming and binding] via the _assignment operator_, `=`.
-[Variables][variables] are written in [`snake_case`][snake case], and _constants_ usually in `SCREAMING_SNAKE_CASE`.
-A `name` (_variable or constant_) is not itself typed, and can be attached or re-attached to different objects over its lifetime.
-For extended naming conventions and advice, see [PEP 8][pep8].
+~~~~exercism/note
+
+In general, content, tests, and analyzer tooling for the Python track follow the style conventions outlined in [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 257](https://www.python.org/dev/peps/pep-0257/) for Python code style, with the additional (strong) suggestion that there be no single letter variable names.
+
+~~~~
+
+
+## Name Assignment and Re-assignment
+
+
+There are no keywords in Python to define variables or constants and there is no difference in the way Python treats them.
+Both are considered [_names_][facts-and-myths-about-python-names] that help programmers reference values (_objects_) in a program and are written differently only by convention.
+On Exercism, [variables][variables] are always written in [`snake_case`][snake case], and _constants_ in `SCREAMING_SNAKE_CASE`.
+
+Names are assigned to values via `=`, or the [_assignment operator_][assignment statements]: ` = `.
+A name (_variable or constant_) can be assigned or re-assigned over its lifetime to different values/different object types.
+For example, `my_first_variable` can be assigned and re-assigned many times using `=`, and can refer to different object types with each re-assignment:
```python
+# Assigning my_first_variable to a numeric value.
>>> my_first_variable = 1
->>> my_first_variable = "Last one, I promise"
+>>> print(type(my_first_variable))
+...
+
+
>>> print(my_first_variable)
...
-"Last one, I promise"
+1
+
+# Reassigning my_first_variable to a new string value.
+>>> my_first_variable = "Now, I'm a string."
+>>> print(type(my_first_variable))
+...
+
+
+>>> print(my_first_variable)
+...
+"Now, I'm a string."
```
-Constants are typically defined on a [module][module] or _global_ level, and although they _can_ be changed, they are _intended_ to be named only once.
-Their `SCREAMING_SNAKE_CASE` is a message to other developers that the assignment should not be altered:
+### Constants
+
+Constants are typically defined at a [module][module] level, being values that are accessible outside function or class scope.
+Constant names **_can be reassigned to new values_**, but they are _intended_ to be named only once.
+Using `SCREAMING_SNAKE_CASE` warns other programmers that these names should not be mutated or reassigned.
+
```python
# All caps signal that this is intended as a constant.
@@ -31,23 +61,43 @@ MY_FIRST_CONSTANT = 16
# Re-assignment will be allowed by the compiler & interpreter,
# but this is VERY strongly discouraged.
-# Please don't do: MY_FIRST_CONSTANT = "Some other value"
+# Please don't: MY_FIRST_CONSTANT = "Some other value"
```
+
+## Functions
+
The keyword `def` begins a [function definition][function definition].
It must be followed by the function name and a parenthesized list of zero or more formal [parameters][parameters].
Parameters can be of several different varieties, and can even [vary][more on functions] in length.
The `def` line is terminated with a colon.
+```python
+# function definition
+def my_function_name(parameter, second_parameter):
+
+
+```
+
Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_.
There is no strict indentation amount (_either space **OR** [tab] characters are acceptable_), but [indentation][indentation] must be _consistent for all indented statements_.
+
+```python
+# Function definition on first line.
+def add_two_numbers(number_one, number_two):
+ print(number_one + number_two) # Prints the sum of the numbers, and is indented by 2 spaces.
+
+>>> add_two_numbers(3, 4)
+7
+```
+
Functions explicitly return a value or object via the [`return`][return] keyword.
```python
# Function definition on first line.
def add_two_numbers(number_one, number_two):
- return number_one + number_two # Returns the sum of the numbers, and is indented by 2 spaces.
+ return number_one + number_two # Returns the sum of the numbers.
>>> add_two_numbers(3, 4)
7
@@ -64,33 +114,28 @@ def add_two_numbers(number_one, number_two):
None
```
-Inconsistent indentation will raise an error:
+While you may choose any indentation depth, _inconsistent_ indentation in your code blocks will raise an error:
```python
# The return statement line does not match the first line indent.
>>> def add_three_numbers_misformatted(number_one, number_two, number_three):
... result = number_one + number_two + number_three # Indented by 4 spaces.
... return result #this was only indented by 3 spaces
+...
+...
File "", line 3
return result
^
IndentationError: unindent does not match any outer indentation level
```
-Functions are [_called_][calls] using their name followed by `()`.
-The number of arguments passed in the parentheses must match the number of parameters in the original function definition unless [default arguments][default arguments] have been used:
+### Calling Functions
+
+Functions are [_called_][calls] or invoked using their name followed by `()`.
+The number of arguments passed in the parentheses must match the number of parameters in the original function definition..
```python
->>> def number_to_the_power_of(number_one, number_two):
- """Raise a number to an arbitrary power.
-
- :param number_one: int the base number.
- :param number_two: int the power to raise the base number to.
- :return: int - number raised to power of second number
-
- Takes number_one and raises it to the power of number_two, returning the result.
- """
-
+>>> def number_to_the_power_of(number_one, number_two):
return number_one ** number_two
...
@@ -98,7 +143,7 @@ The number of arguments passed in the parentheses must match the number of param
27
```
-A mis-match between parameters and arguments will raise an error:
+A mis-match between the number of parameters and the number of arguments will raise an error:
```python
>>> number_to_the_power_of(4,)
@@ -109,48 +154,34 @@ TypeError: number_to_the_power_of() missing 1 required positional argument: 'num
```
-Adding a [default value][default arguments] for a parameter can defend against such errors:
+Calling functions defined inside a class (_class methods_) use `