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 ferig
oder 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: , , ,


Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]





<< Startseite

This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]