SPQR

I’ve been programming Perl for a while now, but I’ve seen lot of people around me (even Perl enthusiasts) getting very excited about Python.

In Newbay it’s used mainly for automated and performance tests, but some times also for some system engineering automation.

I can understand them, Python seems to be a pretty cool language, very clear and rigorous when compared to Perl, which tend to be messy (depending on the coder).

Python seems also to have more stuff into the default distribution which comes pre-installed on every Linux/Unix box. You already have http and xml libraries without the necessity of installing stuff (with Perl for example you don’t have LWP or any XML dom library by default and you have to install them through CPAN or your packaging system).

Also, with Jython is easier to interface with Java based stuff like JMS, etc. In the pat I’ve used some awful hacks using Inline::Java , they were working but, you know, they looked really scary.

I still do like Perl though, for doing simple and quick hacks, one-liners, etc.And I will probably continue to use it.

I’m currently reading this Diving into Python book, which is free to download or view online.

My colleague Brano has always tried to evangelize me, with no success so far (because of my chronic laziness), but today he gave me a nice puzzle which took me a couple of hours to solve 😛

I’m sharing it with you just in case you fancy the idea to solve it too. Feel free to send me the result at
pallotron (at) freaknet (dot) org 🙂

It’s called roman.py, it contains two functions called roman2dec and dec2roman, they should convert integers into roman numbers and viceversa.

Brano gave it to me with the excuse that, because I’m Italian, I should be pretty used with romans numbers and it wouldn’t take too much to solve it 😛

I started in the afternoon while at work, it took a 1 hour and half to me to figure out the algo for the dec2roman function, I don’t consider myself a math genius so excuse me if I was slow 😛

The roman2dec was a bit of a challenge, but after 3 glasses of nice white wine (Corvo Glicine) I started seen the pattern and I’ve nailed it 🙂

In vino veritas! ahaha!

You have to write the code of the two functions so that the all unit tests pass (there are also negatives tests that you should take care of), here is the code. Just copy paste in a file called roman.py and execute it with python roman.py, enjoy!

I’ll publish my script in a week or so (just to give you the time to play with it, PLEASE DO NOT CHEAT 🙂 ).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import unittest
 
# your global variables can go here, too ;)
 
def roman2dec(roman):
    assert isinstance(roman, str), 'Expected string argument'
    # your code goes here
 
    return None
 
def dec2roman(dec):
    assert isinstance(dec, int), 'Expected integer argument'
    # your code goes here
 
    return None
 
class RomanTests(unittest.TestCase):
 
    def testSimple(self):
        self.assertEqual(roman2dec('XXIV'), 24)
        self.assertEqual(roman2dec('mcmxcix'), 1999)
        self.assertEqual(dec2roman(13), 'XIII')
        self.assertEqual(dec2roman(78), 'LXXVIII')
 
    def testInvalid(self):
        self.assertRaises(ValueError, roman2dec, 'IIII')
        self.assertRaises(ValueError, roman2dec, 'VX')
        self.assertRaises(ValueError, roman2dec, 'VV')
        self.assertRaises(ValueError, roman2dec, 'IC')
        self.assertRaises(ValueError, dec2roman, -1)
 
    def testExhaustive(self):
        for i in range(4000):
            self.assertEqual(roman2dec(dec2roman(i)), i)
 
    def testExhaustive2(self):
        letters = ('I', 'V', 'X', 'L', 'C', 'D', 'M')
        first_example = None
        count = 0
        for i in xrange(8*8*8*8*8):
            rom = ''
            while True:             # do { ... } while i>0
                rom += letters[i % 7]
                i /= 7
                if i<=0: break
 
            try:
                dec = roman2dec(rom)
                roman = dec2roman(dec)
                if rom <> roman:
                    if first_example is None:
                        first_example = '%s != %s (decimal %d)' % (rom, roman, dec)
                    count += 1
            except ValueError, e:
                pass
 
        self.assertEqual(count, 0, 'Accepted %d invalid roman numbers (e.g. %s)' % (count, first_example))
 
if __name__ == '__main__':
    unittest.main()

[Update]:

There much more invalid roman numbers that you have to check, the quiz has been updated above :P

Comments Posted in English posts, Informatica, Linux, Programmazione
Tagged , , , , , ,

Comments

  1. lookee says:

    This is my solution patched with the suggested validation.

    at the end of the roman2dec function:


    if roman != dec2roman(dec) : raise ValueError

    http://paste.debian.net/103225 (never expires)

    I’ll work on other independend validation algorithm.

    1. pallotron says:

      I see the laziness has caught us all 😀
      i did the same, i need to find some time to work out a proper validation check 😛

  2. Andrea Spadaccini says:

    🙂 what do you mean by “atomic bombs?” Do you refer to regex? I encoded most of the knowledge of the conversion in the regex and in the table, in order to make the code clear and rid of special cases.

  3. pallotron says:

    ahaha, you have used atomic bombs to tackle the problem 😛

  4. Andrea Spadaccini says:

    @Andrea Spadaccini – I changed the code, but only to fix variable names (I misnamed hundreds as thousands -_-)

  5. Andrea Spadaccini says:

    @pallotron – yes, it does.. I just did copy and paste from the article 🙂

  6. Andrea Spadaccini says:

    BTW, thanks for making me develop code using TDD for the first time! 🙂

  7. Andrea Spadaccini says:

    Ecco la mia soluzione, ora spulcio le vostre!
    http://pastebin.com/yWPQgKdM

    1. pallotron says:

      Did you try to execute your code using the last version of the test unit? (i’ve modified the script attached to the article) 🙂

      Does it pass testExhaustive2?

  8. pallotron says:

    Here is my new version, to deal with all the possible invalid roman numbers, probably the solution is just stupid, i’ve put this at the end of the roman2dec function:


    if roman <> dec2roman(result): raise ValueError

    i just do a cross-check, because i know 100% that the dec2roman is correct, if the two results are different there is something wrong in the input of roman2dec. but again this is probably cheating 😛

    new solution here:

    http://paste.debian.net/102793/ (never expires)

  9. pallotron says:

    Hey lookee,

    i’ve updated the test with some more checks… there are lot of invalid roman numbers than i thought 😛
    run your solution against it 🙂
    I had to modify mine to pass the new tests 🙂

  10. lookee says:

    here it is my solution:
    (it expires in 72h)
    http://paste.debian.net/102791/

  11. pallotron says:

    @antonio: nice 🙂

    Your roman2dec solution is better than mine 🙂
    But i think my dec2roman is more concise:

    http://paste.debian.net/102723/

  12. antonio says:

    my python is awful, but anyway 😀

    (it expires in 72h)
    http://paste.debian.net/102722/

  13. pallotron says:

    @nitto – Traducitelo con google translate 😀

  14. nitto says:

    ‘n italiano pleaze.
    su ‘non ci a fai, m’accuntento macari ro siciliano.