From fa137afa928b4a6d621a6d47647e32bc7d0afb3c Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 10 Mar 2023 13:26:20 -0800 Subject: [PATCH 1/5] Initial docs for darts approaches. --- exercises/practice/darts/.approaches/config.json | 0 exercises/practice/darts/.approaches/introduction.md | 0 exercises/practice/darts/.articles/config.json | 0 exercises/practice/darts/.articles/performance/content.md | 0 exercises/practice/darts/.articles/performance/snippet.md | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 exercises/practice/darts/.approaches/config.json create mode 100644 exercises/practice/darts/.approaches/introduction.md create mode 100644 exercises/practice/darts/.articles/config.json create mode 100644 exercises/practice/darts/.articles/performance/content.md create mode 100644 exercises/practice/darts/.articles/performance/snippet.md diff --git a/exercises/practice/darts/.approaches/config.json b/exercises/practice/darts/.approaches/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/exercises/practice/darts/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/exercises/practice/darts/.articles/config.json b/exercises/practice/darts/.articles/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/exercises/practice/darts/.articles/performance/content.md b/exercises/practice/darts/.articles/performance/content.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/exercises/practice/darts/.articles/performance/snippet.md b/exercises/practice/darts/.articles/performance/snippet.md new file mode 100644 index 0000000000..e69de29bb2 From 547ca2a8520276aa14ed663f9a4defc8a8929add Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 5 Apr 2023 14:59:05 -0700 Subject: [PATCH 2/5] Initial rough draft of darts approaches. --- .../.approaches/booleans-as-ints/content.md | 7 + .../.approaches/booleans-as-ints/snippet.txt | 3 + .../practice/darts/.approaches/config.json | 50 +++++ .../.approaches/dict-and-dict-get/content.md | 14 ++ .../.approaches/dict-and-dict-get/snippet.txt | 5 + .../.approaches/dict-and-generator/content.md | 12 ++ .../dict-and-generator/snippet.txt | 8 + .../.approaches/if-statements/content.md | 26 +++ .../.approaches/if-statements/snippet.txt | 8 + .../darts/.approaches/introduction.md | 180 ++++++++++++++++++ .../darts/.approaches/match-case/content.md | 13 ++ .../darts/.approaches/match-case/snippet.txt | 8 + .../.approaches/tuple-and-loop/content.md | 11 ++ .../.approaches/tuple-and-loop/snippet.txt | 7 + 14 files changed, 352 insertions(+) create mode 100644 exercises/practice/darts/.approaches/booleans-as-ints/content.md create mode 100644 exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt create mode 100644 exercises/practice/darts/.approaches/dict-and-dict-get/content.md create mode 100644 exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt create mode 100644 exercises/practice/darts/.approaches/dict-and-generator/content.md create mode 100644 exercises/practice/darts/.approaches/dict-and-generator/snippet.txt create mode 100644 exercises/practice/darts/.approaches/if-statements/content.md create mode 100644 exercises/practice/darts/.approaches/if-statements/snippet.txt create mode 100644 exercises/practice/darts/.approaches/match-case/content.md create mode 100644 exercises/practice/darts/.approaches/match-case/snippet.txt create mode 100644 exercises/practice/darts/.approaches/tuple-and-loop/content.md create mode 100644 exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/content.md b/exercises/practice/darts/.approaches/booleans-as-ints/content.md new file mode 100644 index 0000000000..d54327e2ac --- /dev/null +++ b/exercises/practice/darts/.approaches/booleans-as-ints/content.md @@ -0,0 +1,7 @@ +# Using Boolean Values as Integers + +```python +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 +``` \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt b/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt new file mode 100644 index 0000000000..ec7dcfabbf --- /dev/null +++ b/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt @@ -0,0 +1,3 @@ +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/config.json b/exercises/practice/darts/.approaches/config.json index e69de29bb2..aa93e2aebe 100644 --- a/exercises/practice/darts/.approaches/config.json +++ b/exercises/practice/darts/.approaches/config.json @@ -0,0 +1,50 @@ +{ + "introduction": { + "authors": ["bethanyg"], + "contributors": [] + }, + "approaches": [ + { + "uuid": "7d78f598-8b4c-4f7f-89e1-e8644e934a4c", + "slug": "if-statements", + "title": "Use If Statements", + "blurb": "Use if-statements to check scoring boundaries for a dart toss.", + "authors": ["bethanyg"] + }, + { + "uuid": "f8f5533a-09d2-4b7b-9dec-90f268bfc03b", + "slug": "tuple-and-loop", + "title": "Use a Tuple & Loop through Scores", + "blurb": "Score the Dart toss by looping through a tuple of scores.", + "authors": ["bethanyg"] + }, + { + "uuid": "a324f99e-15bb-43e0-9181-c1652094bc4f", + "slug": "match-case", + "title": "Use Structural Pattern Matching ('Match-Case')", + "blurb": "Use a Match-Case (Structural Pattern Matching) to score the dart toss.)", + "authors": ["bethanyg"] + }, + { + "uuid": "966bd2dd-c4fd-430b-ad77-3a304dedd82e", + "slug": "dict-and-generator", + "title": "Use a Dictionary with a Generator Expression", + "blurb": "Use a generator expression looping over a scoring dictionary, getting the max score for the dart toss.", + "authors": ["bethanyg"] + }, + { + "uuid": "5b087f50-31c5-4b84-9116-baafd3a30ed6", + "slug": "booleans-as-ints", + "title": "Use Boolean Values as Integers", + "blurb": "Use True and False as integer values to calculate the score of the dart toss.", + "authors": ["bethanyg"] + }, + { + "uuid": "0b2dbcd3-f0ac-45f7-af75-3451751fd21f", + "slug": "dict-and-dict-get", + "title": "Use a Dictionary with dict.get", + "blurb": "Loop over a dictionary and retrieve score via dct.get.", + "authors": ["bethanyg"] + } + ] +} \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md new file mode 100644 index 0000000000..11919ad63c --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md @@ -0,0 +1,14 @@ +# Using a Dictionary and `dict.get()` + +```python +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 100: 1, + point <= 25: 5, + point <= 1: 10 + } + + return scores.get(True, 0) +``` + diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt b/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt new file mode 100644 index 0000000000..6d496f54d3 --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt @@ -0,0 +1,5 @@ +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = {point <= 100: 1, point <= 25: 5, point <= 1: 10} + + return scores.get(True, 0) \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-generator/content.md b/exercises/practice/darts/.approaches/dict-and-generator/content.md new file mode 100644 index 0000000000..d93894baf2 --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-generator/content.md @@ -0,0 +1,12 @@ +# Use a Dictionary and a Generator Expression + +```python +def score(x_coord, y_coord): + length = x_coord**2 + y_coord**2 + rules = {1.0: 10, 25.0: 5, 100.0: 1, 200: 0} + score = max(point for + distance, point in + rules.items() if length <= distance) + + return score +``` \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt b/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt new file mode 100644 index 0000000000..f6649cf3a9 --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt @@ -0,0 +1,8 @@ +def score(x_coord, y_coord): + length = x_coord**2 + y_coord**2 + rules = {1.0: 10, 25.0: 5, 100.0: 1, 200: 0} + score = max(point for + distance, point in + rules.items() if length <= distance) + + return score \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/if-statements/content.md b/exercises/practice/darts/.approaches/if-statements/content.md new file mode 100644 index 0000000000..68092f587b --- /dev/null +++ b/exercises/practice/darts/.approaches/if-statements/content.md @@ -0,0 +1,26 @@ +# Use `if-statements` + +```python +import math + +# Checks scores from the center --> edge. +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + + if distance <= 1: return 10 + if distance <= 5: return 5 + if distance <= 10: return 1 + + return 0 + +# ##OR## + +# Checks scores from the edge --> center +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + + if distance > 10: return 0 + if 5 < distance <= 10: return 1 + if 1 < distance <= 5: return 5 + if distance <= 1: return 10 +``` diff --git a/exercises/practice/darts/.approaches/if-statements/snippet.txt b/exercises/practice/darts/.approaches/if-statements/snippet.txt new file mode 100644 index 0000000000..18537416e2 --- /dev/null +++ b/exercises/practice/darts/.approaches/if-statements/snippet.txt @@ -0,0 +1,8 @@ +import math + +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + if distance <= 1: return 10 + if distance <= 5: return 5 + if distance <= 10: return 1 + return 0 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md index e69de29bb2..1884a03891 100644 --- a/exercises/practice/darts/.approaches/introduction.md +++ b/exercises/practice/darts/.approaches/introduction.md @@ -0,0 +1,180 @@ +# Introduction + + +There are multiple Pythonic ways to solve the Darts exercise. +Among them are: + +- Using if statements +- Using a tuple (or list or dict) and a for loop +- Using a dict (or tuple or list) and a `generator-expression` +- Using boolean values as ints +- Using a dict and dict.get() +- Using `match/case` (_Python 3.10+ only_) + + + +## General guidance + +The goal of the Darts exercise is to score a single toss in a Darts game. +The scoring areas are _concentric circles_, so boundary values need to be checked in order to properly score a toss. +The key is to determine how far from the center the dart lands (_by calculating sqrt(x**2 + y**2), or a variation_) and then determine what scoring ring it falls into. + + +**_Order matters_** - each bigger target circle contains all the smaller circles, so the most straightforward solution is to check the smallest circle first. +Otherwise, you must box your scoring by checking both a _lower bound_ and an _upper bound_. + + +Darts that fall on a _boundary_ are scored based on the area below the line (_closer to center_). + + +## Approach: Using `if` statements + + +```python +import math + +# Checks scores from the center --> edge. +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + + if distance <= 1: return 10 + if distance <= 5: return 5 + if distance <= 10: return 1 + + return 0 + + ###OR## + +# Checks scores from the edge --> center +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + + if distance > 10: return 0 + if 5 < distance <= 10: return 1 + if 1 < distance <= 5: return 5 + if distance <= 1: return 10 +``` + + +This approach uses [concept:python/conditionals]() to check the boundaries for each scoring ring, returning the corresponding score. +Because the `if-statements` are simple and readable, they're written on one line to shorten the function body. + +One variant checks from the center, moving outward. +Zero is returned if no other if check is true. + +The other variant checks from the edge, moving inward, and includes an explicit check for the zero case. +To avoid importing the `math` module, (x**2 +y**2) can be calculated instead, and the scoring rings can be adjusted to 1, 25, and 100. + +For more details, see the [if statements][approach-if-statements] approach. + + +## Approach: Using a `tuple` and a `loop` + +```python +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + rules = (1.0, 10), (25.0, 5), (100.0, 1), (200.0, 0) + + for distance, point in rules: + if length <= distance: + return point +``` + + +This approach loops through the _rules_ `tuple`, unpacking each `distance` and corresponding`score`. +If the calculated distance of the toss is less than or equal to a given distance, the point score is returned. +For more details, see the [tuple and loop][approach-tuple-and-loop] approach. + + +## Approach: Using a `dict` with a `generator-expression` + +```python +def score(x_coord, y_coord): + length = x_coord**2 + y_coord**2 + rules = {1.0: 10, 25.0: 5, 100.0: 1, 200: 0} + score = max(point for + distance, point in + rules.items() if length <= distance) + + return score +``` + +This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items], and writes the `loop` as a [`generator-expression`][generator-expression] inside `max()`. +For more information, see the [dict with generator-expression][approach-dict-with-generator-expression] approach. + + +## Approach: Using Boolean Values as Integers + +```python +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 +``` + + +This approach exploits Boolean values as integers. + +In Python, the boolean values `True` and `False` are _subclasses_ of `int` , and can be interpreted as `0` (False) and `1` (True) in a mathematical context. + +For more information, see the [boolean values as integers][approach-boolean-values-as-integers] approach. + + +## Approach: Using a `Dictionary` and `dict.get()` + +```python +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 100: 1, + point <= 25: 5, + point <= 1: 10 + } + + return scores.get(True, 0) +``` + +This approach uses a dictionary to hold the distance --> scoring mappings and `dict.get()` to retrieve the correct points value. +For more details, read the [`Dictionary and dict.get()`][approach-dict-and-dict-get] approach. + + +## Approach: Using `match/case` (structural pattern matching) + +```python +from math import hypot, ceil + + +def score(x, y): + match ceil(hypot(x, y)): + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 + +``` + + +This approach uses Python 3.10's [`structural pattern matching`][structural-pattern-matching], with return values on the same line as `case`. +A fallthrough case (`_`) is used if the dart throw is outside the outer circle of the target (_greater than 10_). +For more details, see the [structural pattern matching][approach-struct-pattern-matching] approach. + + +## Which approach to use? + +Many of these approaches are a matter of personal preference - there aren't significant memory or performance differences. +That being said, ___ is the fastest and __ the slowest for the test data. + +# TODO: add in the details here! + +To compare the performance and other tradeoffs of the approaches, take a look at the [Performance article][article-performance]. + + +[approach-boolean-values-as-integers]: https://exercism.org/tracks/python/exercises/darts/approaches/boolean-values-as-integers +[approach-dict-and-dict-get]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-and-dict-get +[approach-dict-with-generator-expression]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-with-gnerator-expresson +[approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements +[approach-struct-pattern-matching]: https://exercism.org/tracks/python/exercises/darts/approaches/struct-pattern-matching +[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop +[article-performance]:https://exercism.org/tracks/python/exercises/darts/articles/performance +[structural-pattern-matching]: https://peps.python.org/pep-0636/ +[dict-items]: https://docs.python.org/3/library/stdtypes.html#dict.items +[generator-expression]: https://dbader.org/blog/python-generator-expressions diff --git a/exercises/practice/darts/.approaches/match-case/content.md b/exercises/practice/darts/.approaches/match-case/content.md new file mode 100644 index 0000000000..f14dd506ca --- /dev/null +++ b/exercises/practice/darts/.approaches/match-case/content.md @@ -0,0 +1,13 @@ +# Use `match/case` (Structural Pattern Matching) + +```python +from math import hypot, ceil + + +def score(x, y): + match ceil(hypot(x, y)): + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 +``` diff --git a/exercises/practice/darts/.approaches/match-case/snippet.txt b/exercises/practice/darts/.approaches/match-case/snippet.txt new file mode 100644 index 0000000000..e66b5382b2 --- /dev/null +++ b/exercises/practice/darts/.approaches/match-case/snippet.txt @@ -0,0 +1,8 @@ +from math import hypot, ceil + +def score(x, y): + match ceil(hypot(x, y)): + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/content.md b/exercises/practice/darts/.approaches/tuple-and-loop/content.md new file mode 100644 index 0000000000..3f589e229e --- /dev/null +++ b/exercises/practice/darts/.approaches/tuple-and-loop/content.md @@ -0,0 +1,11 @@ +# Use a tuple and Loop + +```python +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + rules = (1.0, 10), (25.0, 5), (100.0, 1), (200.0, 0) + + for distance, point in rules: + if length <= distance: + return point +``` diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt b/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt new file mode 100644 index 0000000000..ad50500526 --- /dev/null +++ b/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt @@ -0,0 +1,7 @@ +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + rules = (1.0, 10), (25.0, 5), (100.0, 1), (200.0, 0) + + for distance, point in rules: + if length <= distance: + return point \ No newline at end of file From b0513bcef474ae95a9f0a9fe115d885f5f0358f0 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 5 Apr 2023 15:13:32 -0700 Subject: [PATCH 3/5] Removing .articles for now. --- exercises/practice/darts/.articles/config.json | 0 exercises/practice/darts/.articles/performance/content.md | 0 exercises/practice/darts/.articles/performance/snippet.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 exercises/practice/darts/.articles/config.json delete mode 100644 exercises/practice/darts/.articles/performance/content.md delete mode 100644 exercises/practice/darts/.articles/performance/snippet.md diff --git a/exercises/practice/darts/.articles/config.json b/exercises/practice/darts/.articles/config.json deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/exercises/practice/darts/.articles/performance/content.md b/exercises/practice/darts/.articles/performance/content.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/exercises/practice/darts/.articles/performance/snippet.md b/exercises/practice/darts/.articles/performance/snippet.md deleted file mode 100644 index e69de29bb2..0000000000 From d54b2d14ba6fcd5d2f8d3ebb95d54d78e97f81ca Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 29 Nov 2024 15:27:03 -0800 Subject: [PATCH 4/5] Wrapped up approaches content details and removed performance info. --- .../.approaches/booleans-as-ints/content.md | 33 +++++++- .../.approaches/dict-and-dict-get/content.md | 58 ++++++++++++++ .../.approaches/dict-and-generator/content.md | 73 +++++++++++++++-- .../.approaches/if-statements/content.md | 51 +++++++++++- .../darts/.approaches/introduction.md | 80 ++++++------------- .../darts/.approaches/match-case/content.md | 72 +++++++++++++++++ .../.approaches/tuple-and-loop/content.md | 56 +++++++++++-- 7 files changed, 348 insertions(+), 75 deletions(-) diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/content.md b/exercises/practice/darts/.approaches/booleans-as-ints/content.md index d54327e2ac..64e934e63e 100644 --- a/exercises/practice/darts/.approaches/booleans-as-ints/content.md +++ b/exercises/practice/darts/.approaches/booleans-as-ints/content.md @@ -1,7 +1,36 @@ # Using Boolean Values as Integers + +```python +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return (radius<=1)*5 + (radius<=25)*4 + (radius<=100)*1 +``` + + +In Python, the [Boolean values `True` and `False` are _subclasses_ of `int`][bools-as-ints] and can be interpreted as `0` (False) and `1` (True) in a mathematical context. +This approach leverages that interpretation by checking which areas the toss falls into and multiplying each Boolean `int` by a scoring multiple. +For example, a toss that lands on the 25 (_or 5 if using `math.sqrt(x**2 + y**2)`_) circle should have a score of 5: + +```python +>>> (False)*5 + (True)*4 + (True)*1 +5 +``` + + +This makes for very compact code and has the added boost of not requiring any `loops` or additional data structures. +However, it is considered bad form to rely on Boolean interpretation. +Instead, the Python documentation recommends an explicit conversion to `int`: + + ```python def score(x_coord, y_coord): radius = (x_coord**2 + y_coord**2) - return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 -``` \ No newline at end of file + return int(radius<=1)*5 + int(radius<=25)*4 + int(radius<=100)*1 +``` + +Beyond that recommendation, the terseness of this approach might be harder to reason about or decode — especially if a programmer is coming from a programming langauge that does not treat Boolean values as `ints`. +Despite the "radius" variable name, is also more difficult to relate the scoring "rings" of the Dartboard to the values being checked and calculated in the `return` statement. +If using this code in a larger program, it would be strongly recommended that a docstring be provided to explain the Dartboard rings, scoring rules, and the corresponding scores. + +[bools-as-ints]: https://docs.python.org/3/library/stdtypes.html#boolean-type-bool \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md index 11919ad63c..6fecb970de 100644 --- a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md @@ -1,5 +1,6 @@ # Using a Dictionary and `dict.get()` + ```python def score(x_coord, y_coord): point = (x_coord**2 + y_coord**2) @@ -12,3 +13,60 @@ def score(x_coord, y_coord): return scores.get(True, 0) ``` +At first glance, this approach looks similar to the [Booleans as Integers][approach-boolean-values-as-integers] approach, due to the Boolean evaluation used in the dictionary keys. +However, this approach is **not** interpreting Booleans as integers and is instead exploiting three key properties of [dictionaries][dicts]: + + +1. [Keys must be hashable][hashable-keys] — in other words, keys have to be _unique_. +2. Insertion order is preserved (_as of `Python 3.7`_), and evaluation/iteration happens in insertion order. +3. Duplicate keys _overwrite_ existing keys. + If the first key is `True` and the third key is `True`, the _value_ from the third key will overwrite the value from the first key. + +Finally, the `return` line uses [`dict.get()`][dict-get] to `return` a default value of 0 when a toss is outside the existing circle radii. +To see this in action, you can view this code on [Python Tutor][dict-get-python-tutor]. + + +Because of the listed dictionary qualities, **_order matters_**. +This approach depends on the outermost scoring circle containing all smaller circles and that +checks proceed from largest --> smallest circle. +Iterating in the opposite direction will not resolve to the correct score. +The following code variations do not pass the exercise tests: + + +```python + +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 1: 10, + point <= 25: 5, + point <= 100: 1, + } + + return scores.get(True, 0) + + #OR# + +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 25: 5, + point <= 1: 10, + point <= 100: 1, + } + + return scores.get(True, 0) + +``` + +While this approach is a _very clever_ use of dictionary properties, it is likely to be very hard to reason about for those who are not deeply knowledgeable. +Even those experienced in Python might take longer than usual to figure out what is happening in the code. +Extensibility could also be error-prone due to needing a strict order for the `dict` keys. + +This approach offers no space or speed advantages over using `if-statements` or other strategies, so is not recommended for use beyond a learning context. + +[approach-boolean-values-as-integers]: https://exercism.org/tracks/python/exercises/darts/approaches/boolean-values-as-integers +[dicts]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict +[dict-get]: https://docs.python.org/3/library/stdtypes.html#dict.get +[dict-get-python-tutor]: https://pythontutor.com/render.html#code=def%20score%28x_coord,%20y_coord%29%3A%0A%20%20%20%20point%20%3D%20%28x_coord**2%20%2B%20y_coord**2%29%0A%20%20%20%20scores%20%3D%20%7B%0A%20%20%20%20%20%20%20%20point%20%3C%3D%20100%3A%201,%0A%20%20%20%20%20%20%20%20point%20%3C%3D%2025%3A%205,%0A%20%20%20%20%20%20%20%20point%20%3C%3D%201%3A%2010%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20return%20scores.get%28True,%200%29%0A%20%20%20%20%0Aprint%28score%281,3%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false +[hashable-keys]: https://www.pythonmorsels.com/what-are-hashable-objects/#dictionary-keys-must-be-hashable diff --git a/exercises/practice/darts/.approaches/dict-and-generator/content.md b/exercises/practice/darts/.approaches/dict-and-generator/content.md index d93894baf2..576511e23e 100644 --- a/exercises/practice/darts/.approaches/dict-and-generator/content.md +++ b/exercises/practice/darts/.approaches/dict-and-generator/content.md @@ -2,11 +2,68 @@ ```python def score(x_coord, y_coord): - length = x_coord**2 + y_coord**2 - rules = {1.0: 10, 25.0: 5, 100.0: 1, 200: 0} - score = max(point for - distance, point in - rules.items() if length <= distance) - - return score -``` \ No newline at end of file + toss = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return max(point for distance, point in + rules.items() if toss <= distance) +``` + + +This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items] and writes the `loop` as a [`generator-expression`][generator-expression] inside `max()`. +In cases where the scoring circles overlap, `max()` will return the maximum score available for the toss. +The generator expression inside `max()` is the equivalent of using a `for-loop` and a variable to determine the max score: + + +```python +def score(x_coord, y_coord): + toss = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1} + max_score = 0 + + for distance, point in rules.items(): + if toss <= distance and point > max_score: + max_score = point + return max_score +``` + + +A `list` or `tuple` can also be used in place of `max()`, but then requires and index to return the max score: + +```python +def score(x_coord, y_coord): + toss = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return [point for distance, point in + rules.items() if toss <= distance][0] #<-- have to specify index 0. + +#OR# + +def score(x_coord, y_coord): + toss = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return tuple(point for distance, point in + rules.items() if toss <= distance)[0] +``` + + +This solution can even be reduced to a "one-liner". +However, this is not performant, and is difficult to read: + +```python +def score(x_coord, y_coord): + return max(point for distance, point in + {1: 10, 25: 5, 100: 1, 200: 0}.items() if + (x_coord**2 + y_coord**2) <= distance) +``` + +While all of these variations do pass the tests, they suffer from even more over-engineering/performance caution than the earlier tuple and loop approach. +Additionally, the dictionary will take much more space in memory than using a `tuple` of tuples to hold scoring values. +In some circumstances, these variations might also be harder to reason about for those not familiar with `generator-expressions` or `list comprehensions`. + + +[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop +[dict-items]: https://docs.python.org/3/library/stdtypes.html#dict.items +[generator-expression]: https://dbader.org/blog/python-generator-expressions diff --git a/exercises/practice/darts/.approaches/if-statements/content.md b/exercises/practice/darts/.approaches/if-statements/content.md index 68092f587b..40e2886ddb 100644 --- a/exercises/practice/darts/.approaches/if-statements/content.md +++ b/exercises/practice/darts/.approaches/if-statements/content.md @@ -1,5 +1,6 @@ # Use `if-statements` + ```python import math @@ -12,8 +13,35 @@ def score(x_coord, y_coord): if distance <= 10: return 1 return 0 +``` + +This approach uses [concept:python/conditionals]() to check the boundaries for each scoring ring, returning the corresponding score. +Calculating the euclidian distance is assigned to the variable "distance" to avoid having to re-calculate it for every if check. +Because the `if-statements` are simple and readable, they're written on one line to shorten the function body. +Zero is returned if no other check is true. + + +To avoid importing the `math` module (_for a very very slight speedup_), (x**2 +y**2) can be calculated instead, and the scoring rings can be adjusted to 1, 25, and 100: + + +```python +# Checks scores from the center --> edge. +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + + if distance <= 1: return 10 + if distance <= 25: return 5 + if distance <= 100: return 1 -# ##OR## + return 0 +``` + + +# Variation 1: Check from Edge to Center Using Upper and Lower Bounds + + +```python +import math # Checks scores from the edge --> center def score(x_coord, y_coord): @@ -22,5 +50,24 @@ def score(x_coord, y_coord): if distance > 10: return 0 if 5 < distance <= 10: return 1 if 1 < distance <= 5: return 5 - if distance <= 1: return 10 + + return 10 +``` + +This variant checks from the edge moving inward, checking both a lower and upper bound due to the overlapping scoring circles in this direction. + +Scores for any of these solutions can also be assigned to a variable to avoid multiple `returns`, but this isn't really necessary: + +```python +# Checks scores from the edge --> center +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + points = 10 + + if distance > 100: points = 0 + if 25 < distance <= 100: points = 1 + if 1 < distance <= 25: points = 5 + + return points ``` + diff --git a/exercises/practice/darts/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md index 1884a03891..41966db830 100644 --- a/exercises/practice/darts/.approaches/introduction.md +++ b/exercises/practice/darts/.approaches/introduction.md @@ -4,14 +4,14 @@ There are multiple Pythonic ways to solve the Darts exercise. Among them are: -- Using if statements -- Using a tuple (or list or dict) and a for loop -- Using a dict (or tuple or list) and a `generator-expression` -- Using boolean values as ints -- Using a dict and dict.get() +- Using `if-statements` +- Using a `tuple` (or `list` or `dict`) and a `for-loop` +- Using a `dict` (or `tuple` or `list`) and a `generator-expression` +- Using `boolean` values as `ints` +- Using a `dict` and `dict.get()` - Using `match/case` (_Python 3.10+ only_) - +
## General guidance @@ -24,7 +24,7 @@ The key is to determine how far from the center the dart lands (_by calculating Otherwise, you must box your scoring by checking both a _lower bound_ and an _upper bound_. -Darts that fall on a _boundary_ are scored based on the area below the line (_closer to center_). +Darts that fall on a _boundary_ are scored based on the area below the line (_closer to center_), so checking `<=` or `>=` is advised. ## Approach: Using `if` statements @@ -42,29 +42,10 @@ def score(x_coord, y_coord): if distance <= 10: return 1 return 0 - - ###OR## - -# Checks scores from the edge --> center -def score(x_coord, y_coord): - distance = math.sqrt(x_coord**2 + y_coord**2) - - if distance > 10: return 0 - if 5 < distance <= 10: return 1 - if 1 < distance <= 5: return 5 - if distance <= 1: return 10 ``` This approach uses [concept:python/conditionals]() to check the boundaries for each scoring ring, returning the corresponding score. -Because the `if-statements` are simple and readable, they're written on one line to shorten the function body. - -One variant checks from the center, moving outward. -Zero is returned if no other if check is true. - -The other variant checks from the edge, moving inward, and includes an explicit check for the zero case. -To avoid importing the `math` module, (x**2 +y**2) can be calculated instead, and the scoring rings can be adjusted to 1, 25, and 100. - For more details, see the [if statements][approach-if-statements] approach. @@ -72,17 +53,16 @@ For more details, see the [if statements][approach-if-statements] approach. ```python def score(x_coord, y_coord): - distance = x_coord**2 + y_coord**2 - rules = (1.0, 10), (25.0, 5), (100.0, 1), (200.0, 0) + toss = x_coord**2 + y_coord**2 + rules = (1, 10), (25, 5), (100, 1), (200, 0) - for distance, point in rules: - if length <= distance: - return point + for distance, points in rules: + if toss <= distance: + return points ``` -This approach loops through the _rules_ `tuple`, unpacking each `distance` and corresponding`score`. -If the calculated distance of the toss is less than or equal to a given distance, the point score is returned. +This approach uses a loop to iterate through the _rules_ `tuple`, unpacking each `distance` and corresponding`score`. For more details, see the [tuple and loop][approach-tuple-and-loop] approach. @@ -90,16 +70,14 @@ For more details, see the [tuple and loop][approach-tuple-and-loop] approach. ```python def score(x_coord, y_coord): - length = x_coord**2 + y_coord**2 - rules = {1.0: 10, 25.0: 5, 100.0: 1, 200: 0} - score = max(point for - distance, point in - rules.items() if length <= distance) - - return score + toss = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return max(point for distance, point in + rules.items() if toss <= distance) ``` -This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items], and writes the `loop` as a [`generator-expression`][generator-expression] inside `max()`. +This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items]. For more information, see the [dict with generator-expression][approach-dict-with-generator-expression] approach. @@ -112,10 +90,7 @@ def score(x_coord, y_coord): ``` -This approach exploits Boolean values as integers. - -In Python, the boolean values `True` and `False` are _subclasses_ of `int` , and can be interpreted as `0` (False) and `1` (True) in a mathematical context. - +This approach exploits the fact that Boolean values are an integer subtype in Python. For more information, see the [boolean values as integers][approach-boolean-values-as-integers] approach. @@ -149,24 +124,18 @@ def score(x, y): case 2 | 3 | 4 | 5: return 5 case 6 | 7 | 8 | 9 | 10: return 1 case _: return 0 - ``` -This approach uses Python 3.10's [`structural pattern matching`][structural-pattern-matching], with return values on the same line as `case`. +This approach uses `Python 3.10`'s structural pattern matching with `return` values on the same line as `case`. A fallthrough case (`_`) is used if the dart throw is outside the outer circle of the target (_greater than 10_). For more details, see the [structural pattern matching][approach-struct-pattern-matching] approach. ## Which approach to use? -Many of these approaches are a matter of personal preference - there aren't significant memory or performance differences. -That being said, ___ is the fastest and __ the slowest for the test data. - -# TODO: add in the details here! - -To compare the performance and other tradeoffs of the approaches, take a look at the [Performance article][article-performance]. - +Many of these approaches are a matter of personal preference - there are not significant memory or performance differences. +Although a strong argument could be made for simplicity and clarity — many listed solutions (_while interesting_) are harder to reason about or are over-engineered for the current scope of the exercise. [approach-boolean-values-as-integers]: https://exercism.org/tracks/python/exercises/darts/approaches/boolean-values-as-integers [approach-dict-and-dict-get]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-and-dict-get @@ -174,7 +143,4 @@ To compare the performance and other tradeoffs of the approaches, take a look at [approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements [approach-struct-pattern-matching]: https://exercism.org/tracks/python/exercises/darts/approaches/struct-pattern-matching [approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop -[article-performance]:https://exercism.org/tracks/python/exercises/darts/articles/performance -[structural-pattern-matching]: https://peps.python.org/pep-0636/ [dict-items]: https://docs.python.org/3/library/stdtypes.html#dict.items -[generator-expression]: https://dbader.org/blog/python-generator-expressions diff --git a/exercises/practice/darts/.approaches/match-case/content.md b/exercises/practice/darts/.approaches/match-case/content.md index f14dd506ca..eda0ed4928 100644 --- a/exercises/practice/darts/.approaches/match-case/content.md +++ b/exercises/practice/darts/.approaches/match-case/content.md @@ -1,9 +1,21 @@ # Use `match/case` (Structural Pattern Matching) + ```python from math import hypot, ceil +def score(x, y): + toss = ceil(hypot(x, y)) + + match toss: + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 + +#OR# + def score(x, y): match ceil(hypot(x, y)): case 0 | 1: return 10 @@ -11,3 +23,63 @@ def score(x, y): case 6 | 7 | 8 | 9 | 10: return 1 case _: return 0 ``` + +This approach uses `Python 3.10`'s [`structural pattern matching`][structural-pattern-matching] with `return` values on the same line as `case`. +Because the match is numeric, each case explicitly lists allowed values using the `|` (OR) operator. +A fallthrough case (`_`) is used if the dart throw is greater than 10 (_the outer circle radius of the target_). +This is equivalent to using `if-statements` to check toss values although some might argue it is clearer to read. +An `if-statement` equivalent would be: + +```python +from math import hypot, ceil + + +def score(x, y): + toss = ceil(hypot(x, y)) + + if toss in (0, 1): return 10 + if toss in (2, 3, 4, 5): return 5 + if toss in (6, 7, 8, 9, 10): return 1 + + return 0 +``` + +One can also use `<`, `>`, or `<=` and `>=` in structural pattern matching, although the syntax becomes almost identical to using them with `if-statements`, but more verbose: + + +```python +from math import hypot, ceil + + +def score(x, y): + toss = ceil(hypot(x, y)) + + match toss: + case toss if toss <= 1: return 10 + case toss if toss <= 5: return 5 + case toss if toss <= 10: return 1 + case _: return 0 +``` + + +Finally, one can use an [assignment expression][assignment-expression] or [walrus operator][walrus] to calculate the toss value rather than calculating and assigning a variable on a separate line. +This isn't necessary (_the first variations shows this clearly_) and might be harder to reason about/understand for some programmers: + + +```python +from math import hypot, ceil + +def score(x, y): + match toss := ceil(hypot(x, y)): + case toss if toss <= 1: return 10 + case toss if toss <=5: return 5 + case toss if toss <=10: return 1 + case _: return 0 +``` + +Using structural pattern matching for this exercise doesn't offer any clear performance advantages over the `if-statement`, but might be "cleaner", more "organized looking", or easier for others to scan/read. + + +[assignment-expression]: https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-assignment_expression +[structural-pattern-matching]: https://peps.python.org/pep-0636/ +[walrus]: https://peps.python.org/pep-0572/ diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/content.md b/exercises/practice/darts/.approaches/tuple-and-loop/content.md index 3f589e229e..5412f174fc 100644 --- a/exercises/practice/darts/.approaches/tuple-and-loop/content.md +++ b/exercises/practice/darts/.approaches/tuple-and-loop/content.md @@ -1,11 +1,55 @@ -# Use a tuple and Loop +# Use a tuple with a loop ```python def score(x_coord, y_coord): - distance = x_coord**2 + y_coord**2 - rules = (1.0, 10), (25.0, 5), (100.0, 1), (200.0, 0) + toss = x_coord**2 + y_coord**2 + rules = (1, 10), (25, 5), (100, 1), (200, 0) - for distance, point in rules: - if length <= distance: - return point + for distance, points in rules: + if toss <= distance: + return points ``` + +This approach uses a loop to iterate through the _rules_ `tuple`, unpacking each (`distance`, `points`) pair (_For a little more on unpacking, see [Tuple Unpacking Improves Python Code Readability][tuple-unpacking]_). +If the calculated distance of the toss is less than or equal to a given distance, the score for that region is returned. +A `list` of `lists`, a `list` of `tuples`, or a dictionary could be used here to the same effect: + +```python +def score(x_coord, y_coord): + toss = x_coord**2 + y_coord**2 + rules = [[1, 10], [25, 5], [100, 1]] + + for distance, points in rules: + if toss <= distance: + return points + + return 0 + +#OR# + +def score(x_coord, y_coord): + toss = x_coord**2 + y_coord**2 + rules = [(1, 10), (25, 5), (100, 1), (200, 0)] + + for distance, points in rules: + if toss <= distance: + return points + +#OR# + +def score(x_coord, y_coord): + toss = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1} + + for distance, points in rules.items(): + if toss <= distance: + return points + + return 0 +``` + +This approach would work nicely in a scenario where you expect to be adding more scoring "rings", since it is cleaner to edit the data structure than to add additional `if-statements` as you would have to in the [`if-statement` approach][approach-if-statements ]. +For the three rings as defined by the current exercise, it is a bit over-engineered to use a data structure + `loop`, and results in a slight (_**very** slight_) slowdown over using `if-statements`. + +[tuple-unpacking]: https://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/#Unpacking_in_a_for_loop +[approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements From f8dd8a991daa7789e9dd3260b1e762a43a0df603 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 30 Nov 2024 11:26:00 -0800 Subject: [PATCH 5/5] Cleaned up typos and changed toss to throw. --- .../.approaches/booleans-as-ints/content.md | 6 ++-- .../practice/darts/.approaches/config.json | 10 +++--- .../.approaches/dict-and-dict-get/content.md | 2 +- .../.approaches/dict-and-generator/content.md | 22 ++++++------ .../darts/.approaches/introduction.md | 12 +++---- .../darts/.approaches/match-case/content.md | 34 +++++++++---------- .../.approaches/tuple-and-loop/content.md | 18 +++++----- 7 files changed, 52 insertions(+), 52 deletions(-) diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/content.md b/exercises/practice/darts/.approaches/booleans-as-ints/content.md index 64e934e63e..d3c1541d2a 100644 --- a/exercises/practice/darts/.approaches/booleans-as-ints/content.md +++ b/exercises/practice/darts/.approaches/booleans-as-ints/content.md @@ -9,8 +9,8 @@ def score(x_coord, y_coord): In Python, the [Boolean values `True` and `False` are _subclasses_ of `int`][bools-as-ints] and can be interpreted as `0` (False) and `1` (True) in a mathematical context. -This approach leverages that interpretation by checking which areas the toss falls into and multiplying each Boolean `int` by a scoring multiple. -For example, a toss that lands on the 25 (_or 5 if using `math.sqrt(x**2 + y**2)`_) circle should have a score of 5: +This approach leverages that interpretation by checking which areas the throw falls into and multiplying each Boolean `int` by a scoring multiple. +For example, a throw that lands on the 25 (_or 5 if using `math.sqrt(x**2 + y**2)`_) circle should have a score of 5: ```python >>> (False)*5 + (True)*4 + (True)*1 @@ -30,7 +30,7 @@ def score(x_coord, y_coord): ``` Beyond that recommendation, the terseness of this approach might be harder to reason about or decode — especially if a programmer is coming from a programming langauge that does not treat Boolean values as `ints`. -Despite the "radius" variable name, is also more difficult to relate the scoring "rings" of the Dartboard to the values being checked and calculated in the `return` statement. +Despite the "radius" variable name, it is also more difficult to relate the scoring "rings" of the Dartboard to the values being checked and calculated in the `return` statement. If using this code in a larger program, it would be strongly recommended that a docstring be provided to explain the Dartboard rings, scoring rules, and the corresponding scores. [bools-as-ints]: https://docs.python.org/3/library/stdtypes.html#boolean-type-bool \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/config.json b/exercises/practice/darts/.approaches/config.json index aa93e2aebe..77f331bfce 100644 --- a/exercises/practice/darts/.approaches/config.json +++ b/exercises/practice/darts/.approaches/config.json @@ -8,35 +8,35 @@ "uuid": "7d78f598-8b4c-4f7f-89e1-e8644e934a4c", "slug": "if-statements", "title": "Use If Statements", - "blurb": "Use if-statements to check scoring boundaries for a dart toss.", + "blurb": "Use if-statements to check scoring boundaries for a dart throw.", "authors": ["bethanyg"] }, { "uuid": "f8f5533a-09d2-4b7b-9dec-90f268bfc03b", "slug": "tuple-and-loop", "title": "Use a Tuple & Loop through Scores", - "blurb": "Score the Dart toss by looping through a tuple of scores.", + "blurb": "Score the Dart throw by looping through a tuple of scores.", "authors": ["bethanyg"] }, { "uuid": "a324f99e-15bb-43e0-9181-c1652094bc4f", "slug": "match-case", "title": "Use Structural Pattern Matching ('Match-Case')", - "blurb": "Use a Match-Case (Structural Pattern Matching) to score the dart toss.)", + "blurb": "Use a Match-Case (Structural Pattern Matching) to score the dart throw.)", "authors": ["bethanyg"] }, { "uuid": "966bd2dd-c4fd-430b-ad77-3a304dedd82e", "slug": "dict-and-generator", "title": "Use a Dictionary with a Generator Expression", - "blurb": "Use a generator expression looping over a scoring dictionary, getting the max score for the dart toss.", + "blurb": "Use a generator expression looping over a scoring dictionary, getting the max score for the dart throw.", "authors": ["bethanyg"] }, { "uuid": "5b087f50-31c5-4b84-9116-baafd3a30ed6", "slug": "booleans-as-ints", "title": "Use Boolean Values as Integers", - "blurb": "Use True and False as integer values to calculate the score of the dart toss.", + "blurb": "Use True and False as integer values to calculate the score of the dart throw.", "authors": ["bethanyg"] }, { diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md index 6fecb970de..a3c5bc2ac5 100644 --- a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md @@ -22,7 +22,7 @@ However, this approach is **not** interpreting Booleans as integers and is inste 3. Duplicate keys _overwrite_ existing keys. If the first key is `True` and the third key is `True`, the _value_ from the third key will overwrite the value from the first key. -Finally, the `return` line uses [`dict.get()`][dict-get] to `return` a default value of 0 when a toss is outside the existing circle radii. +Finally, the `return` line uses [`dict.get()`][dict-get] to `return` a default value of 0 when a throw is outside the existing circle radii. To see this in action, you can view this code on [Python Tutor][dict-get-python-tutor]. diff --git a/exercises/practice/darts/.approaches/dict-and-generator/content.md b/exercises/practice/darts/.approaches/dict-and-generator/content.md index 576511e23e..30ffeac1eb 100644 --- a/exercises/practice/darts/.approaches/dict-and-generator/content.md +++ b/exercises/practice/darts/.approaches/dict-and-generator/content.md @@ -2,50 +2,50 @@ ```python def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = {1: 10, 25: 5, 100: 1, 200: 0} return max(point for distance, point in - rules.items() if toss <= distance) + rules.items() if throw <= distance) ``` This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items] and writes the `loop` as a [`generator-expression`][generator-expression] inside `max()`. -In cases where the scoring circles overlap, `max()` will return the maximum score available for the toss. +In cases where the scoring circles overlap, `max()` will return the maximum score available for the throw. The generator expression inside `max()` is the equivalent of using a `for-loop` and a variable to determine the max score: ```python def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = {1: 10, 25: 5, 100: 1} max_score = 0 for distance, point in rules.items(): - if toss <= distance and point > max_score: + if throw <= distance and point > max_score: max_score = point return max_score ``` -A `list` or `tuple` can also be used in place of `max()`, but then requires and index to return the max score: +A `list` or `tuple` can also be used in place of `max()`, but then requires an index to return the max score: ```python def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = {1: 10, 25: 5, 100: 1, 200: 0} return [point for distance, point in - rules.items() if toss <= distance][0] #<-- have to specify index 0. + rules.items() if throw <= distance][0] #<-- have to specify index 0. #OR# def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = {1: 10, 25: 5, 100: 1, 200: 0} return tuple(point for distance, point in - rules.items() if toss <= distance)[0] + rules.items() if throw <= distance)[0] ``` @@ -59,7 +59,7 @@ def score(x_coord, y_coord): (x_coord**2 + y_coord**2) <= distance) ``` -While all of these variations do pass the tests, they suffer from even more over-engineering/performance caution than the earlier tuple and loop approach. +While all of these variations do pass the tests, they suffer from even more over-engineering/performance caution than the earlier tuple and loop approach (_although for the data in this problem, the performance hit is slight_). Additionally, the dictionary will take much more space in memory than using a `tuple` of tuples to hold scoring values. In some circumstances, these variations might also be harder to reason about for those not familiar with `generator-expressions` or `list comprehensions`. diff --git a/exercises/practice/darts/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md index 41966db830..588c266184 100644 --- a/exercises/practice/darts/.approaches/introduction.md +++ b/exercises/practice/darts/.approaches/introduction.md @@ -15,8 +15,8 @@ Among them are: ## General guidance -The goal of the Darts exercise is to score a single toss in a Darts game. -The scoring areas are _concentric circles_, so boundary values need to be checked in order to properly score a toss. +The goal of the Darts exercise is to score a single throw in a Darts game. +The scoring areas are _concentric circles_, so boundary values need to be checked in order to properly score a throw. The key is to determine how far from the center the dart lands (_by calculating sqrt(x**2 + y**2), or a variation_) and then determine what scoring ring it falls into. @@ -53,11 +53,11 @@ For more details, see the [if statements][approach-if-statements] approach. ```python def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = (1, 10), (25, 5), (100, 1), (200, 0) for distance, points in rules: - if toss <= distance: + if throw <= distance: return points ``` @@ -70,11 +70,11 @@ For more details, see the [tuple and loop][approach-tuple-and-loop] approach. ```python def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = {1: 10, 25: 5, 100: 1, 200: 0} return max(point for distance, point in - rules.items() if toss <= distance) + rules.items() if throw <= distance) ``` This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items]. diff --git a/exercises/practice/darts/.approaches/match-case/content.md b/exercises/practice/darts/.approaches/match-case/content.md index eda0ed4928..04430a5dc5 100644 --- a/exercises/practice/darts/.approaches/match-case/content.md +++ b/exercises/practice/darts/.approaches/match-case/content.md @@ -6,9 +6,9 @@ from math import hypot, ceil def score(x, y): - toss = ceil(hypot(x, y)) + throw = ceil(hypot(x, y)) - match toss: + match throw: case 0 | 1: return 10 case 2 | 3 | 4 | 5: return 5 case 6 | 7 | 8 | 9 | 10: return 1 @@ -27,7 +27,7 @@ def score(x, y): This approach uses `Python 3.10`'s [`structural pattern matching`][structural-pattern-matching] with `return` values on the same line as `case`. Because the match is numeric, each case explicitly lists allowed values using the `|` (OR) operator. A fallthrough case (`_`) is used if the dart throw is greater than 10 (_the outer circle radius of the target_). -This is equivalent to using `if-statements` to check toss values although some might argue it is clearer to read. +This is equivalent to using `if-statements` to check throw values although some might argue it is clearer to read. An `if-statement` equivalent would be: ```python @@ -35,11 +35,11 @@ from math import hypot, ceil def score(x, y): - toss = ceil(hypot(x, y)) + throw = ceil(hypot(x, y)) - if toss in (0, 1): return 10 - if toss in (2, 3, 4, 5): return 5 - if toss in (6, 7, 8, 9, 10): return 1 + if throw in (0, 1): return 10 + if throw in (2, 3, 4, 5): return 5 + if throw in (6, 7, 8, 9, 10): return 1 return 0 ``` @@ -52,17 +52,17 @@ from math import hypot, ceil def score(x, y): - toss = ceil(hypot(x, y)) + throw = ceil(hypot(x, y)) - match toss: - case toss if toss <= 1: return 10 - case toss if toss <= 5: return 5 - case toss if toss <= 10: return 1 + match throw: + case throw if throw <= 1: return 10 + case throw if throw <= 5: return 5 + case throw if throw <= 10: return 1 case _: return 0 ``` -Finally, one can use an [assignment expression][assignment-expression] or [walrus operator][walrus] to calculate the toss value rather than calculating and assigning a variable on a separate line. +Finally, one can use an [assignment expression][assignment-expression] or [walrus operator][walrus] to calculate the throw value rather than calculating and assigning a variable on a separate line. This isn't necessary (_the first variations shows this clearly_) and might be harder to reason about/understand for some programmers: @@ -70,10 +70,10 @@ This isn't necessary (_the first variations shows this clearly_) and might be ha from math import hypot, ceil def score(x, y): - match toss := ceil(hypot(x, y)): - case toss if toss <= 1: return 10 - case toss if toss <=5: return 5 - case toss if toss <=10: return 1 + match throw := ceil(hypot(x, y)): + case throw if throw <= 1: return 10 + case throw if throw <=5: return 5 + case throw if throw <=10: return 1 case _: return 0 ``` diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/content.md b/exercises/practice/darts/.approaches/tuple-and-loop/content.md index 5412f174fc..042b9e88ae 100644 --- a/exercises/practice/darts/.approaches/tuple-and-loop/content.md +++ b/exercises/practice/darts/.approaches/tuple-and-loop/content.md @@ -2,25 +2,25 @@ ```python def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = (1, 10), (25, 5), (100, 1), (200, 0) for distance, points in rules: - if toss <= distance: + if throw <= distance: return points ``` This approach uses a loop to iterate through the _rules_ `tuple`, unpacking each (`distance`, `points`) pair (_For a little more on unpacking, see [Tuple Unpacking Improves Python Code Readability][tuple-unpacking]_). -If the calculated distance of the toss is less than or equal to a given distance, the score for that region is returned. +If the calculated distance of the throw is less than or equal to a given distance, the score for that region is returned. A `list` of `lists`, a `list` of `tuples`, or a dictionary could be used here to the same effect: ```python def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = [[1, 10], [25, 5], [100, 1]] for distance, points in rules: - if toss <= distance: + if throw <= distance: return points return 0 @@ -28,21 +28,21 @@ def score(x_coord, y_coord): #OR# def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = [(1, 10), (25, 5), (100, 1), (200, 0)] for distance, points in rules: - if toss <= distance: + if throw <= distance: return points #OR# def score(x_coord, y_coord): - toss = x_coord**2 + y_coord**2 + throw = x_coord**2 + y_coord**2 rules = {1: 10, 25: 5, 100: 1} for distance, points in rules.items(): - if toss <= distance: + if throw <= distance: return points return 0