patternpythonMinor
Project Euler #22
Viewed 0 times
projecteulerstackoverflow
Problem
I gave problem #22 on Project Euler a try in Python3.
Other than suggested in the instructions, I used
The code below does produce the correct result but takes no measures to catch exceptions if something breaks, e.g. when the download is not successful.
I managed to split a string, in which names in double quotes are separated by a comma and turn it to a list, but I'm wondering if there isn't any better or more pythonic way.
As far as the summation of
```
#!/usr/bin/env python3
# -- coding: utf-8 --
'''Project Euler - Problem 22 - Name scores
URL: https://projecteuler.net/problem=22
Resource (names.txt): https://projecteuler.net/project/resources/p022_names.txt
Instructions: Using names.txt (right click and 'Save Link/Target As...'),
a 46K text file containing over five-thousand first names, begin by
sorting it into alphabetical order. Then working out the alphabetical value
for each name, multiply this value by its alphabetical position in the list
to obtain a name score.
For example, when the list is sorted into alphabetical order, COLIN, which is
worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list.
So, COLIN would obtain a score of 938 × 53 = 49714.
Assignment: What is the total of all the name scores in the file?
'''
import sys
import requests
URL = 'https://projecteuler.net/project/resources/p022_names.txt'
def fetch_names(url):
'''Get a text file with names and return a sorted list
Parameters
----------
url : str
url of a text file at Project Euler.
Returns
-------
alphabetically sorted list of first names
'''
r = requests.get(url)
# r.text is a string with names
# the names are in capital letters, enclosed in double quotes
# and separated by commas
names
Other than suggested in the instructions, I used
requests to fetch the file to work on.The code below does produce the correct result but takes no measures to catch exceptions if something breaks, e.g. when the download is not successful.
I managed to split a string, in which names in double quotes are separated by a comma and turn it to a list, but I'm wondering if there isn't any better or more pythonic way.
As far as the summation of
int in a list is concerned, I'm aware that reduce from functools might be an alternative, but for my first contribution here, I decided to keep it (more) simple.```
#!/usr/bin/env python3
# -- coding: utf-8 --
'''Project Euler - Problem 22 - Name scores
URL: https://projecteuler.net/problem=22
Resource (names.txt): https://projecteuler.net/project/resources/p022_names.txt
Instructions: Using names.txt (right click and 'Save Link/Target As...'),
a 46K text file containing over five-thousand first names, begin by
sorting it into alphabetical order. Then working out the alphabetical value
for each name, multiply this value by its alphabetical position in the list
to obtain a name score.
For example, when the list is sorted into alphabetical order, COLIN, which is
worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list.
So, COLIN would obtain a score of 938 × 53 = 49714.
Assignment: What is the total of all the name scores in the file?
'''
import sys
import requests
URL = 'https://projecteuler.net/project/resources/p022_names.txt'
def fetch_names(url):
'''Get a text file with names and return a sorted list
Parameters
----------
url : str
url of a text file at Project Euler.
Returns
-------
alphabetically sorted list of first names
'''
r = requests.get(url)
# r.text is a string with names
# the names are in capital letters, enclosed in double quotes
# and separated by commas
names
Solution
Code looks pretty good. Nicely commented, well-structured, seems to do what it’s supposed to do. Just a few small suggestions from me:
-
Don’t use magic numbers. In this line, it’s not immediately obvious what 64 means:
If I think a little, and find that
-
Cache the results from ord()? You’ll be making a call to
and then just look up scores in this dictionary. It probably won’t make much of a difference for this problem, but it’s something to bear in mind.
-
Use generator comprehensions unless you need a list. A generator comprehension is like a list comprehension, but instead of precomputing all the elements upfront and storing them in a list, it computes-and-yields one element at a time. That can have big savings in memory.
Both of your list comprehensions get passed straight to
The change is minimal – just replace the square brackets of a list comprehension with parentheses instead, i.e.
-
Don’t use magic numbers. In this line, it’s not immediately obvious what 64 means:
ch_scores = [ord(ch) - 64 for ch in name]If I think a little, and find that
ord('A') = 65, I can work out what’s happening here. But it would be even clearer to write:ch_scores = [ord(char) - ord('A') + 1 for char in name]-
Cache the results from ord()? You’ll be making a call to
ord() at least once for every letter in every name; twice if you adopt my strategy. It might be better to precompute the scores in a global dictionary, e.g.:import string
CHAR_SCORES = {char: ord(char) - ord('A') + 1
for char in string.ascii_uppercase}and then just look up scores in this dictionary. It probably won’t make much of a difference for this problem, but it’s something to bear in mind.
-
Use generator comprehensions unless you need a list. A generator comprehension is like a list comprehension, but instead of precomputing all the elements upfront and storing them in a list, it computes-and-yields one element at a time. That can have big savings in memory.
Both of your list comprehensions get passed straight to
sum(), which can just get one element at a time – it isn’t jumping around the list.The change is minimal – just replace the square brackets of a list comprehension with parentheses instead, i.e.
scores = (calculate_name_score(pos, name) for (pos, name) in enumerate(names))Code Snippets
ch_scores = [ord(ch) - 64 for ch in name]ch_scores = [ord(char) - ord('A') + 1 for char in name]import string
CHAR_SCORES = {char: ord(char) - ord('A') + 1
for char in string.ascii_uppercase}scores = (calculate_name_score(pos, name) for (pos, name) in enumerate(names))Context
StackExchange Code Review Q#115489, answer score: 6
Revisions (0)
No revisions yet.