Skip to content

Instantly share code, notes, and snippets.

@brighid
Created August 20, 2016 04:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brighid/91db733e5f3a0bd5a5e77e55ee53f8f0 to your computer and use it in GitHub Desktop.
Save brighid/91db733e5f3a0bd5a5e77e55ee53f8f0 to your computer and use it in GitHub Desktop.
Coder calisthenics: a recursive function for naming numbers
def words_from_int(int_or_string):
"""
Given a positive integer (or a string that looks like one), returns a
string that represents the spoken American English (short scale) form of
the given number.
"""
# Numbers' names
ones_place = [None, "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine"]
teens_place = [None, "eleven", "twelve", "thirteen", "fourteen",
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]
tens_place = [None, "ten", "twenty", "thirty", "forty",
"fifty", "sixty", "seventy", "eighty", "ninety"]
hundreds_marker = "hundred"
exponent_place = [None, "thousand", "million", "billion", "trillion",
"quadrillion", "quintillion", "sextillion", "septillion"]
# Basic "can we work with this?" checks
digit_string = str(int_or_string)
try:
digit_list = map(int, list(digit_string))
while digit_list[0] == 0:
digit_list.pop(0)
except ValueError:
# Raises if we got a negative int or a float.
raise ValueError("{!r} isn't a positive number.".format(int_or_string))
except IndexError:
# Raises if digit_list is empty, which happens if it was all zeros.
raise ValueError("Zero isn't a positive number.")
else:
if (len(digit_list) // 3) > len(exponent_place):
raise ValueError("Can't handle numbers bigger than {}s.".format(
exponent_place[-1]))
# Now that we've checked, let's process our digits.
def _word_from_triplets(digits):
"""Recursively turns a list of digits into a string describing them."""
# Base cases: zeros, ones, tens
# Return early if all digits in this chunk are zero
if sum(digits) == 0:
exponent = (len(digits) // 3) - 1
return ("", exponent)
exponent = 0
# Ones
if len(digits) == 1:
return (ones_place[digits[0]], exponent)
# Tens
if len(digits) == 2:
tens, ones = digits
if tens == 0:
return (ones_place[ones], exponent)
if ones == 0:
return (tens_place[tens], exponent)
if tens == 1:
return (teens_place[ones], exponent)
return ("{}-{}".format(tens_place[tens],
ones_place[ones]), exponent)
# "Half-recursive" case: hundreds
if len(digits) == 3:
if sum(digits) == digits[0]:
# i.e. if the other two digits are zero
return ("{} {}".format(ones_place[digits[0]],
hundreds_marker), exponent)
hundreds = ones_place[digits[0]]
rest, _ = _word_from_triplets(digits[1:])
if hundreds is None:
return (rest, exponent)
else:
return (
"{} {} {}".format(hundreds, hundreds_marker, rest),
exponent)
# Recursive case: thousands and up
head, rest = digits[:3], digits[3:]
head_words, _ = _word_from_triplets(head)
rest_words, rest_exp = _word_from_triplets(rest)
exponent = rest_exp + 1
exp_word = exponent_place[exponent]
all_words = "{head} {exp}{sep}{rest}".format(
head=head_words,
exp=exp_word,
sep=", " if rest_words != "" else "",
rest=rest_words)
return (all_words, exponent)
# Short numbers we process in one chunk.
if len(digit_list) < 4:
all_words, _ = _word_from_triplets(digit_list)
print(all_words)
return all_words
# If the length of our digit list isn't evenly divisible by three, take
# some numbers from the head until we have a tail that's divisible by
# three. Recursively process the tail, combine it with the head.
d = len(digit_list) % 3
head, rest = digit_list[:d], digit_list[d:]
head_words, _ = _word_from_triplets(head)
rest_words, rest_exp = _word_from_triplets(rest)
exponent = rest_exp + 1
exp_word = exponent_place[exponent]
if head_words == "":
all_words = rest_words
elif rest_words == "":
all_words = "{} {}".format(head_words, exp_word)
else:
rest_sep = " " if len(digit_list) < 5 else ", "
all_words = "{head} {exp}{rest_sep}{rest}".format(
head=head_words,
exp=exp_word,
rest_sep=rest_sep,
rest=rest_words)
print(all_words)
return all_words
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment