Reputation: 25
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
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
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
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