Skip to content

Commit 0f0c01e

Browse files
princemuelBethanyG
andauthored
[Pangram]: fix redundant double iteration in dig deeper solution (exercism#3948)
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 Reviewed-by: bethanyg * docs(pangram): improve approach's content and links * docs(pangram): improve approach's content and links * docs(pangram): improve performance content and links * Added princemuel as contributor --------- Co-authored-by: BethanyG <[email protected]>
1 parent a51a10e commit 0f0c01e

File tree

8 files changed

+29
-31
lines changed

8 files changed

+29
-31
lines changed

exercises/practice/pangram/.approaches/config.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"introduction": {
3-
"authors": ["bobahop"]
3+
"authors": ["bobahop"],
4+
"contributors": ["princemuel"]
45
},
56
"approaches": [
67
{
@@ -22,7 +23,8 @@
2223
"slug": "set-len",
2324
"title": "set with len",
2425
"blurb": "Use set with len.",
25-
"authors": ["bobahop"]
26+
"authors": ["bobahop"],
27+
"contributors": ["princemuel"]
2628
},
2729
{
2830
"uuid": "0a6d1bbf-6d60-4489-b8d9-b8375894628b",

exercises/practice/pangram/.approaches/introduction.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ For more information, check the [`set` with `issubset()` approach][approach-set-
4242

4343
```python
4444
def is_pangram(sentence):
45-
return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \
46-
== 26
47-
45+
return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26
4846
```
4947

5048
For more information, check the [`set` with `len()` approach][approach-set-len].

exercises/practice/pangram/.approaches/set-len/content.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,18 @@
22

33
```python
44
def is_pangram(sentence):
5-
return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \
6-
== 26
7-
5+
return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26
86
```
97

108
- This approach first makes a [set][set] from the [`lower`][lower]cased characters of the `sentence`.
11-
- The characters in the `set`are then iterated in a [list comprehension][list-comprehension].
12-
- The characters are filtered by an `if` [`isalpha()`][isalpha] statement, so that only alphabetic characters make it into the list.
13-
- The function returns whether the [`len()`][len] of the [`list`][list] is `26`.
14-
If the number of unique letters in the `set` is equal to the `26` letters in the alphabet, then the function will return `True`.
9+
- 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.
10+
- The function returns whether the [`len()`][len] of the [`set`][set] is `26`.
11+
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`.
12+
- This approach is efficient because it uses a set to eliminate duplicates and directly checks the length, which is a constant time operation.
1513

1614
[lower]: https://docs.python.org/3/library/stdtypes.html?#str.lower
1715
[set]: https://docs.python.org/3/library/stdtypes.html?#set
18-
[list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
16+
[set-comprehension]: https://realpython.com/python-set-comprehension/#introducing-set-comprehensions
1917
[isalpha]: https://docs.python.org/3/library/stdtypes.html?highlight=isalpha#str.isalpha
2018
[len]: https://docs.python.org/3/library/functions.html?#len
21-
[list]: https://docs.python.org/3/library/stdtypes.html?#list
19+
[ascii]: https://en.wikipedia.org/wiki/ASCII
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
def is_pangram(sentence):
2-
return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) \
3-
== 26
2+
return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26

exercises/practice/pangram/.articles/config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"slug": "performance",
66
"title": "Performance deep dive",
77
"blurb": "Deep dive to find out the most performant approach to determining a pangram.",
8-
"authors": ["bobahop"]
8+
"authors": ["bobahop"],
9+
"contributors": ["princemuel"]
910
}
1011
]
1112
}

exercises/practice/pangram/.articles/performance/code/Benchmark.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def is_pangram(sentence):
2828
val = timeit.timeit("""is_pangram("Victor jagt zwölf_(12) Boxkämpfer quer über den großen Sylter Deich.")""",
2929
"""
3030
def is_pangram(sentence):
31-
return len([ltr for ltr in set(sentence.lower()) if ltr.isalpha()]) == 26
31+
return len(set(ltr for ltr in sentence.lower() if ltr.isalpha())) == 26
3232
3333
""", number=loops) / loops
3434

exercises/practice/pangram/.articles/performance/content.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ For our performance investigation, we'll also include a fourth approach that [us
1515
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
1616

1717
```
18-
all: 1.505466179997893e-05
19-
all: 1.6063886400021147e-05 // with sentence.casefold()
20-
set: 1.950172399985604e-06
21-
len: 3.7158977999933994e-06
22-
bit: 8.75982620002469e-06
18+
all: 1.8692991019000146e-05
19+
all: 1.686682232399926e-05 // with sentence.casefold()
20+
set: 2.5181135439997888e-06
21+
len: 5.848111433000668e-06
22+
bit: 1.2118699087000096e-05
2323
```
2424

2525
- The `set` `len()` approach is not as fast as the `set` `issubset()` approach.
26-
- The `all()` approach is slower than either `set` approach.
27-
Using `casefold` was slower than using `lower`.
26+
- The `all()` approach is significantly slower than either `set` approach (approximately 6-8x slower).
27+
Using `casefold()` versus `lower()` showed variable performance, with each being faster in different runs.
2828
- Although the bit field approach may be faster in other languages, it is significantly slower in Python.
29-
It is faster than the `all()` approach, but much slower than either `set` approach.
29+
It is faster than the `all()` approach, but much slower than either `set` approach.
3030

3131
[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/pangram/.articles/performance/code/Benchmark.py
3232
[timeit]: https://docs.python.org/3/library/timeit.html
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
```
2-
all: 1.505466179997893e-05
3-
all: 1.6063886400021147e-05 // with sentence.casefold()
4-
set: 1.950172399985604e-06
5-
len: 3.7158977999933994e-06
6-
bit: 8.75982620002469e-06
2+
all: 1.8692991019000146e-05
3+
all: 1.686682232399926e-05 // with sentence.casefold()
4+
set: 2.5181135439997888e-06
5+
len: 5.848111433000668e-06
6+
bit: 1.2118699087000096e-05
77
```

0 commit comments

Comments
 (0)