Skip to content

Commit 906676a

Browse files
meatball133BethanyG
andcommitted
Apply suggestions from code review
Co-authored-by: BethanyG <[email protected]>
1 parent 12e692f commit 906676a

File tree

5 files changed

+107
-119
lines changed

5 files changed

+107
-119
lines changed

exercises/practice/scrabble-score/.approaches/dictionary/content.md

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,52 @@
22

33
```python
44
LETTER_SCORES = {
5-
'A': 1,
6-
'E': 1,
7-
'I': 1,
8-
'O': 1,
9-
'U': 1,
10-
'L': 1,
11-
'N': 1,
12-
'R': 1,
13-
'S': 1,
14-
'T': 1,
15-
'D': 2,
16-
'G': 2,
17-
'B': 3,
18-
'C': 3,
19-
'M': 3,
20-
'P': 3,
21-
'F': 4,
22-
'H': 4,
23-
'V': 4,
24-
'W': 4,
25-
'Y': 4,
26-
'K': 5,
27-
'J': 8,
28-
'X': 8,
29-
'Q': 10,
30-
'Z': 10
5+
'A': 1, 'E': 1, 'I': 1, 'O': 1, 'U': 1,
6+
'L': 1, 'N': 1, 'R': 1, 'S': 1, 'T': 1,
7+
'D': 2, 'G': 2, 'B': 3, 'C': 3, 'M': 3,
8+
'P': 3, 'F': 4, 'H': 4, 'V': 4, 'W': 4,
9+
'Y': 4, 'K': 5, 'J': 8, 'X': 8, 'Q': 10, 'Z': 10
3110
}
32-
3311
def score(word):
34-
return sum(LETTER_SCORES[letter.upper()] for letter in word)
12+
return sum(LETTER_SCORES[letter] for letter in word.upper())
3513
```
3614

37-
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.
38-
Then a function is defined that takes a word as an argument.
15+
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.
16+
Then the `score` function is defined, which takes a `<word>` as an argument.
17+
18+
The function returns the total score for the word using the built-in function [`sum`][sum].
19+
Sum is passed a [generator expression][generator-expression] that iterates over the letters in the word, looking up each score in LETTER_SCORES.
20+
The generator expression produces the score values on the fly.
21+
This means that it doesn't use memory to store all the values from LETTER_SCORES.
22+
Instead, each value is looked up as needed by `sum`.
23+
24+
Within the generator expression, the word is converted from lower to uppercase.
25+
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.
26+
This is almost exactly the same process as using a `list comprehension`.
27+
However, a `list comprehension` would look up the values and save them into a `list` in memory.
28+
`sum` would then "unpack" or iterate over the `list`.
29+
30+
A variation on this dictionary approach is to use a dictionary transposition.
31+
32+
```python
33+
LETTER_SCORES = {
34+
1: {'A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T'},
35+
2: {'D', 'G'},
36+
3: {'B', 'C', 'M', 'P'},
37+
4: {'F', 'H', 'V', 'W', 'Y'},
38+
5: {'K'},
39+
8: {'J', 'X'},
40+
10: {'Q', 'Z'}
41+
}
42+
43+
def score(word):
44+
return sum(next(score for score, letters in LETTER_SCORES.items() if character in letters) for character in word.upper())
3945

40-
The function returns the built in function [`sum`][sum] that takes a [generator expression][generator-expersion] that iterates over the letters in the word.
41-
What a generator expression does is that it generates the values on the fly.
42-
Meaning that it doesn't have to use a lot of memory since it uses the last value and generates the next value.
4346

44-
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.
47+
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.
48+
Therefore, arranging the dictionary by letter is both more efficient and easier to understand.
4549

46-
There is also a very similar approach that uses a dictionary transposition.
47-
Although that approach requires more computational calculation therefore is this approach more efficient.
4850

4951
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
50-
[generator-expersion]: https://peps.python.org/pep-0289/
52+
[generator-expression]: https://peps.python.org/pep-0289/
5153
[sum]: https://docs.python.org/3/library/functions.html#sum

exercises/practice/scrabble-score/.approaches/enum/content.md

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,40 @@ class Scrabble(IntEnum):
1313
Q = Z = 10
1414

1515
def score(word):
16-
return sum(Scrabble[character.upper()] for character in word)
17-
```
16+
return sum(Scrabble[character] for character in word.upper())
1817

19-
This approach uses an [`enum`][enum] to define the score of each letter.
20-
An `enum` or known as an **enumeration** is sets of named constant and is immutable.
21-
`enum` was added to python standard library (_also known as stdlib_) in python 3.4.
18+
This approach uses an [`Enum`][enum] to define the score of each letter.
19+
An [`Enum`][enum] (_also known as an **enumeration**_) is an object with named attributes assigned unique values.
20+
These attributes are referred to as the enumeration _members_.
21+
`Enum`s can be iterated over to return their members in definition order.
22+
Values can be accessed via index syntax using the member name (_similar to how a dictionary lookup works_) .
23+
`Enum`s are immutable, and their members function as constants.
24+
The `enum` module was added to python standard library (_also known as stdlib_) in Python 3.4.
2225

23-
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.
24-
Thereby acts like integers.
26+
This approach uses an [`IntEnum`][int-enum].
27+
An `IntEnum` is very similar to an `Enum`, but restricts assigned values to `int`s.
28+
This allows the `IntEnum` to act as a collection of integers.
29+
In fact, `IntEnum`s are considered subclasses of `int`s.
2530

26-
To use an `intEnum` you need to [import][import] it using: `from enum import IntEnum`.
27-
Then you can define the `enum` class.
31+
To use an `IntEnum` you need to first [import][import] it using: `from enum import IntEnum`.
32+
Then you can define your `IntEnum` subclass.
2833

29-
The `enum` class is defined by using the [`class`][classes] keyword.
30-
Then you need to specify the name of the class.
34+
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:
3135

32-
After that is the constant in the enum declared by giving the constant capital letters and the value is assigned by using the `=` operator.
33-
This approach works by giving all the letters as constants and then value of the constant is the score of the letter.
34-
After the `enum` is defined, the `score` function is defined.
36+
```python
37+
class ClassName(IntEnum):
38+
39+
Member names are declared as constants (ALL CAPS) and assigned values using the `=` operator.
40+
41+
This approach works by creating all the uppercase letters as members with their values being the score.
42+
After the `IntEnum` is defined, the `score` function is defined.
3543

36-
The `score` function takes a word as a parameter.
37-
And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] but with a slight modification.
38-
Which is that instead of looking up the value in a dictionary it looks it up in the `enum` class.
44+
The `score` function takes a word as an argument.
45+
The `score` function uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach], but with a slight modification.
46+
Instead of looking up the value in a _dictionary_, it looks up the `InEnum` class member value.
3947

4048
[classes]: https://docs.python.org/3/tutorial/classes.html
4149
[enum]: https://docs.python.org/3/library/enum.html
42-
[generator-expersion]: https://peps.python.org/pep-0289/
50+
[generator-expression]: https://peps.python.org/pep-0289/
4351
[int-enum]: https://docs.python.org/3/library/enum.html#enum.IntEnum
4452
[import]: https://docs.python.org/3/reference/import.html

exercises/practice/scrabble-score/.approaches/introduction.md

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,52 @@
11
# Introduction
22

33
There are various ways to solve `scrabble-score`.
4-
This approache document shows different strategies to solve this exercise.
4+
This approaches document shows different strategies to solve this exercise.
55

66
## General guidance
77

88
The goal of this exercise is to write a function that calculates the scrabble score for a given word.
9-
The problem is that the scrabble score is calculated by the sum of the scores of each letter in the word.
9+
The challenge is that the scrabble score is calculated by summing the scores of individual letters in a word.
10+
The student needs to find an efficient and easily accessed way to store individual letter scores for lookup when processing different words.
1011

1112
## Approach: Using a single dictionary
1213

13-
Using a single dictionary is an approach, it is simple and fast.
14-
It is also very pythonic.
14+
Using a single dictionary for letter lookup is simple and fast.
15+
It is also very pythonic, and could be considered the canonical approach to this exercise.
1516

1617
```python
1718
LETTER_SCORES = {
18-
'A': 1,
19-
'E': 1,
20-
'I': 1,
21-
'O': 1,
22-
'U': 1,
23-
'L': 1,
24-
'N': 1,
25-
'R': 1,
26-
'S': 1,
27-
'T': 1,
28-
'D': 2,
29-
'G': 2,
30-
'B': 3,
31-
'C': 3,
32-
'M': 3,
33-
'P': 3,
34-
'F': 4,
35-
'H': 4,
36-
'V': 4,
37-
'W': 4,
38-
'Y': 4,
39-
'K': 5,
40-
'J': 8,
41-
'X': 8,
42-
'Q': 10,
43-
'Z': 10
19+
'A': 1, 'E': 1, 'I': 1, 'O': 1, 'U': 1,
20+
'L': 1, 'N': 1, 'R': 1, 'S': 1, 'T': 1,
21+
'D': 2, 'G': 2, 'B': 3, 'C': 3, 'M': 3,
22+
'P': 3, 'F': 4, 'H': 4, 'V': 4, 'W': 4,
23+
'Y': 4, 'K': 5, 'J': 8, 'X': 8, 'Q': 10, 'Z': 10
4424
}
4525

4626
def score(word):
47-
return sum(LETTER_SCORES[letter.upper()] for letter in word)
48-
```
27+
return sum(LETTER_SCORES[letter] for letter in word.upper())
4928

5029
For more information, check the [Dictionary Approach][dictionary-approach].
5130

5231
## Approach: Using two sequences
5332

54-
Using two sequences is an approach, it removes the need of using a nested data structure or a dictonary.
55-
Although the reason you might not want to do this is that it is hard to read.
33+
Using two sequences removes the need to use a nested data structure or a dictionary.
34+
Although you might not want to use this approach because it is hard to read and maintain.
5635

5736
```python
5837
KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
5938
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
6039

6140
def score(word):
62-
return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
41+
return sum(SCORES[KEYS.index(letter] for letter in word.upper())
6342
```
6443

6544
For more information, check the [Two Sequences Approach][two-sequences-approach].
6645

6746
## Approach: Enum
6847

69-
Using an `enum` is an approach, it is short and easy to read.
70-
Although it is more complicated since it uses a oop (object oriented programmering) elements.
48+
Using an `Enum` is is short and easy to read.
49+
Although creating an `Enum` can be more complicated since it uses OOP (object oriented programming).
7150

7251
```python
7352
from enum import IntEnum
@@ -82,15 +61,16 @@ class Scrabble(IntEnum):
8261
Q = Z = 10
8362

8463
def score(word):
85-
return sum(Scrabble[letter.upper()] for letter in word)
64+
return sum(Scrabble[letter] for letter in word.upper())
8665
```
8766

8867
For more information, check the [Enum Approach][enum-approach].
8968

9069
## Approach: Using a nested tuple
9170

92-
Tuples in python is more memory efficent than using a dictonary in python.
93-
Although this solution since it is iterating over a tuple for every letter so is it slower.
71+
Using a tuple in Python is generally more memory efficient than using a dictionary.
72+
However, this solution requires iterating over the entire `tuple` for every letter in order to score a full word.
73+
This makes the solution slower than the dictionary approach.
9474

9575
```python
9676
LETTERS_OF_SCORE = (
@@ -104,8 +84,8 @@ LETTERS_OF_SCORE = (
10484
)
10585

10686
def score(word):
107-
return sum(for character in word for letters, score in LETTERS_OF_SCORE if character in letters)
108-
```
87+
return sum(score for character in word.upper() for
88+
letters, score in LETTERS_OF_SCORE if character in letters)
10989

11090
For more information, check the [Nested Tuple Approach][nested-tuple-approach].
11191

exercises/practice/scrabble-score/.approaches/nested-tuple/content.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ LETTERS_OF_SCORE = (
1212
)
1313

1414
def score(word):
15-
return sum(score for character in word for letters, score in LETTERS_OF_SCORE if character.upper() in letters)
16-
```
15+
return sum(score for character in word.upper() for
16+
letters, score in LETTERS_OF_SCORE if character in letters)
1717

18-
The code starts with initializing a constant with a [tuple][tuple] of tuples (_also known as a nested tuple_).
19-
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.
18+
The code starts with defining a constant, LETTERS_OF_SCORE as a [`tuple`][tuple] of tuples (_also known as a nested tuple_).
19+
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.
2020

21-
Then a function is defined that takes a word as an argument.
22-
The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach] but has some slight modifcations.
21+
Next, the `score` function is defined, taking a word as an argument.
22+
The `score` function uses a [generator expression][generator-expression] similar to the [dictionary approach][dictionary-approach] with some slight modifications.
2323

24-
The difference is that this one uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
25-
We first iterate over the characters in the word and then iterate over the tuples.
26-
Which means that for each letter are we iterating over all of the tuples.
27-
There the tuple is unpacked into the letters and the score.
24+
This particular approach uses a _nested_ [for loop][for-loop] to iterate over the letters and the tuples.
25+
We first iterate over the characters in the word and then the tuples.
26+
Which means that for **_each letter_** we iterate over **all** of the tuples.
27+
Each iteration, the tuple is unpacked into the letters and their corresponding score.
2828
You can read more about unpacking in the [concept:python/unpacking-and-multiple-assignment]().
2929

30-
Then we check if the character is in the letters and if it is we return the score.
30+
Then the code checks if the character is in the unpacked letters and if it is we return its score.
3131

3232
[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
33-
[generator-expersion]: https://peps.python.org/pep-0289/
33+
[generator-expression]: https://peps.python.org/pep-0289/
3434
[for-loop]: https://realpython.com/python-for-loop/

exercises/practice/scrabble-score/.approaches/two-sequences/content.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,17 @@ KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
55
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 + [10] * 2
66

77
def score(word):
8-
return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
9-
```
8+
return sum(SCORES[KEYS.index(letter)] for letter in word.upper())
109

11-
This approach uses a string and a [list][list], both of these data types belongs to the parent data type [sequences][sequence].
12-
The code starts with defining a string constant with letters.
13-
Then another constant is definded which is a list with corresponding score for the same index as the string.
10+
This approach uses a string and a [list][list], both of which are [sequence][sequence] types.
11+
The code begins by defining a string constant with letters.
12+
Then another constant is defined as a list with the corresponding letter score at the same index as the letter in the string.
1413

15-
The `score` function takes a word as a parameter.
16-
And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] with some slight modifications.
14+
The `score` function takes a word as an argument.
15+
And uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach] with some slight modifications.
1716

18-
The difference is that instead of using a [dictionary][dictionary] and looking up the score inside of a dictonary.
19-
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.
20-
Then takes that value and return that to the generator expression.
17+
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.
18+
These values are then added up by `sum`.
2119

2220
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
2321
[dictionary-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/dictionary

0 commit comments

Comments
 (0)