<<  4 ˌ HTML  >>

5 ˌ. ׸

5.1.

˟ ˌ, “ ” ˞ ȓ 킯硒 ґ. ˼ ̋ 皡 Ղ, ղ 򵌡 󹼵 ˟˂(before) ȓ .

ˌ 󹼂硒 . ۸ 븀皫 皦, ˼ . ˼ һ ռ ȍ ( “Copyright 1946” “Copyright MCMXLVI” ), ˼ Сһ ˼ ˂ ˂ ( “established 1888” “established MDCCCLXXXVIII” ) . ˼ ס굎 ˟ 禹 ˅. ס˼ ˂ ̸ ̦ ̫ ȍ ( ).

Ы, 7˂ ը ˅ վ ՠյ 禂͵ .

  1. I = 1
  2. V = 5
  3. X = 10
  4. L = 50
  5. C = 100
  6. D = 500
  7. M = 1000

嵴  ˅:

  1. ը˼ ˞. I˼ 1, II 2, III˼ 3. VI˼ 6 (ը˞, “5 1”), VII 7, VIII˼ 8.
  2. 10 ˤ˂ ը (I, X, C, M)˼ 3 տ ՠյ ˅. 4, ˼ ˸˂ ˌ ҙ˼ 5 ը ȩ . ˼ 4 IIII һҲ ; , ס˼ IV һȲ 翫 (“5 1 ”). 40˼ XL 翫 (“50 10”), 41˼ XLI, 42 XLII, 43˼ XLIII, 44 (“50 10, 5 1Ղ”) XLIV 翫.
  3. , 9, ˼ ˸˂ ˌ ҙ˼ 10 ը ȩ : 8˼ VIII, һ 9 (“10 1 Ղ”) IX, VIIIII ը 4 տ ՠյ ը). 90˼ XC, 900˼ CM.
  4. 5 ը˼ ՠյ . 10˼ VV 즗 ȗ X . 100˼ ȗ C, ૑ LL .
  5. ȗ ˤ , 翫, ը˂ Ы . DC 600; CD ˟ (400뵴, “ 500 100 ”). CI 101; IC ֵ (Ҁ ˼ 1 100 ˞ ¹ ը; ˼ ס XCIX 皵 (“ 100 10, 10 1 Ղ”).

 ˼ ˍ˅ ȍ 垂 ˅:

  1. һ һȫ ˂ 騂 վ ˅.
  2. : ˂ ը , ס˼ һ˂ һҸ (, Ȃ ס˼ վ ˅).
  3. ς ՟ˤ˂ ˅, 1 3999. (뵎˼ ˂ վ 嵴, ˂ ˤ ᫑ ײ қ˸븇 ȗ˞ ˼ 1000 稲 һȲ, һ ס 皫 . ˌ˂ ˞ ˤ, ˼ 1 3999 .)
  4. 0˼ վ . (Ҕﵵ, 뵎˼ 0 ࡢ Ҋ . ˼ 皡 ˅ ա ˤ ; ˼ 皡 ˅ ˼ ȡ ?)
  5. ˸ վ .
  6. һ ܹ վ .

ݵ ˟ , ﹚˂ Dz ȗ ˅?

roman.py˂ 禡

  1. toRoman˼ 1 3999 ݵ ۹ ˂ ՠ .
  2. toRoman˼ 1 3999 ՟ˤ ˂ ۹ 煲 ܂ .
  3. toRoman˼ -۹ 񹚡 煲 ܂ .
  4. fromRoman˼ ̑ 졒 ס ՠ .
  5. fromRoman˼ 煲 ܂ .
  6. ̑, ס ̀, һ ס 즗, ˼ һ . 1..3999ȍ˂ ݵ n fromRoman(toRoman(n)) == n.
  7. toRoman˼ ȗ ը ȍ ՠ .
  8. fromRoman˼ ը . ( Ȃ, ը 煲翫 ܂ ).

ܲ

5.2. romantest.py ҂챉

˟ ˂ ﹚ ȗ ˅ ˤ ˂Ղ, ˼ ȗ 굎 硒 : ׸ ݵ 󹼂 ﹚ ˂ 졒 ׵ 硒 . ˼ : 皱 󹼂 ׸ 󹼂硒 .

׸ 嵴, Ҁ ˂ ﹚˂ , , 󹼵皡 ׸ ˅ ը. ˼ ˂ . ˼ ׸ ˤ 皡 ˅, ˞ unittest ݵ 翫.

Note
unittest 2.1 ȍ. 2.0 ȍ ס pyunit.sourceforge.net ˅.

׸ ˟̹ ׸-秸 է ˟˂ ˌ . ׸ 󹼂, ס굎 󹼂 ( ׵ ׸ 󹼂챉 ˟ ), ȍ ס굎 ȸ즗. ׸ -˂ ˞ ˼ ̸ ׸ ˤ ̹ա , ס˼ ݵ է˂ :

˼ ﹚ ˤ ˟ ׸ ݵ, ס굎˼ 󹼵 roman.py 皡 . ȡ ݵ 굎 ҵ 皫 ˞ Վ 皫 ; ˼ ޸ 굵 禂 . ̾܂ ˅, ס 廡 ȓ .

Example 5.1. romantest.py

, ˼ ꡜ ̴ ȍ ϵ ˅ (Windows, UNIX, Mac OS).

"""Unit test for roman.py"""

import roman
import unittest

class KnownValues(unittest.TestCase):
    knownValues = ( (1, 'I'),
                    (2, 'II'),
                    (3, 'III'),
                    (4, 'IV'),
                    (5, 'V'),
                    (6, 'VI'),
                    (7, 'VII'),
                    (8, 'VIII'),
                    (9, 'IX'),
                    (10, 'X'),
                    (50, 'L'),
                    (100, 'C'),
                    (500, 'D'),
                    (1000, 'M'),
                    (31, 'XXXI'),
                    (148, 'CXLVIII'),
                    (294, 'CCXCIV'),
                    (312, 'CCCXII'),
                    (421, 'CDXXI'),
                    (528, 'DXXVIII'),
                    (621, 'DCXXI'),
                    (782, 'DCCLXXXII'),
                    (870, 'DCCCLXX'),
                    (941, 'CMXLI'),
                    (1043, 'MXLIII'),
                    (1110, 'MCX'),
                    (1226, 'MCCXXVI'),
                    (1301, 'MCCCI'),
                    (1485, 'MCDLXXXV'),
                    (1509, 'MDIX'),
                    (1607, 'MDCVII'),
                    (1754, 'MDCCLIV'),
                    (1832, 'MDCCCXXXII'),
                    (1993, 'MCMXCIII'),
                    (2074, 'MMLXXIV'),
                    (2152, 'MMCLII'),
                    (2212, 'MMCCXII'),
                    (2343, 'MMCCCXLIII'),
                    (2499, 'MMCDXCIX'),
                    (2574, 'MMDLXXIV'),
                    (2646, 'MMDCXLVI'),
                    (2723, 'MMDCCXXIII'),
                    (2892, 'MMDCCCXCII'),
                    (2975, 'MMCMLXXV'),
                    (3051, 'MMMLI'),
                    (3185, 'MMMCLXXXV'),
                    (3250, 'MMMCCL'),
                    (3313, 'MMMCCCXIII'),
                    (3408, 'MMMCDVIII'),
                    (3501, 'MMMDI'),
                    (3610, 'MMMDCX'),
                    (3743, 'MMMDCCXLIII'),
                    (3844, 'MMMDCCCXLIV'),
                    (3888, 'MMMDCCCLXXXVIII'),
                    (3940, 'MMMCMXL'),
                    (3999, 'MMMCMXCIX'))

    def testToRomanKnownValues(self):
        """toRoman should give known result with known input"""
        for integer, numeral in self.knownValues:
            result = roman.toRoman(integer)
            self.assertEqual(numeral, result)

    def testFromRomanKnownValues(self):
        """fromRoman should give known result with known input"""
        for integer, numeral in self.knownValues:
            result = roman.fromRoman(numeral)
            self.assertEqual(integer, result)

class ToRomanBadInput(unittest.TestCase):
    def testTooLarge(self):
        """toRoman should fail with large input"""
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 4000)

    def testZero(self):
        """toRoman should fail with 0 input"""
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 0)

    def testNegative(self):
        """toRoman should fail with negative input"""
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, -1)

    def testDecimal(self):
        """toRoman should fail with non-integer input"""
        self.assertRaises(roman.NotIntegerError, roman.toRoman, 0.5)

class FromRomanBadInput(unittest.TestCase):
    def testTooManyRepeatedNumerals(self):
        """fromRoman should fail with too many repeated numerals"""
        for s in ('MMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'):
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)

    def testRepeatedPairs(self):
        """fromRoman should fail with repeated pairs of numerals"""
        for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'):
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)

    def testMalformedAntecedent(self):
        """fromRoman should fail with malformed antecedents"""
        for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV',
                  'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'):
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)

class SanityCheck(unittest.TestCase):
    def testSanity(self):
        """fromRoman(toRoman(n))==n for all n"""
        for integer in range(1, 4000):
            numeral = roman.toRoman(integer)
            result = roman.fromRoman(numeral)
            self.assertEqual(integer, result)

class CaseCheck(unittest.TestCase):
    def testToRomanCase(self):
        """toRoman should always return uppercase"""
        for integer in range(1, 4000):
            numeral = roman.toRoman(integer)
            self.assertEqual(numeral, numeral.upper())

    def testFromRomanCase(self):
        """fromRoman should only accept uppercase input"""
        for integer in range(1, 4000):
            numeral = roman.toRoman(integer)
            roman.fromRoman(numeral.upper())
            self.assertRaises(roman.InvalidRomanNumeralError,
                              roman.fromRoman, numeral.lower())

if __name__ == "__main__":
    unittest.main()   

ܲ

5.3. ˤ ׸

׸˂ ˌ ˞ ˼ Ҽ˞ ׸ . һ˂ ׸ ׸ ˅ ˂ ը 篫.

׸ .... ˅

˟ , ˂ տ ׸ ̈. ˸ ˼ 翫:

  1. toRoman˼ 1 3999˂ ݵ ۹ ՠ .

Example 5.2. testToRomanKnownValues

class KnownValues(unittest.TestCase):
1
    knownValues = ( (1, 'I'),
                    (2, 'II'),
                    (3, 'III'),
                    (4, 'IV'),
                    (5, 'V'),
                    (6, 'VI'),
                    (7, 'VII'),
                    (8, 'VIII'),
                    (9, 'IX'),
                    (10, 'X'),
                    (50, 'L'),
                    (100, 'C'),
                    (500, 'D'),
                    (1000, 'M'),
                    (31, 'XXXI'),
                    (148, 'CXLVIII'),
                    (294, 'CCXCIV'),
                    (312, 'CCCXII'),
                    (421, 'CDXXI'),
                    (528, 'DXXVIII'),
                    (621, 'DCXXI'),
                    (782, 'DCCLXXXII'),
                    (870, 'DCCCLXX'),
                    (941, 'CMXLI'),
                    (1043, 'MXLIII'),
                    (1110, 'MCX'),
                    (1226, 'MCCXXVI'),
                    (1301, 'MCCCI'),
                    (1485, 'MCDLXXXV'),
                    (1509, 'MDIX'),
                    (1607, 'MDCVII'),
                    (1754, 'MDCCLIV'),
                    (1832, 'MDCCCXXXII'),
                    (1993, 'MCMXCIII'),
                    (2074, 'MMLXXIV'),
                    (2152, 'MMCLII'),
                    (2212, 'MMCCXII'),
                    (2343, 'MMCCCXLIII'),
                    (2499, 'MMCDXCIX'),
                    (2574, 'MMDLXXIV'),
                    (2646, 'MMDCXLVI'),
                    (2723, 'MMDCCXXIII'),
                    (2892, 'MMDCCCXCII'),
                    (2975, 'MMCMLXXV'),
                    (3051, 'MMMLI'),
                    (3185, 'MMMCLXXXV'),
                    (3250, 'MMMCCL'),
                    (3313, 'MMMCCCXIII'),
                    (3408, 'MMMCDVIII'),
                    (3501, 'MMMDI'),
                    (3610, 'MMMDCX'),
                    (3743, 'MMMDCCXLIII'),
                    (3844, 'MMMDCCCXLIV'),
                    (3888, 'MMMDCCCLXXXVIII'),
                    (3940, 'MMMCMXL'),
                    (3999, 'MMMCMXCIX'))                        2

    def testToRomanKnownValues(self):                           3
        """toRoman should give known result with known input"""
        for integer, numeral in self.knownValues:
            result = roman.toRoman(integer)                     4 5
            self.assertEqual(numeral, result)                   6
1 ׸ 󹼂챉 ˤ, տ ˼ unittest ݵ˂ TestCase . ˼ ޸ ˅ ˂ ׸ ס ȍ ˼ 禡 ׸ ˅.
2 ˼ ȡ 낄 ۹/ ˂ . ס ˌ ˼ 10 ˂ , ˌ ҙ˼ , ˂-ը , ݵ , ˤ ˼ ւ. ׸˂ ˼ ݵ ׸ 즗, ˞ ȝ ׸ .
3 ݵ Ҽ˞ ׸ ˂ ޸, ס˼ 빚 ՠ . ޸ է́ 桒 ȗ˞ ˍ, ׸ 煵; ޸ , ׸ ܂ 煵.
4 ˂ toRoman ﹚ ̉ 즗. (, ﹚ 󹼵 , һ 󹼵, ס ̉ .) 皱 toRoman ﹚ ˤ API ˂: ס˼ ( ) ˂ ۹ ̑ ( ) ը ՠ . API סꡜ , ׸ ܂ 煵.
5 ˼ toRoman ̉ ² . ˼ ˂˞. ס ̉ , toRoman˼ է̀Ы ˼ ݵ . toRoman ӫ, ׸ ܂ 煵.
6 toRoman ﹚ 騂 ˂ҡ, 騂 ̉, ˞ ᇵ, һ˂ ՠ ۂ, ˌ ﹚ ՠ ˼ (right) . ˼ 鍂 ը, TestCase ޸, assertEqual ϡ嵴, ˂ ˼ 肄. toRoman ՠ (result) ȗ졒 ˅ (numeral) , assertEqual ׸ ܂ . ˂ , assertEqual ǡ굵 . toRoman ՠ ݵ ȗ , assertEqual ૑ , testToRomanKnownValues ȗ˞ ᇂ, ס˼ toRoman ׸ ˂킄.

5.4. ˤ ׸

˂ ﹚ ˼ 煲 ׸ ̾܂ ; һ 煲 ס굎 ܂ ׸ . ˂ ܡ 즗; ס굎˼ ȗ վ ܂.

toRoman ˤ ˂ 즗 :

  1. toRoman 1 3999˂ ՟ˤ ˅ ۹ 煲翫 ܂ .
  2. toRoman -۹ 񹚡 煲 ܂ .

, ﹚ 븇, һҸ unittest ݵ˼, ﹚ һ 塻 煲 ׸ 챉 ˤ ޸ ϡ.

Example 5.3. Testing bad input to toRoman

class ToRomanBadInput(unittest.TestCase):
    def testTooLarge(self):
        """toRoman should fail with large input"""
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 4000) 1

    def testZero(self):
        """toRoman should fail with 0 input"""
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 0)    2

    def testNegative(self):
        """toRoman should fail with negative input"""
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, -1)

    def testDecimal(self):
        """toRoman should fail with non-integer input"""
        self.assertRaises(roman.NotIntegerError, roman.toRoman, 0.5)  3
1 unittest˂ TestCase assertRaises ޸ ϡ嵴, ס˼ ˸˂ 빚 ̑: ȗ졒 ˅ , ׸ ˅ ﹚, ﹚ 煫 빚. ( ׸ ﹚ ȗ˂ 빚 , Ы, ׵ ݵ assertRaises ҄ 煲, ס˼ ׵ ׸졒 ˅ ﹚ .) 졒 ˅ ˂ : toRoman ˞ ̉졒 (ס try...except ҅˸븇) ס , assertRaises ݵ 굎 Ђ. ݵ ˼ ס (roman.OutOfRangeError), ﹚(toRoman), toRoman˂ 빚(4000) 煫 , assertRaises ˂ toRoman ̉졒 ס roman.OutOfRangeError 騸 肄. (﹚ ւ ݵ ˼ ̹ һ ȡ ̅ ݱނ ˅?)
2 ׸, ˼ ׸ ˅. 즗, 0 ˸˂ ۹ , ׸ 翫 (testZero testNegative). testZero, toRoman 0 ̉ roman.OutOfRangeError ׸; ס roman.OutOfRangeError (ס ˂ ՠ졁һ ˼, ס ը), ׸ 煵.
3 禡 #3˼ toRoman -۹ ޲ ۂ, toRoman (0.5) ̉ roman.NotIntegerError ׸ . toRoman roman.NotIntegerError , ׸ 煵.

˸˂ ˼, toRoman fromRoman ˞ ȩ, ˸ :

  1. fromRoman ̑ 졒 ס ՠ .
  2. fromRoman˼ 煲 ܂ .

禡 #4 requirement #1 ˼ վ , ˂ ȝ ՠՂ Ы ׸. 禡 #5 禡 #2, #3 ˼ վ , ˂ һ ׸졒 fromRoman ˞ 낄.

Example 5.4. Testing bad input to fromRoman

class FromRomanBadInput(unittest.TestCase):
    def testTooManyRepeatedNumerals(self):
        """fromRoman should fail with too many repeated numerals"""
        for s in ('MMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'):
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s) 1

    def testRepeatedPairs(self):
        """fromRoman should fail with repeated pairs of numerals"""
        for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'):
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)

    def testMalformedAntecedent(self):
        """fromRoman should fail with malformed antecedents"""
        for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV',
                  'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'):
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)
1 Ғ ˼ ț ; ܁˼ toRoman һ ׸嵴 ȍ ܁졜 騂 . 廡 Ȃ 翫 һ Ȃ : roman.InvalidRomanNumeralError. ס˼ (roman.OutOfRangeError roman.NotIntegerError ) roman.py ˂ ˅ ̄ ˂ ̋ . ̋ ˂ , ˌ˂ ՠ . roman.py ӱ .

5.5. ȡ ˤ ׸

, ˼ ˤ˂ 򵌡, ﹚˂ , ȗ̉˞ ﹚˂ ւ졒 ˅ է , һ A B 졒 һ B A . , ˼ “ȡ (sanity check)” 󹼂 A B A, ˂ ܵ ՚ 桒, ՚ 桒, ˼ ˂ ՚׵ է́ 桒, ˅ .

  1. һ˂ ̑, ס , ס , ˼ 󂧫 . 1..3999 ˅ ݵn 'fromRoman(toRoman(n)) == n '.

Example 5.5. Testing toRoman against fromRoman

class SanityCheck(unittest.TestCase):
    def testSanity(self):
        """fromRoman(toRoman(n))==n for all n"""
        for integer in range(1, 4000):        1 2
            numeral = roman.toRoman(integer)
            result = roman.fromRoman(numeral)
            self.assertEqual(integer, result) 3
1 ˟ range ˅, һ ס˼ ˂ 빚 ̉, ס˼ ۹˂ ՠ嵴 տ 빚 (1) ¹˞ տ 빚(4000) һ ւ 桒 . , 1..3999, ס˼ 챉 ˤ ՟ˤ.
2 ȡ ݱނ졒 ˼ (빚) ҄煱 integer(۹) 즗 ; ס˼ ꡜ 坡 .
3 ˞ ׸ Ғ ֹ˞ 豉 : һ˂ (integer) ̑, ס (numeral) , ס (result) 졒 ˸ 󂧫 ˼ ҵ 낄. , assertEqual ׸ 畸 煵 . ݵ , assertEqual ȗ ղ , ˟̹ testSanity ޸ ղ , ׸ .

˂ 禡˼ 굎 Ҁ ס굎˼ ˂˞ ȍ챉 ը:

  1. toRoman ȗ ը ȍ ՠ , fromRoman ը˂ ޲ (, Ȃ ס˼ ը 煲 ܂ ).
  2. fromRoman˼ ը˂ ޲ (, Ȃ ס˼ ը 煲 ܂ ).

ȍ˼, ס굎˼ ˼ ˂˞. , fromRoman ˼ ը ը ޲ ۂ. һ ס굎˼ ˟ ˂˞ ˼ ; toRoman ȗ ը˂ ̉ ՠ, fromRoman˼ ˞ ը˂ ޲ , ˂ “ȡ (sanity check)” (禡 #6)˼ ܂ . 'ס˼ (only) ը˂ ޲ ' ȍ˼ ˂˞, һ ݵ ̸ 쵒, ը ȗ ըϡ, ˤ ˟ ۂ ˅. ס ۂ ˅, ׸ ˅

Example 5.6. Testing for case

class CaseCheck(unittest.TestCase):
    def testToRomanCase(self):
        """toRoman should always return uppercase"""
        for integer in range(1, 4000):
            numeral = roman.toRoman(integer)
            self.assertEqual(numeral, numeral.upper()) 1

    def testFromRomanCase(self):
        """fromRoman should only accept uppercase input"""
        for integer in range(1, 4000):
            numeral = roman.toRoman(integer)
            roman.fromRoman(numeral.upper())           2 3
            self.assertRaises(roman.InvalidRomanNumeralError,
                              roman.fromRoman, numeral.lower())
1 ׸ ˌ ˼ ס ׸ ݵ . ס˼ toRoman ՠ ˼ ˼ 皲 졟 ˅ ׸ ; ը˼ ˂ ׸ ˂ 翫. ը- ׸ ˟ ׸ 翫. ˼ , ˟̹ ՟ˤ˂ 졒 toRoman ̉븇, ȡ (sanity check) ͂졒 ˼ 皵 .[11] һ ס˼ ˞  ˤՠ : ׸ ˂ ը . ȡ 衜 ͂졒, ׸ ܂ ȗȗ . ˼ ˼ ܹ ׸ ˂ ըϡ Dz ۂ嵴 ܂ ± . ׸˂ ܹ ס굎 Dz ± , ס˼ ׸ ˧-낧 騸 .
2 Տ 炄 ˅: toRoman ȗ ը ՠ “ 衒 ˅˸” 졒, ˞ ס˂ ՠ ը fromRoman ը ޲ ׸. ? Ҁ 'toRoman ȗ ը ՠ '˼ ˞ 禡 ը. , ס ȗ ը ՠ쵵 禡 , testToRomanCase ׸ , һ ׸ ˟ . ˼ ˂ ˞ : ׸ ݵ ꡜ ˅ . ݵ ׸ ҵ .
3 fromRoman˂ ՠ 񫍂 즗. ˼ ˞碂 ը; ﹚ ՠ 졒 ǵ , ˼ ՠ ՚. , ס . ׸ ՠ ǡ굵 ׸ ; ס˼ fromRoman ը ޲ ׸.

5.6. roman.py, 1

˂ ׸ 칼Ղ, ˂ ׸ ׸챉 ̵졒 ˅ 󹼂챉 ̡. 硒 , ݵ ׸ ܂ ˅ roman.py˂ Վ һ һ ס굎 ˅.

Example 5.7. roman1.py

, ˼ ꡜ ̴ ȍ ϵ ˅ (Windows, UNIX, Mac OS).

"""Convert to and from Roman numerals"""

#Define exceptions
class RomanError(Exception): pass                1
class OutOfRangeError(RomanError): pass          2
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass 3

def toRoman(n):
    """convert integer to Roman numeral"""
    pass                                         4

def fromRoman(s):
    """convert Roman numeral to integer"""
    pass
1 ˂ ̋ ˂ վ. , ˼ ˍ 븇 ˂ ๼. Exception (湚˞ ˼ ) 챉 ˌ, ס˼ ݵ ˌ ȗ . һ (Exception ȗ) RomanError ˂嵴, ס˼ һ˂ ݵ ̋ . ˼ ˂ ը; һ Exception ˞ Ҽ˞ ȗ ˅.
2 OutOfRangeError NotIntegerError toRoman ˂ ȍ, ToRomanBadInput ۵ , ˂ 飂 .
3 InvalidRomanNumeralError fromRoman ˂ ȍ稹, FromRomanBadInput ۵ , 飂 .
4 , ˂ ﹚ ˤ API 󹼂챉 , һ ס굎 򵝂챉 皫 , ˂ ղ pass ȍ ס굎 .

˞ ( ̀煹 ~): ˂ ׸ ˞ ˼ ݵ 硒 . , ݵ ׸ ܂ . ȍ, ׸ 1 , romantest.py ղ 졒 ǡ굵-皲 ﹚ 皡 ۵ ׸ 󹼂 ˍ .

romantest1.py -v ჲ- ȍ 즗, ס˼ 򹹂 ̉ ׸ 騂 Dz 翂 ˅ ˅. , ˂ ̉˼ ꡜ 炯 :

Example 5.8. Output of romantest1.py against roman1.py

fromRoman should only accept uppercase input ... ERROR
toRoman should always return uppercase ... ERROR
fromRoman should fail with malformed antecedents ... FAIL
fromRoman should fail with repeated pairs of numerals ... FAIL
fromRoman should fail with too many repeated numerals ... FAIL
fromRoman should give known result with known input ... FAIL
toRoman should give known result with known input ... FAIL
fromRoman(toRoman(n))==n for all n ... FAIL
toRoman should fail with non-integer input ... FAIL
toRoman should fail with negative input ... FAIL
toRoman should fail with large input ... FAIL
toRoman should fail with 0 input ... FAIL

======================================================================
ERROR: fromRoman should only accept uppercase input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 154, in testFromRomanCase
    roman1.fromRoman(numeral.upper())
AttributeError: 'None' object has no attribute 'upper'
======================================================================
ERROR: toRoman should always return uppercase
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 148, in testToRomanCase
    self.assertEqual(numeral, numeral.upper())
AttributeError: 'None' object has no attribute 'upper'
======================================================================
FAIL: fromRoman should fail with malformed antecedents
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 133, in testMalformedAntecedent
    self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with repeated pairs of numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 127, in testRepeatedPairs
    self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with too many repeated numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 122, in testTooManyRepeatedNumerals
    self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 99, in testFromRomanKnownValues
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
======================================================================
FAIL: toRoman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 93, in testToRomanKnownValues
    self.assertEqual(numeral, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: I != None
======================================================================
FAIL: fromRoman(toRoman(n))==n for all n
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 141, in testSanity
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
======================================================================
FAIL: toRoman should fail with non-integer input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 116, in testDecimal
    self.assertRaises(roman1.NotIntegerError, roman1.toRoman, 0.5)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: NotIntegerError
======================================================================
FAIL: toRoman should fail with negative input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 112, in testNegative
    self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, -1)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: OutOfRangeError
======================================================================
FAIL: toRoman should fail with large input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 104, in testTooLarge
    self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, 4000)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: OutOfRangeError
======================================================================
FAIL: toRoman should fail with 0 input                                 1
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 108, in testZero
    self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, 0)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: OutOfRangeError                                        2
----------------------------------------------------------------------
Ran 12 tests in 0.040s                                                 3

FAILED (failures=10, errors=2)                                         4
1 Ү unittest.main() , ס˼ ׸ , ס˼ romantest.py˂ ˂ ޸ Ȃ. ׸ , ס˼ ޸˂ doc string ׸ ܂ ̉傄. ȗ , ˂ ׸ 굵 .
2 ܂ ׸ , unittest 騂 Ǹ 첔ҵ 煫 ̧˞ ۼ ̉傄. , assertRaises ˂ ̉˼ (also called failUnlessRaises) AssertionError ӫ Ҁ ס˼ toRoman OutOfRangeError ȗ졒 ˅һ 첔һ ը.
3 򹹂 ̉ , unittest һ ˼ ׸ һ ˼ ̡ ۼ 篫.
4 ˟̹˞, ׸ ܂ Ҁ ˞ һ˂ ׸ 汉 ը. ׸ , unittest ȍ . ܫ assertEqual assertRaises ˼, assertXYZ ޸ ̉, ݵ 禡 졁һ ˼ ȗ է ը ܂. ׸졒 ˅ ˼ ׸ ̹ 첔һ ݵ ˂ . , testFromRomanCase ޸ (“fromRoman˼ ը 碹 Ղ”) , Ҁ numeral.upper() ̉ AttributeError ױ ը뵴, toRoman˼ ը ՠ챉 ˅һ ը. һ testZero (“toRoman˼ 0 ܂ Ղ”) , Ҁ fromRoman ̉ assertRaises ̣ ˅ InvalidRomanNumeral ը.

5.7. roman.py, 2

roman ݵ˂ 篼 Ղ, 󹼂졒 ׸ ҄ ̡.

Example 5.9. roman2.py

, ˼ ꡜ ̴ ȍ ϵ ˅ (Windows, UNIX, Mac OS).

"""Convert to and from Roman numerals"""

#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass

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

def toRoman(n):
    """convert integer to Roman numeral"""
    result = ""
    for numeral, integer in romanNumeralMap:
        while n >= integer:      2
            result += numeral
            n -= integer
    return result

def fromRoman(s):
    """convert Roman numeral to integer"""
    pass
1 romanNumeralMap ˂ ˂:
  1. ˌ ˞ ˂ ը . ˼ -ը 즗 ; , CM ˼, -ը ˂ (“1000 100˼ ”); ˼ ˂ toRoman .
  2. ˂ . ס굎˼, M I, ֹ ˂ һе.
  3. ˂ . ȼ˂ ˼ (numeral, value)˂ .
2 ˂ Ҽ낄 禡 ȫ , Ҁ ȩ  챉 ˤ Ғ 汉 ը. 챉 ˤ, romanNumeralMap ՠՂ ˂ һ ˼ ˌ ۹ ̣ . է, ̉˂ ̧졒, ȗ˂ ۹ ǫ, ՠՂ.

Example 5.10. How toRoman works

ȡ toRoman ˧ , print йը while ˂ ̧즗:

        while n >= integer:
            result += numeral
            n -= integer
            print 'subtracting', integer, 'from input, adding', numeral, 'to output'
>>> import roman2
>>> roman2.toRoman(1424)
subtracting 1000 from input, adding M to output
subtracting 400 from input, adding CD to output
subtracting 10 from input, adding X to output
subtracting 10 from input, adding X to output
subtracting 4 from input, adding IV to output
'MCDXXIV'

toRoman ˞ ˂ Ы, . һ ס ׸ ? ˸ , ˟˞ .

Example 5.11. Output of romantest2.py against roman2.py

fromRoman should only accept uppercase input ... FAIL
toRoman should always return uppercase ... ok                  1
fromRoman should fail with malformed antecedents ... FAIL
fromRoman should fail with repeated pairs of numerals ... FAIL
fromRoman should fail with too many repeated numerals ... FAIL
fromRoman should give known result with known input ... FAIL
toRoman should give known result with known input ... ok       2
fromRoman(toRoman(n))==n for all n ... FAIL
toRoman should fail with non-integer input ... FAIL            3
toRoman should fail with negative input ... FAIL
toRoman should fail with large input ... FAIL
toRoman should fail with 0 input ... FAIL
1 toRoman˼, ȍ, ȗ ը ՠ, Ҁ ˂ romanNumeralMap ը ˂챉 ը. ׸ һ .
2 ˅: toRoman ﹚˂ ՚˟˼ ׸ . 즗, ס˼ ֡˞皫 , һ ס˼ ˼ 皡, ﹚˂ . ւ﵂ ˼ -ը˂ ݵ Ȑ̉ 嵎, ˌ (3999), ˌ (3888) Ȑ̉ . , ﹚ ҅ ˅ ˼ 塻 Ғ˞ 騸 ˅.
3 ﹚ һ “” ; ס˼ ݵ Ҽ˞ һ ׸ ܂. ס˼ ‚, Ҁ һ 뵵 ւ ը. ׸ (assertRaises ) ۂ 첔һ , ס굎 է́ . ˸ .

׸˂ ̉塇 ˅, ݵ ܵ ȍ һЂ. 10˂ 킄.


======================================================================
FAIL: fromRoman should only accept uppercase input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 156, in testFromRomanCase
    roman2.fromRoman, numeral.lower())
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with malformed antecedents
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 133, in testMalformedAntecedent
    self.assertRaises(roman2.InvalidRomanNumeralError, roman2.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with repeated pairs of numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 127, in testRepeatedPairs
    self.assertRaises(roman2.InvalidRomanNumeralError, roman2.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with too many repeated numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 122, in testTooManyRepeatedNumerals
    self.assertRaises(roman2.InvalidRomanNumeralError, roman2.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 99, in testFromRomanKnownValues
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
======================================================================
FAIL: fromRoman(toRoman(n))==n for all n
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 141, in testSanity
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
======================================================================
FAIL: toRoman should fail with non-integer input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 116, in testDecimal
    self.assertRaises(roman2.NotIntegerError, roman2.toRoman, 0.5)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: NotIntegerError
======================================================================
FAIL: toRoman should fail with negative input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 112, in testNegative
    self.assertRaises(roman2.OutOfRangeError, roman2.toRoman, -1)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: OutOfRangeError
======================================================================
FAIL: toRoman should fail with large input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 104, in testTooLarge
    self.assertRaises(roman2.OutOfRangeError, roman2.toRoman, 4000)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: OutOfRangeError
======================================================================
FAIL: toRoman should fail with 0 input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage2\romantest2.py", line 108, in testZero
    self.assertRaises(roman2.OutOfRangeError, roman2.toRoman, 0)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: OutOfRangeError
----------------------------------------------------------------------
Ran 12 tests in 0.320s

FAILED (failures=10)

5.8. roman.py, 3

toRoman (1 3999˂ ۹˂) ˼ 騂 Ղ, ס ( ݵ ) һ 塻뵵 騂 쵵 ̡.

Example 5.12. roman3.py

, ˼ ꡜ ̴ ȍ ϵ ˅ (Windows, UNIX, Mac OS).

"""Convert to and from Roman numerals"""

#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass

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

def toRoman(n):
    """convert integer to Roman numeral"""
    if not (0 < n < 4000):                                             1
        raise OutOfRangeError, "number out of range (must be 1..3999)" 2
    if int(n) <> n:                                                    3
        raise NotIntegerError, "decimals can not be converted"

    result = ""                                                        4
    for numeral, integer in romanNumeralMap:
        while n >= integer:
            result += numeral
            n -= integer
    return result

def fromRoman(s):
    """convert Roman numeral to integer"""
    pass
1 ˼ ˅ : տ . ˼ 'if not ((0 < n) and (n < 4000))' , һ ס ܱ . ˼ ˂ ՟ˤ , ס˼ , ˸, 0 塻 һւ .
2 ˼ raise йը 皡 ӫ. ˼ ˌ ˅ ˂ ̋ ˅. տ Ҽ, ޸皫, ˞; 煲翫, ̉嵂 ̧˞ ޸ ס˼ ̉嵃 .
3 ˼ ˂ . 񹚫 .
4 ﹚˂ һ皫 .

Example 5.13. Watching toRoman handle bad input

>>> import roman3
>>> roman3.toRoman(4000)
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
  File "roman3.py", line 27, in toRoman
    raise OutOfRangeError, "number out of range (must be 1..3999)"
OutOfRangeError: number out of range (must be 1..3999)
>>> roman3.toRoman(1.5)
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
  File "roman3.py", line 29, in toRoman
    raise NotIntegerError, "decimals can not be converted"
NotIntegerError: decimals can not be converted

Example 5.14. Output of romantest3.py against roman3.py

fromRoman should only accept uppercase input ... FAIL
toRoman should always return uppercase ... ok
fromRoman should fail with malformed antecedents ... FAIL
fromRoman should fail with repeated pairs of numerals ... FAIL
fromRoman should fail with too many repeated numerals ... FAIL
fromRoman should give known result with known input ... FAIL
toRoman should give known result with known input ... ok 1
fromRoman(toRoman(n))==n for all n ... FAIL
toRoman should fail with non-integer input ... ok        2
toRoman should fail with negative input ... ok           3
toRoman should fail with large input ... ok
toRoman should fail with 0 input ... ok
1 toRoman˼ ˟ ׸ 嵴, ס˼ 疡 . 2 ݵ ׸ ˟ , ˂ 򵌫 ǡ굵 .
2 һ ˼ ˂ ݵ һ ׸ . ׸, testDecimal˼ int(n) <> n˂ ը . 񹚡 toRoman ҄ , int(n) <> n ס 貮ۡ NotIntegerError 嵴, ס testDecimal ˅ .
3 ׸, testNegative not (0 < n < 4000) ը , ס˼ OutOfRangeError 嵴, ס testNegative ˅ .

======================================================================
FAIL: fromRoman should only accept uppercase input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 156, in testFromRomanCase
    roman3.fromRoman, numeral.lower())
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with malformed antecedents
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 133, in testMalformedAntecedent
    self.assertRaises(roman3.InvalidRomanNumeralError, roman3.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with repeated pairs of numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 127, in testRepeatedPairs
    self.assertRaises(roman3.InvalidRomanNumeralError, roman3.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with too many repeated numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 122, in testTooManyRepeatedNumerals
    self.assertRaises(roman3.InvalidRomanNumeralError, roman3.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 99, in testFromRomanKnownValues
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
======================================================================
FAIL: fromRoman(toRoman(n))==n for all n
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 141, in testSanity
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
----------------------------------------------------------------------
Ran 12 tests in 0.401s

FAILED (failures=6) 1
1 6 ˂ 킄, ׵ ݵ fromRoman ¡: ׸, ˂ Ҽ˞ һ ׸, ը , ȡ . ס˼ toRoman ˅ ݵ ׸ ˂킄. (ס˼ ȡ 衜 ¡, һ ס˼ fromRoman 󹼵 , ס˼ 󹼵 .) ס˼ 皱 toRoman 򵝂챉 ̧ ˂킄. ӱ, , “ ” ̧˞ 赵 ݵ 皂즗. 皂즗. 皱. Ҳ稦.
Note
֡˞ ׸ ˅ ˌ ˼ ̧. ﹚ ˤ ݵ ׸ , ﹚ 򵝂 皂즗. ˟̹ ݵ ݵ ׸ , ݵ 򵝂 皂즗.

5.9. roman.py, 4

toRoman 칼Ղ, fromRoman 򵝂 ̡. Ҽ˞ ۹ 煫 ˂ Ҽ낄 , ˼ toRoman ﹚ 坡 .

Example 5.15. roman4.py

, ˼ ꡜ ̴ ȍ ϵ ˅ (Windows, UNIX, Mac OS).

"""Convert to and from Roman numerals"""

#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass

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

# toRoman function omitted for clarity (it hasn't changed)

def fromRoman(s):
    """convert Roman numeral to integer"""
    result = 0
    index = 0
    for numeral, integer in romanNumeralMap:
        while s[index:index+len(numeral)] == numeral: 1
            result += integer
            index += len(numeral)
    return result
1 ˂ ܁˼ toRoman . (̵˂ ) , ˌ ҙ˼ ۹ , “ˌ ҙ˼” ը .

Example 5.16. How fromRoman works

ȡ fromRoman 킯 , print йը while ˂ ̧즗:

        while s[index:index+len(numeral)] == numeral:
            result += integer
            index += len(numeral)
            print 'found', numeral, ', adding', integer
>>> import roman4
>>> roman4.fromRoman('MCMLXXII')
found M , adding 1000
found CM , adding 900
found L , adding 50
found X , adding 10
found X , adding 10
found I , adding 1
found I , adding 1
1972

Example 5.17. Output of romantest4.py against roman4.py

fromRoman should only accept uppercase input ... FAIL
toRoman should always return uppercase ... ok
fromRoman should fail with malformed antecedents ... FAIL
fromRoman should fail with repeated pairs of numerals ... FAIL
fromRoman should fail with too many repeated numerals ... FAIL
fromRoman should give known result with known input ... ok 1
toRoman should give known result with known input ... ok
fromRoman(toRoman(n))==n for all n ... ok                  2
toRoman should fail with non-integer input ... ok
toRoman should fail with negative input ... ok
toRoman should fail with large input ... ok
toRoman should fail with 0 input ... ok
1 ˂ һ ˅. 填 fromRoman ˼ , ˞ ׸ , .
2 填 ˂ ȡ (sanity check) . ׸ ͂, toRoman fromRoman , ݵ ˼ ˞ Ғ˞ 騸 ˅. ( ˌ皫 ; ˞ ˼ toRoman ՚ 皡 ˅ ˂ ˧ Ȑ̉ ˅ , fromRoman ȗ̉˞ ՚ 皡 ˅, toRoman ˞ ๼ ˂ ͡ 騂 顡, ˧ ۹ Ȑ̉皵 . ˂ 禡 , ˼ ˅; , ס ֡˞ ׸ 󹼂즗.)

======================================================================
FAIL: fromRoman should only accept uppercase input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 156, in testFromRomanCase
    roman4.fromRoman, numeral.lower())
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with malformed antecedents
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 133, in testMalformedAntecedent
    self.assertRaises(roman4.InvalidRomanNumeralError, roman4.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with repeated pairs of numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 127, in testRepeatedPairs
    self.assertRaises(roman4.InvalidRomanNumeralError, roman4.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with too many repeated numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 122, in testTooManyRepeatedNumerals
    self.assertRaises(roman4.InvalidRomanNumeralError, roman4.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
----------------------------------------------------------------------
Ran 12 tests in 1.222s

FAILED (failures=4)

5.10. roman.py, 5

fromRoman ˼ ˞ Ղ, ˂ 禡 ̧ ҅ ̡: һ ס ˞ 쵵 . ס˼ ը վ ̣ ˂ ס ۂ վ ̣ ˂킄. ˼ toRoman ˞ ᮫, һ ˸ ȍ ˅ 傄 ˅: ۱ .

۱ ւ 졒 ۱ 101 , 皱 ˼ .

ˌ˂ ՠ , һ ˂  ˅. տ填 ̵ˤ , ˅, ˂ M ը ˂ .

Example 5.18. Checking for thousands

>>> import re
>>> pattern = '^M?M?M?$'       1
>>> re.search(pattern, 'M')    2
<SRE_Match object at 0106FB58>
>>> re.search(pattern, 'MM')   3
<SRE_Match object at 0106C290>
>>> re.search(pattern, 'MMM')  4
<SRE_Match object at 0106AA38>
>>> re.search(pattern, 'MMMM') 5
>>> re.search(pattern, '')     6
<SRE_Match object at 0106F4A8>
1 ܁˼ ˂ 翫:
  1. ^ - ը˂ ˫ . ۵ , ܁˼ M ը ˅ , ס˼ . M ը, ˍ챉 , ը˂ ˅ 챉 .
  2. M? - ˞ ˂ M ը . տ ՠյՂ, 0 3ұ˂ M ը ˅ һ ́ӫ.
  3. $ - ը˂ ͹ ˅ . ˂ ^ ը ͵, ˼ ܁ ˟̹ ը, M ը˂ һ ը, .
2 re ݵ˂ ˼ search ﹚, ס˼ ۱ (pattern) ը('M') ̑ ۱ 皲 硒 ̵. է, search һ˂ ̹ ՠ; ̹ 煫 ޸ 翫; է , search ˂ , None ՠ. ( ס ˅ ), search ՠ ̹ ˞ 皫 . Ҁ 皫 ݵ ˼ ܁ ˂ ը, ס search˂ ՠ ȓ븇 Ȃ ˅. 'M' ۱ , Ҁ տ ˞ M 졒 տ տ˂ ˞ M ը˼ Ǹ̵ ը.
3 'MM' Ҁ տ տ ˞ M ը 졒 տ M˼ Ǹ̵ ը.
4 'MMM'˼ Ҁ ˂ ݵ M ը 챉 ը.
5 'MMMM'˼ . ˂ ݵ M ը , һ һ ۱ ˼ ($ ը ը) ը˂ , ը˼ (տ˂ M ը) һ . search None ՠ.
6 ˍ˅ﵵ, ը ۱ , Ҁ ݵ M ը ˞ ը. ȍ 범; ס˼ ˸ .

Վ˂ ̵˂ ᮫, Ҁ, , ȗ Տ˞ ˅ վൎ ˅ ը.

˂ ܁ ˅:

  1. CM
  2. CD
  3. 0 3 ˂ C ը ( Վ˂ 0 0)
  4. D, ˸ 0 3 ˂ C ը

˂ ܁˼ 禂͵ ˅:

Example 5.19. Checking for hundreds

>>> import re
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)$' 1
>>> re.search(pattern, 'MCM')            2
<SRE_Match object at 01070390>
>>> re.search(pattern, 'MD')             3
<SRE_Match object at 01073A50>
>>> re.search(pattern, 'MMMCCC')         4
<SRE_Match object at 010748A8>
>>> re.search(pattern, 'MCMC')           5
>>> re.search(pattern, '')               6
<SRE_Match object at 01071D98>
1 ܁˼, ը˂ 졒 (^), Վ˂ (M?M?M?) ˟˂ ˂ ܁졜 󂄫. , 飲, ț 翫, ס˼ 쫆˂ ˂ ȗ Տ˞ ܁, ᫑ , ˂: CM, CD, D?C?C?C? (ס˼ ˞ DЫ˸ 0 3ұ˂ ˞ C ը ). ۱ (PARSER) ܁˂ ( ) Ы , տ ̑졒, һ皫 Ǹ̂.
2 'MCM'˼ Ҁ տ M 졒, տ տ M ը˼ Ǹ̵ ը, CM˼ ( CD D?C?C?C? ܁˼ ). MCM˼ 1900˂ .
3 'MD' Ҁ տ M 챉 ը, տ տ M ը˼ Ǹ̵, D?C?C?C? ܁˼ D ( 3˂ C ը˼ ˞ Ǹ̵). MD 1500˂ . 'MD' Ҁ տ M 졒, տ տ M ը Ǹ̵ ը, D?C?C?C? ܁˼ D (3˂ C ը ˼ ˞ Ǹ̵). MD 1500˂ .
4 'MMMCCC' Ҁ 3˂ M ը ݵ 챉 ը, D?C?C?C? ܁˼ CCC (D ˞ Ǹ̵) MMMCCC 3300˂ . 'MMMCCC'˼ Ҁ 3˂ ݵ M ը 챉 ը, D?C?C?C? ܁˼ CCC ( D ˞ Ǹ̵). MMMCCC 3300˂ .
5 'MCMC' . տ M˼ , տ տ M ը Ǹ̵, CM˼ , һ 'MCMC' . տ M˼ , տ տ M ը˼ Ǹ̵, CM˼ , һ $ 嵴 Ҁ ը˂ 忂 ը ( ˟ ˼ C ը 翫). C D?C?C?C? ܁˂ , Ҁ ȗ Տ˞ CM ܁ ը.
6 ˍ˅ﵵ, ը˼ ˟ ܁ , Ҁ ݵ M ը˼ ˞ Ǹ̵ ը, ը˼ D?C?C?C? ܁ Ы ݵ ը ˞ Ǹ̵ ը.

! ۱ һ ˅ ? Վ˂ ̵˂ ፲ . ( ˌ˂ ՠ, ۱ 󹼂 ը , ˉ챉 , ˞ ס˼ ˂ - ը .) 􂈸᫡ﵵ, ݵ , ˂ ˂ , Ҁ ס굎˼ ˼ ܁ ը.

Example 5.20. roman5.py

, ˼ ꡜ ̴ ȍ ϵ ˅ (Windows, UNIX, Mac OS).

"""Convert to and from Roman numerals"""
import re

#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass

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

def toRoman(n):
    """convert integer to Roman numeral"""
    if not (0 < n < 4000):
        raise OutOfRangeError, "number out of range (must be 1..3999)"
    if int(n) <> n:
        raise NotIntegerError, "decimals can not be converted"

    result = ""
    for numeral, integer in romanNumeralMap:
        while n >= integer:
            result += numeral
            n -= integer
    return result

#Define pattern to detect valid Roman numerals
romanNumeralPattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$' 1

def fromRoman(s):
    """convert Roman numeral to integer"""
    if not re.search(romanNumeralPattern, s):                                    2
        raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s

    result = 0
    index = 0
    for numeral, integer in romanNumeralMap:
        while s[index:index+len(numeral)] == numeral:
            result += integer
            index += len(numeral)
    return result
1 ˼ , ̵˂ Վ˂ 쫿 ܁˂ ¹. ˂ XC (90), XL (40)һ , ˼ ˞ L ˸ 0 3ұ˂ ˞ X ը . ˂ IX (9), IV (4)һ, ˼ ˞ V ˸ 0 3ұ˂ ˞ I ը .
2 ݵ Ғ ˂ ۱ 򵝂 ҅Ղ, 򵌫 ȍ . re.search ̹ ՠ, ۱ ˼ 졒 ˂ ˼ ; , ˂ ˼ .

, ొ ۱ ݵ ˂ һւ ˅ ˼ ˂ . һ ݫ , ȓ :

Example 5.21. Output of romantest5.py against roman5.py


fromRoman should only accept uppercase input ... ok          1
toRoman should always return uppercase ... ok
fromRoman should fail with malformed antecedents ... ok      2
fromRoman should fail with repeated pairs of numerals ... ok 3
fromRoman should fail with too many repeated numerals ... ok
fromRoman should give known result with known input ... ok
toRoman should give known result with known input ... ok
fromRoman(toRoman(n))==n for all n ... ok
toRoman should fail with non-integer input ... ok
toRoman should fail with negative input ... ok
toRoman should fail with large input ... ok
toRoman should fail with 0 input ... ok

----------------------------------------------------------------------
Ran 12 tests in 2.864s

OK                                                           4
1 ۱ ȡ ݱނ ˼ 皫, ˞, ۱ ˼ ը-롬 . ˂ ۱ romanNumeralPattern ը Ղ, ˂ re.search ˼ ˟ ը 嵵 . ˂ ը ׸ .
2 ﵵ, ˂ һ ׸ . , ొ ȗ ׸ MCMC ˼ 肄. , ˼ ˂ ۱ , fromRoman˼ InvalidRomanNumeralError ӫ, ס˼ ొ ȗ ׸ , ׸ .
3 ȍ, ݵ һ ׸ . ۱ ˼ ׸ ࡢ ˅ ݵ һւ .
4 ̂˂ ̅ ȗ˼ “OK” ղ ; ס˼ ݵ ׸ unittest ݵ ˂ ̉.
Note
˂ ݵ ׸ , 皂즗.

5.11. ՚ 챉

̅ ֡˞ ׸ 󹼂硒 ˸ 졒, ՚׫ 첔Ы. һ “bug” Dz ˂? ՚׫ ֱ 󹼂 ˞ ׸ .

Example 5.22. The bug

>>> import roman5
>>> roman5.fromRoman("") 1
0
1 ? ˟˂ ը 챉 ˤ ۱ . , ˼ ˼ ۱ ˂ ̅ ՚˟ ˟ . ס˼ ՚; ¹˞ ը򵎡 坡 InvalidRomanNumeralError , ը .

՚ ˍȐ , ס ۂ챉 ˟, ˼ , ՚ ո̂ 煫 ׸ 󹼂 .

Example 5.23. Testing for the bug (romantest61.py)

class FromRomanBadInput(unittest.TestCase):

    # previous test cases omitted for clarity (they haven't changed)

    def testBlank(self):
        """fromRoman should fail with blank string"""
        self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, "") 1
1 ˍᇡ ˅. fromRoman ը ̉졒 ס InvalidRomanNumeralError