jgats
jgats

Reputation: 25

Variable loses it's value somewhere along the way

I've been working on a roman numeral converter that loops until told to quit and it works... on the first iteration. The variable is just empty afterwards and it could have to do with where I assigned it?

Code:

num_array = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1),
('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'))

def int_to_roman(i):
    result = []
    for integer, numeral in num_array:
        count = int(i / integer)
        result.append(numeral * count)
        i -= integer * count
    return ''.join(result)

def roman_to_int(n):
    result = 0
    i = result
    for integer, numeral in num_array:
        while n[i:i + len(numeral)] == numeral:
            result += integer
            i += len(numeral)
    return result

x = ""
while x != "quit":
    x = ""
    x = input("enter number to be converted or quit to stop ")
    if x == "quit":
        break
    else:
        if x.isdigit():
            x = int(x)
            print(x, 'is',(int_to_roman(x)))
            print(' ')
        else:
            if x.isalpha():
                print(x,'is',(roman_to_int(x)))
                print(' ')

Outputs:

enter number to be converted or quit to stop C
C is 100

enter number to be converted or quit to stop C
C is 0

enter number to be converted or quit to stop 

Upvotes: 1

Views: 42

Answers (3)

Chris
Chris

Reputation: 22953

Your problem is this line:

num_array = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1),
('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'))

Using zip() like this at first may have seemed like a good idea. You only have to zip() your list once. Right?

However, this logic would be wrong. zip() returns an iterator. Once that iterator is exhausted(ie. You iterate over it once), it is done. You can no longer use it. You can see this yourself by looking at an example of the source code for zip():

def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

Instead of using zip() once, you should zip together your tuples each time you iterate over them:

First create your tuples:

ints = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
numerals = ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I')

Then zip them together:

for integer, numeral in zip(ints, numerals)

However, a possible more idomatic soultion would be to simply cast the result of zip() to a list, as @John La Rooy has already said. That way, you can use the result multiple times and avoid repeated calls to zip().

Upvotes: 1

Stephen Rauch
Stephen Rauch

Reputation: 49774

This line:

num_array = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1),
                ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V',
                 'IV', 'I'))

creates an iterator. Once the iterator is iterated, it is done. It will not iterate again. You either need to create the zip everytime you need to iterate, or store the results of the iterator into a list or tuple.

Upvotes: 1

John La Rooy
John La Rooy

Reputation: 304137

In Python3, zip returns an iterable. After the first call, the result of zip has been consumed. You could create a list from the result of zip instead

num_array = list(zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1),
                ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V',
                 'IV', 'I')))

It's easier to see with a smaller example

>>> nums = zip((1, 2, 3, 4), ('A', 'B', 'C', 'D'))
>>> nums
<zip object at 0x7f5a860cba08>
>>> for item in nums:
...     print(item)
... 
(1, 'A')
(2, 'B')
(3, 'C')
(4, 'D')
>>> for item in nums:   # Nothing left in zip object
...     print(item)
... 
>>> nums = list(zip((1, 2, 3, 4), ('A', 'B', 'C', 'D')))
>>> nums
[(1, 'A'), (2, 'B'), (3, 'C'), (4, 'D')]
>>> for item in nums:
...     print(item)
... 
(1, 'A')
(2, 'B')
(3, 'C')
(4, 'D')
>>> for item in nums:   # Can loop over a list as many times as you like
...     print(item)
... 
(1, 'A')
(2, 'B')
(3, 'C')
(4, 'D')
>>> 

Upvotes: 2

Related Questions