From 1a164c78851503fa323727ef44346458fe29e3ae Mon Sep 17 00:00:00 2001 From: Sam Chukwuzube <68931805+princemuel@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:21:18 +0100 Subject: [PATCH 1/6] perf: optimize is_pangram by fixing unnecessary list creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: Current implementation creates an intermediate list from a set, which is redundant and inefficient. **Before:** ```python return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) == 26 ``` **After:** ```python return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26 ``` Why this is better: - Eliminates double iteration: Old version iterates once for set(), again for list comprehension - Removes unnecessary list creation: No need to convert set → list just to count - Better memory usage: Generator expression feeds directly into set constructor - Same time complexity but more efficient constant factors The old approach first deduplicates all the characters, then filters alphabetic ones. The new approach filters while building the set, which is the natural order of operations for this problem. --- exercises/practice/pangram/.approaches/introduction.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/exercises/practice/pangram/.approaches/introduction.md b/exercises/practice/pangram/.approaches/introduction.md index cf5538c0158..247348feae3 100644 --- a/exercises/practice/pangram/.approaches/introduction.md +++ b/exercises/practice/pangram/.approaches/introduction.md @@ -42,9 +42,7 @@ For more information, check the [`set` with `issubset()` approach][approach-set- ```python def is_pangram(sentence): - return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \ - == 26 - + return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26 ``` For more information, check the [`set` with `len()` approach][approach-set-len]. From b4259d9de0ce315c2628b130806893daab7c5dd6 Mon Sep 17 00:00:00 2001 From: Prince Muel <68931805+princemuel@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:11:39 +0100 Subject: [PATCH 2/6] feat(practice/pangram): update code snippets in approaches for `set with len()` Fixes: Cc: Reviewed-by: --- exercises/practice/pangram/.approaches/set-len/content.md | 6 ++---- exercises/practice/pangram/.approaches/set-len/snippet.txt | 3 +-- .../pangram/.articles/performance/code/Benchmark.py | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/exercises/practice/pangram/.approaches/set-len/content.md b/exercises/practice/pangram/.approaches/set-len/content.md index b647a01d495..bbd79d6b54b 100644 --- a/exercises/practice/pangram/.approaches/set-len/content.md +++ b/exercises/practice/pangram/.approaches/set-len/content.md @@ -2,16 +2,14 @@ ```python def is_pangram(sentence): - return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \ - == 26 - + return len(set(ltr for ltr in 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 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`. + 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 diff --git a/exercises/practice/pangram/.approaches/set-len/snippet.txt b/exercises/practice/pangram/.approaches/set-len/snippet.txt index 9a6a6d537bf..16c2ce6806a 100644 --- a/exercises/practice/pangram/.approaches/set-len/snippet.txt +++ b/exercises/practice/pangram/.approaches/set-len/snippet.txt @@ -1,3 +1,2 @@ def is_pangram(sentence): - return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \ - == 26 + return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26 diff --git a/exercises/practice/pangram/.articles/performance/code/Benchmark.py b/exercises/practice/pangram/.articles/performance/code/Benchmark.py index 1b423744479..6abefe1beed 100644 --- a/exercises/practice/pangram/.articles/performance/code/Benchmark.py +++ b/exercises/practice/pangram/.articles/performance/code/Benchmark.py @@ -28,7 +28,7 @@ def is_pangram(sentence): 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 + return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26 """, number=loops) / loops From cad5281ffe902dba2018e326a091e10bc464c21c Mon Sep 17 00:00:00 2001 From: Prince Muel <68931805+princemuel@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:52:21 +0100 Subject: [PATCH 3/6] docs(pangram): improve approach's content and links --- .../practice/pangram/.approaches/set-len/content.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/exercises/practice/pangram/.approaches/set-len/content.md b/exercises/practice/pangram/.approaches/set-len/content.md index bbd79d6b54b..79d8c58b8ef 100644 --- a/exercises/practice/pangram/.approaches/set-len/content.md +++ b/exercises/practice/pangram/.approaches/set-len/content.md @@ -6,14 +6,13 @@ 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 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`. +- The characters are filtered using a [set comprehension][set-comprehension] with an `if` [`isalpha()`][isalpha] statement, so that only alphabetic characters make it into the set. +- The function returns whether the [`len()`][len] of the [`set`][set] is `26`. + If the number of unique ASCII letters in the `set` is equal to the `26` letters in the ASCII alphabet, then the function will return `True`. +- This approach is efficient because it uses a set to eliminate duplicates and directly checks the length, which is a constant time operation. [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 +[set-comprehension]: https://realpython.com/python-set-comprehension/#introducing-set-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 136c8475d9388da02068cf66ab01ecf866c6be04 Mon Sep 17 00:00:00 2001 From: Prince Muel <68931805+princemuel@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:55:53 +0100 Subject: [PATCH 4/6] docs(pangram): improve approach's content and links --- exercises/practice/pangram/.approaches/set-len/content.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/practice/pangram/.approaches/set-len/content.md b/exercises/practice/pangram/.approaches/set-len/content.md index 79d8c58b8ef..6c0347d5c06 100644 --- a/exercises/practice/pangram/.approaches/set-len/content.md +++ b/exercises/practice/pangram/.approaches/set-len/content.md @@ -8,7 +8,7 @@ def is_pangram(sentence): - This approach first makes a [set][set] from the [`lower`][lower]cased characters of the `sentence`. - The characters are filtered using a [set comprehension][set-comprehension] with an `if` [`isalpha()`][isalpha] statement, so that only alphabetic characters make it into the set. - The function returns whether the [`len()`][len] of the [`set`][set] is `26`. - If the number of unique ASCII letters in the `set` is equal to the `26` letters in the ASCII alphabet, then the function will return `True`. + If the number of unique [ASCII][ascii] (American Standard Code for Information Interchange) letters in the `set` is equal to the `26` letters in the [ASCII][ascii] alphabet, then the function will return `True`. - This approach is efficient because it uses a set to eliminate duplicates and directly checks the length, which is a constant time operation. [lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower @@ -16,3 +16,4 @@ def is_pangram(sentence): [set-comprehension]: https://realpython.com/python-set-comprehension/#introducing-set-comprehensions [isalpha]: https://docs.python.org/3/library/stdtypes.html?highlight=isalpha#str.isalpha [len]: https://docs.python.org/3/library/functions.html?#len +[ascii]: https://en.wikipedia.org/wiki/ASCII From 8298af4bc124a46081b463da5907d49d97a9a79f Mon Sep 17 00:00:00 2001 From: Prince Muel <68931805+princemuel@users.noreply.github.com> Date: Sun, 3 Aug 2025 15:00:53 +0100 Subject: [PATCH 5/6] docs(pangram): improve performance content and links --- .../pangram/.articles/performance/content.md | 16 ++++++++-------- .../pangram/.articles/performance/snippet.md | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/exercises/practice/pangram/.articles/performance/content.md b/exercises/practice/pangram/.articles/performance/content.md index c5546e948ba..32f7fe24d5e 100644 --- a/exercises/practice/pangram/.articles/performance/content.md +++ b/exercises/practice/pangram/.articles/performance/content.md @@ -15,18 +15,18 @@ For our performance investigation, we'll also include a fourth approach that [us 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 +all: 1.8692991019000146e-05 +all: 1.686682232399926e-05 // with sentence.casefold() +set: 2.5181135439997888e-06 +len: 5.848111433000668e-06 +bit: 1.2118699087000096e-05 ``` - 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`. +- The `all()` approach is significantly slower than either `set` approach (approximately 6-8x slower). + Using `casefold()` versus `lower()` showed variable performance, with each being faster in different runs. - 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 diff --git a/exercises/practice/pangram/.articles/performance/snippet.md b/exercises/practice/pangram/.articles/performance/snippet.md index 0509fbee539..8542eba9fc4 100644 --- a/exercises/practice/pangram/.articles/performance/snippet.md +++ b/exercises/practice/pangram/.articles/performance/snippet.md @@ -1,7 +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 +all: 1.8692991019000146e-05 +all: 1.686682232399926e-05 // with sentence.casefold() +set: 2.5181135439997888e-06 +len: 5.848111433000668e-06 +bit: 1.2118699087000096e-05 ``` From 01616aec8a4a56fc8f3fcdb1f30fe2824cb95ef4 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 6 Aug 2025 12:40:27 -0700 Subject: [PATCH 6/6] Added princemuel as contributor --- exercises/practice/pangram/.approaches/config.json | 6 ++++-- exercises/practice/pangram/.articles/config.json | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/exercises/practice/pangram/.approaches/config.json b/exercises/practice/pangram/.approaches/config.json index 550a3b5e11a..19a3b9f9735 100644 --- a/exercises/practice/pangram/.approaches/config.json +++ b/exercises/practice/pangram/.approaches/config.json @@ -1,6 +1,7 @@ { "introduction": { - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["princemuel"] }, "approaches": [ { @@ -22,7 +23,8 @@ "slug": "set-len", "title": "set with len", "blurb": "Use set with len.", - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["princemuel"] }, { "uuid": "0a6d1bbf-6d60-4489-b8d9-b8375894628b", diff --git a/exercises/practice/pangram/.articles/config.json b/exercises/practice/pangram/.articles/config.json index b7de79a678c..ec053d3d0f3 100644 --- a/exercises/practice/pangram/.articles/config.json +++ b/exercises/practice/pangram/.articles/config.json @@ -5,7 +5,8 @@ "slug": "performance", "title": "Performance deep dive", "blurb": "Deep dive to find out the most performant approach to determining a pangram.", - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["princemuel"] } ] }