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 Posts [Atom]
Kommentar veröffentlichen