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 = [name.strip('"') for name in r.text.split(',')]
return sorted(names)
def calculate_name_score(pos, name):
'''
Calculate the "name sore" of a name at a specific position in a list
Parameters
----------
pos : int
zero-based index of a name in a list
name : str
name in CAPS
Returns
-------
name_score : int
'''
# letter scores: 'A' = 1, 'B' = 2, etc.!
ch_scores = [ord(ch) - 64 for ch in name]
score = sum(ch_scores)
# Project Euler: index of the names starts with 1!!
name_score = score * (pos + 1)
return name_score
def calculate_total_score(names):
'''Return a "name score" for a list of names
Parameters
----------
names : list (of strings)
Returns
-------
total_score : int
'''
scores = [calculate_name_score(pos, name) for (pos, name) in enumerate(names)]
total_score = sum(scores)
return total_score
def main():
names = fetch_names(URL)
result = calculate_total_score(names)
print(result)
if __name__ == '__main__':
sys.exit(main())
I'm grateful for any improvement, regardless whether it concerns the structuring of code, naming, documentation, or anything you can think of.