(For context see A-level Computer Science)

This assignment was to write a program that would ask the user for an integer in the range 0 to 99 and write that out as English words.

My son’s solution treated the input as strings and picked the digits out as characters, and then used a series of if statements and a little other logic to write the corresponding words’:

def IntTest(x):
    try:
        int(x)
       
    except:
        x = IntTest(input('Please input an integer between 0 and 100 - '))

    return str(x)

def DigitToWord(x):
    if int(x) == 0:
        print('zero')
    if int(x) == 1:
        print('one')
    if int(x) == 2:
    # (... etc... removed for brevity...)
    if int(x) == 19:
        print('nineteen')
    if int(x) == 20:
        print('twenty')
    if int(x[0]) == 2 and int(x) > 20:
        print('twenty',end='-')
        DigitToWord(x[1])
    if int(x) == 30:
        print('thirty')
    if int(x[0]) == 3 and int(x) > 30:
        print('thirty',end='-')
        DigitToWord(x[1])
    if int(x) == 40:
        print('fourty')
    if int(x[0]) == 4 and int(x) > 40:
        print('fourty',end='-')
        DigitToWord(x[1])
    # (... etc... removed for brevity...)
    if int(x) == 80:
        print('eighty')
    if int(x[0]) == 8 and int(x) > 80:
        print('eighty',end='-')
        DigitToWord(x[1])
    if int(x) == 90:
        print('ninety')
    if int(x[0]) == 9 and int(x) > 90:
        print('ninety',end='-')
        DigitToWord(x[1])
    if int(x) == 100:
        print('one-hundred')
   
while True:
    Variable = IntTest(input('input a value between 0 and 100 - '))

    if int(Variable) >=0 and int(Variable) <= 100:
        DigitToWord(Variable)

It’s interesting that he saw this as a string processing problem rather than a numerical problem. We talked about that and various other things including:

  • How look-up tables can be created and employed to simplify the code considerably
  • The value of choosing good names for things (again!)
  • The idea that avoiding duplication is always good - this program duplicates the names of numbers that are multiples of 10 and the if statements are very repetitive
  • The importance of maintainability and how this is impacted by these other points

To illustrate all that, we looked at an alternative solution:

smallNumber = [
    "zero",
    "one",
    "two",
    "three",
    "four",
    "five",
    "six",
    "seven",
    "eight",
    "nine",
    "ten",
    "eleven",
    "twelve",
    "thirteen",
    "fourteen",
    "fifteen",
    "sixteen",
    "seventeen",
    "eighteen",
    "nineteen",
]

multipleOfTen = [
    "twenty",
    "thirty",
    "forty",
    "fifty",
    "sixty",
    "seventy",
    "eighty",
    "ninety",
]


def numberAsString(num):
    if num < 20:
        return smallNumber[num]
    
    result = multipleOfTen[int(num / 10) - 2]
    num = num % 10
    if num > 0:
        result += "-" + numberAsString(num)

    return result


def getNumber():
    while True:   
        num = input("Enter a number between 0 and 99: ")
        try:
            num = int(num)
            if num < 0 or num > 100:
                raise ValueError("Out of range")
            return num
        except:
            print("Please stay within the range")


def test():
    tests = [
        [0, "zero"],
        [9, "nine"],
        [11, "eleven"],
        [19, "nineteen"],
        [20, "twenty"],
        [21, "twenty-one"],
        [22, "twenty-two"],
        [29, "twenty-nine"],
        [30, "thirty"],
        [99, "ninety-nine"],
    ]

    failCounter = 0
    for test in tests:
        (num, expected) = test
        actual = numberAsString(num)
        if(actual != expected):
            failCounter += 1
            print("Failed test for", num, "got", actual, "but expected", expected)

    if failCounter == 0:
        print("All tests passed")

    print()


def main():
    while True:
        num = getNumber()
        print(numberAsString(num))

test()
main()

The importance of maintainability was highlighted by the next assignment being to extend this code to handle numbers up to 999. For the refactored code, this was a simple change to the numberAsString function:

def numberAsString(num):
    if num < 20:
        return smallNumber[num]

    if num < 100:
        result = multipleOfTen[int(num / 10) - 2]
        num = num % 10
        if num > 0:
            result += "-" + numberAsString(num)
        return result

    result = numberAsString(int(num / 100)) + " hundred"
    num = num % 100
    if num > 0:
        result += " and " + numberAsString(num)

    return result

All that was then needed was a tweak to input validation and a few extra tests.

Merlyn Kline
October 2021