Montag, 18. November 2013
Musterlösung Römische Zahlen umwandeln (POS1: 2BHIF)
Mögliche Lösung zu Römische Zahlen umwandeln (POS1: 2BHIF).
Die Klasse könnte folgendermaßen programmiert werden. Das Dictionary enthält die möglichen "Ziffern" (unter Hochkomma, da bestimmte Ziffernkombinationen, wie zum Beispiel "IX", als eine Ziffer gelten). Zur Umrechnung müssen die Werte absteigend sortiert werden, damit die richtige "Ziffer" (z.B. "IX" für 9) verwendet wird.
Bei der Umrechnung in römische Zahlen wird der Reihe nach von der größten zur kleinsten "Ziffer" (daher absteigend sortiert) probiert, ob der Wert enthalten ist. Ist das der Fall, dann wird die "Ziffer" an den String angehängt und der entsprechende Wert von der Zahl abgezogen. Zum Beispiel 1999
1999 M 999 IM 0 ferigoder 392
392 C 292 C 192 C 92 XC 2 I 1 I 0 fertig
Bei der umgekehrten Umrechnung wird der Reihe nach probiert, ob am Beginn der römischen Zahl eine bestimmte "Ziffer" vorkommt. Ist dies der Fall, so wird der entsprechende Wert zum Ergebnis dazugezählt. Mit einem Zähler wird geprüft, dass keine Ziffer mehr als drei mal vorkommt. Zum Beispiel CCCXCII
CCCXCII 100 (C) CCXCII 200 (C) CXCII 300 (C) XCII 390 (XC) II 391 (I) I 392 (I)
Zum Testen wird einfach eine Liste von Zahlen verwendet, die in römische Zahlen und wieder zurück gerechnet werden. Ungültige Zahlen liefern eine Exception, die bei einem entsprechenden Testfall kommen muss:
# test value error
try:
ra = RomanNumber("VIIII")
assert(False)
except ValueError:
pass
Die gesamte Klasse könnte also so aussehen:
#!/usr/bin/env python3
"""
File: RomanNumber.py
Desc: class for roman numbers
Created: 2013-11-18, Harald R. Haberstroh
"""
class RomanNumber:
"""Roman numbers.
Internal representation is always the integer value.
This implementation stores both values (integer and roman).
"""
digits = { "I":1, "V":5, "X":10, "L":50, "C":100, "D":500, "M":1000,
"IV":4, "IX":9, "XL":40, "XC":90, "CD":400, "CM":900}
keys = list(digits.items())
keys.sort(key=lambda x: -x[1])
def __init__(self, value):
"""Initialize with value.
If type of value is int, than this is the integer value.
If type of value is str, than this is a roman number, which has
to be converted into the internal int-value"""
if type(value) is str:
self.roman = value
self.intval = self.__toInt(value)
else:
self.intval = value
self.roman = self.__toRoman(value)
def toRoman(self):
"""returns string with roman representation"""
return self.roman
def toInt(self):
"""returns integer value of roman number"""
return self.intval
def add(self, other):
"""add the value of other (RomanNumber) to own (self) value and
return a new RomanNumber object."""
return RomanNumber(self.intval + other.toInt())
def sub(self, other):
"""subtract the value of other (RomanNumber) from own (self) value and
return a new RomanNumber object."""
return RomanNumber(self.intval - other.toInt())
def mul(self, other):
"""multiply the value of other (RomanNumber) with own (self) value and
return a new RomanNumber object."""
return RomanNumber(self.intval * other.toInt())
def div(self, other):
"""divide (integer division) own value by the value of other value and
return a new RomanNumber object."""
return RomanNumber(self.intval // other.toInt())
def __str__(self):
"""returns the same value as toRoman(self)"""
return "%s (%d)" % (self.roman, self.intval)
def __toRoman(self, intval):
romval = ""
for digit, val in self.keys:
while intval >= val:
romval += digit
intval -= val
return romval
def __toInt(self, romval):
roman = romval.upper()
intval = 0
for digit, val in self.keys:
cntDigit = 0 # count same group
while roman.startswith(digit):
intval += val
roman = roman[len(digit):]
cntDigit += 1
if cntDigit >= 4:
raise ValueError()
return intval
if __name__ == '__main__':
zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 32, 43, 48, 49, 54, 65, 76, 87, 98,
99, 100, 201, 302, 403, 491, 499, 504, 605, 706, 807, 908, 999, 1001,
1279, 1331, 1496, 1883, 1949, 1967, 1990, 1999, 2006, 2013]
for f in zahlen:
rn = RomanNumber(f)
# print('%4d: %20s - %4d' % (f, rn.toRoman(), rn.toInt()))
assert(f == rn.toInt())
for i in range(len(zahlen) // 2):
ra = RomanNumber(zahlen[i])
rb = RomanNumber(zahlen[-i])
rc = ra.add(rb)
print(ra, "+", rb, "=", rc)
assert(zahlen[i] + zahlen[-i] == rc.toInt())
rc = rb.sub(ra)
print(rb, "-", ra, "=", rc)
assert(zahlen[-i] - zahlen[i] == rc.toInt())
for i in range(len(zahlen) // 8):
ra = RomanNumber(zahlen[i])
rb = RomanNumber(zahlen[len(zahlen) // 2 - i])
rc = ra.mul(rb)
print(ra, "*", rb, "=", rc)
assert(zahlen[i] * zahlen[len(zahlen) // 2 - i] == rc.toInt())
# test value error
try:
ra = RomanNumber("VIIII")
assert(False)
except ValueError:
pass
print("all tests passed")
Beim romancalc.py wird jeder Schritt mit einem try - except - Block umgeben, um die Fehler- und Sonderfälle zu behandeln.
Eine Spezialität ist der Aufruf des entsprechenden Operators. Hier wird ein Dictionary mit den möglichen Operationen verwendet, wobei der Aufruf als Funktion und nicht als Methode stattfindet (der erste Parameter self enthält ja das Objekt, welches dann explizit mit übergeben wird).
Das gesamte Modul könnte wie folgt aussehen:
#!/usr/bin/env python3
"""
File: romancalc.py
Desc: simple caluculator for roman numbers, using RomanNumber
Created: 2013-11-18, Harald R. Haberstroh
"""
from RomanNumber import RomanNumber
import sys
operators = { "+":RomanNumber.add, "-":RomanNumber.sub,
"*":RomanNumber.mul, "/":RomanNumber.div }
while True:
try:
line = input(">>> ")
except EOFError:
break
try:
a, op, b = line.split()
except ValueError:
print("syntax error", file=sys.stderr)
continue
if a.isdigit():
a = int(a)
if b.isdigit():
b = int(b)
try:
ra = RomanNumber(a)
rb = RomanNumber(b)
except ValueError:
print("invalid roman number", file=sys.stderr)
continue
try:
rc = operators[op](ra, rb)
print(ra, op, rb, "=", rc)
except KeyError:
print("invalid operator '%s'" % (op), file=sys.stderr)
Labels: Aufgabe, Lösung, POS1-2, Python
Abonnieren Kommentare [Atom]
Kommentar veröffentlichen