2
\$\begingroup\$

Simple function which returns a text representation for numbers in range [1...1000].

N100 = 'hundred'
N1000 = 'one thousand'
N1_9 = [
    'one', 'two', 'three',
    'four', 'five', 'six',
    'seven', 'eight', 'nine',
]
N10_19 = [
    'ten', 'eleven', 'twelve',
    'thirteen', 'fourteen', 'fifteen',
    'sixteen', 'seventeen', 'eighteen',
    'nineteen',
]
Nx10 = [
    'twenty', 'thirty', 'forty',
    'fifty', 'sixty', 'seventy',
    'eighty', 'ninety',
]


def make_word(num):
    assert 0 < num <= 1000
    if num == 1000:
        return N1000
    lst = [0] * 3
    for i in xrange(3):
        div = 10 ** (2 - i)
        lst[i] = num / div
        num %= div
    res = []
    if lst[0] != 0:
        res.append(
            '{} {}'.format(N1_9[lst[0]-1], N100)
        )
    if lst[1] or lst[2]:
        if lst[1] == 0:
            res.append(
                N1_9[lst[2] - 1]
            )
        elif lst[1] == 1:
            res.append(
                N10_19[lst[2]]
            )
        else:
            if lst[2] == 0:
                res.append(
                    Nx10[lst[1] - 2]
                )
            else:
                res.append(
                    '{}-{}'.format(Nx10[lst[1] - 2], N1_9[lst[2] - 1])
                )
    return ' and '.join(res)

for x in xrange(1, 1000 + 1):
     print x, make_word(x)

Is it possible to rewrite the function a little bit cleaner? Maybe decrease a number of branches somehow

\$\endgroup\$
0

1 Answer 1

4
\$\begingroup\$

rewrite the function a little bit cleaner

A way to achieve it, would be to divide your code into simpler functions, for instance:

lst = [0] * 3
for i in xrange(3):
    div = 10 ** (2 - i)
    lst[i] = num / div
    num %= div

Could be a simple function call:

lst = split_decimals(num)

(btw the name lst is not a very explicit...)

Maybe decrease a number of branches somehow

To clean your branching, I would advise to make a function that treats one part of the problem: numbers between 1 and 99, with a function under_hundred_as_word for instance.

Better variable names might also help the reader to understand your code.


After some further refactoring, this is what I came with:

units = [
    'zero', 'one', 'two', 'three',
    'four', 'five', 'six',
    'seven', 'eight', 'nine',
]
tens = [
    '', '', 'twenty', 'thirty', 'forty',
    'fifty', 'sixty', 'seventy',
    'eighty', 'ninety',
]

hundred_exceptions = {
        10: 'ten',
        11: 'eleven',
        12: 'twelve',
        13: 'thirteen',
        15: 'fifteen',
        18: 'eighteen',
}

def under_hundred_as_word(num):
    assert 0 < num < 100

    unit = num % 10
    ten = num / 10
    if ten == 0:
        return units[unit]
    elif ten == 1:
        if num in hundred_exceptions:
            return hundred_exceptions[num]
        else:
            return units[unit] + 'teen'
    else:
        if unit:
            return tens[ten] + '-' + units[unit]
        else:
            return tens[ten]

def make_word(num):
    assert 0 < num <= 1000
    if num == 1000:
        return 'one thousand'

    parts = []

    # two last digits
    if num % 100:
        parts.insert(0, under_hundred_as_word(num % 100))

    # hundred digit
    hundreds = (num / 100) % 10
    if hundreds:
        hundreds = units[hundreds] + ' hundred'
        parts.insert(0, hundreds)

    return ' and '.join(parts)

for x in xrange(1, 1000 + 1):
    print x, make_word(x)

It can be easily modified to print numbers greater than 1000.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.