HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Project Euler #22

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
projecteulerstackoverflow

Problem

I gave problem #22 on Project Euler a try in Python3.

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:

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.