home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyth_os2.zip / python-1.0.2 / Demo / lutz / roman.py < prev    next >
Text File  |  1994-02-04  |  14KB  |  633 lines

  1. #
  2. #          R O M A N   N U M E R A L S   (python version)
  3. #
  4. #  This program takes Arabic numerals from standard input and writes
  5. #  the corresponding Roman numerals to standard outout.
  6.  
  7.  
  8.  
  9. ##########################################################################
  10. # the icon version:
  11. #
  12. # procedure main()
  13. #    local n
  14. #    while n := read() do
  15. #       write(roman(n) | "cannot convert")
  16. # end
  17. # procedure roman(n)
  18. #    local arabic, result
  19. #    static equiv
  20. #    initial equiv := ["","I","II","III","IV","V","VI","VII","VIII","IX"]
  21. #    integer(n) > 0 | fail
  22. #    result := ""
  23. #    every arabic := !n do
  24. #       result := map(result,"IVXLCDM","XLCDM**") || equiv[arabic+1]
  25. #    if find("*",result) then fail else return result
  26. # end
  27. #
  28. ##########################################################################
  29.  
  30.  
  31.  
  32. import sys
  33. from string import *            # atoi unqualified
  34.  
  35.  
  36.  
  37. # the main driver
  38. #
  39.  
  40.  
  41. def romans(converter):
  42.     while 1:
  43.         roman = converter(raw_input('enter number: '))
  44.         if roman != None: 
  45.             print roman
  46.         else: 
  47.             print 'cannot convert'
  48.  
  49.  
  50.  
  51.  
  52. #
  53. # simplest approach: implement map() inline;
  54. # note: 'equiv' could be global in the module for 'static'
  55. #
  56.  
  57.  
  58.  
  59. def roman1(n):
  60.    equiv = ['','I','II','III','IV','V','VI','VII','VIII','IX']        
  61.    if atoi(n) <= 0: return None
  62.    result = ''
  63.    for arabic in n:
  64.        new = ''
  65.        key, to = 'IVXLCDM*', 'XLCDM***'       # extra '*' or index fails
  66.        for x in result:
  67.            new = new + to[index(key, x)]
  68.        result = new + equiv[atoi(arabic)]     # atoi needed here
  69.    if '*' in result: return None
  70.    return result
  71.  
  72.  
  73.  
  74.  
  75. #
  76. # use a dictionary for mapping
  77. #
  78.  
  79.  
  80.  
  81. def roman2(n):
  82.     
  83.     dig = {'0':'',  '1':'I',  '2':'II',  '3':'III',  '4':'IV', \
  84.            '5':'V', '6':'VI', '7':'VII', '8':'VIII', '9':'IX'}  
  85.            
  86.     map = {'I':'X', 'V':'L', 'X':'C', \
  87.            'L':'D', 'C':'M', 'D':'*', 'M':'*', '*':'*'}                    
  88.     
  89.     if atoi(n) <= 0: return None
  90.     result = ''
  91.     for arabic in n:
  92.         new = ''; 
  93.         for x in result: new = new + map[x]
  94.         result = new + dig[arabic]
  95.     if '*' in result: return None
  96.     return result
  97.  
  98.  
  99.  
  100.  
  101. #
  102. # make our own map();  could add to string.py;
  103. # note: this is the best comparison to the icon version;
  104. # note: can't change strings directly in python;
  105. #
  106.  
  107.  
  108.  
  109. def map(string, key, to):
  110.     result = ''
  111.     for x in string:
  112.         result = result + to[index(key, x)]
  113.     return result
  114.  
  115.  
  116. def roman3(n):
  117.     dig = ['','I','II','III','IV','V','VI','VII','VIII','IX']        
  118.     if atoi(n) <= 0: return None
  119.     result = ''
  120.     for arabic in n:
  121.         result = map(result, 'IVXLCDM*', 'XLCDM***') + dig[atoi(arabic)]
  122.     if '*' in result: return None
  123.     return result
  124.  
  125.  
  126.  
  127.  
  128. #
  129. # alternative ways to map(): slices and (of course) recursion
  130. #
  131.  
  132.  
  133.  
  134. def map2(string, key, to):
  135.     result = string
  136.     for i in range(len(string)):
  137.         result = result[:i] + to[index(key,string[i])] + result[i+1:]
  138.     return result
  139.  
  140.  
  141.  
  142. def map3(string, key, to):
  143.     if string == '':
  144.         return ''
  145.     else:
  146.         return to[index(key, string[0])] + map3(string[1:], key, to)
  147.  
  148.  
  149.  
  150.  
  151. #
  152. # do the lisp thing: convert strings<->lists for mapping
  153. #
  154.  
  155.  
  156.  
  157. def explode(string):
  158.     list = []
  159.     for x in string: list.append(x)
  160.     return list
  161.  
  162. def implode(list):
  163.     string = ''
  164.     for x in list: string = string + x
  165.     return string
  166.  
  167.  
  168. def tuple_implode(list):
  169.     tuple = ()
  170.     for x in list: tuple = tuple + (x,)
  171.     return tuple
  172.  
  173. def tuple_explode(tuple): return explode(tuple)
  174.  
  175.  
  176.  
  177.  
  178.  
  179. #####################################################
  180. # generic sequence conversion, via function pointers;  
  181. # 3 cases suffice for all sequence conversion cases;
  182. #
  183. # ie, only target sequence type matters, since 'in'
  184. # can iterate through any of the 3 types generically
  185. # but need different start/singleton constructors;
  186. #
  187. # note: can't use 'in' or '+' with dictionaries;
  188. #####################################################
  189.  
  190.  
  191.  
  192. def seqcon1(source, start, single):
  193.     result = start
  194.     for x in source: result = result + single(x)
  195.     return result
  196.  
  197.  
  198. def explode2(string):      seqcon1(string, [], list)   # string->list 
  199. def tuple_explode2(tuple): explode2(tuple)             # tuple->list 
  200. def implode2(list):        seqcon1(list, '', string)   # list->string
  201. def tuple_implode2(tuple): implode2(tuple)             # tuple->string 
  202. def list_tuple(list):      seqcon1(list, (), tuple)    # list->tuple
  203. def string_tuple(string):  list_tuple(string)          # string->tuple
  204.  
  205.  
  206. def list(x):   return [x]
  207. def string(x): return x
  208. def tuple(x):  return (x,)
  209.  
  210.  
  211.  
  212.  
  213.  
  214.  
  215.  
  216. #################################################
  217. # generic, using target switch logic or tables
  218. #################################################
  219.  
  220.  
  221.  
  222. def seqcon2(source, target):
  223.     starts = ['', [], ()]
  224.     single = [string, list, tuple]
  225.     result = target
  226.     for x in source: result = result + single[starts.index(target)](x)
  227.     return result
  228.  
  229.  
  230. def seqcon3(source, target):
  231.     config = {'string':('',string), 'list':([],list), 'tuple':((),tuple)}
  232.     result = config[target][0]
  233.     for x in source: result = result + config[target][1](x)
  234.     return result
  235.  
  236.  
  237. def seqcon4(source, target):
  238.     result = target
  239.     for x in source: 
  240.         if   target == '': result = result + x
  241.         elif target == []: result = result + [x]
  242.         elif target == (): result = result + (x,)
  243.     return result
  244.  
  245.  
  246. def seqcon5(source, target):
  247.     config = {'string':('','x'), 'list':([],'[x]'), 'tuple':((),'(x,)')}
  248.     result = config[target][0]
  249.     for x in source: result = result + eval(config[target][1])
  250.     return result
  251.  
  252.  
  253. def seqcon6(source, target):
  254.     start, single = \
  255.         {'string':('','x'), 'list':([],'[x]'), 'tuple':((),'(x,)')}[target]
  256.     result = start
  257.     for x in source: result = result + eval(single)
  258.     return result
  259.  
  260.  
  261.  
  262.  
  263.  
  264. ########################################################
  265. # or use a generic sequence class;
  266. #
  267. # manages a sequence data object internally, and
  268. # keeps track of its actual type manually;
  269. #
  270. # note: methods calls require '()', so data member refs
  271. # don't look same as function member calls (without 
  272. # the '()', function members are accessed directly;
  273. #
  274. # note: the .value() method is really superfluous,
  275. # since the value can be accessed directly using
  276. # the .data member;  get/set function members
  277. # are useful iff the data interface may have to
  278. # be made functional later, or you want to set
  279. # the value with a function call [x=Obj().set(val)]
  280. #   def set(self, val): self.data = val
  281. #
  282. # expected output:
  283. #   >>> test_sequence()
  284. #   abcde
  285. #   ['a', 'b', 'c', 'd', 'e']
  286. #   zbcde
  287. #   []
  288. #   ['a', 'b']
  289. #   ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
  290. #   ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')
  291. #   ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k')
  292. #   abcdefghijk
  293. #   abcdefghijkl
  294. #   ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']
  295. #   a
  296. #   b
  297. #   ...
  298. #   k
  299. #   l
  300. ########################################################
  301.  
  302.  
  303.  
  304.  
  305.  
  306. class Sequence:
  307.     def to_string(self):
  308.         res = ''
  309.         for x in self.data: res = res + x
  310.         self.data = res
  311.     
  312.     def to_list(self):
  313.         res = []
  314.         for x in self.data: res.append(x)
  315.         self.data = res
  316.     
  317.     def to_tuple(self):
  318.         res = ()
  319.         for x in self.data: res = res + (x,)
  320.         self.data = res
  321.  
  322.     def add(self, item):
  323.         if   type(self.data) == type(''): single = item
  324.         elif type(self.data) == type([]): single = [item]
  325.         elif type(self.data) == type(()): single = (item,)
  326.         self.data = self.data + single
  327.  
  328.     def concat(self, items):
  329.         for x in items: self.add(x)         # 'in' is generic
  330.  
  331.     def value(self): return self.data
  332.  
  333.     def init(self, *value):  
  334.         if value == ():
  335.             self.data = []                  # starts as a list
  336.         else:    
  337.             self.data = value[0]            # opt value gives start type
  338.         return self
  339.  
  340.     def show(self):  
  341.         for x in self.data: print x         # indent needed here
  342.  
  343.  
  344.  
  345.  
  346. def test_sequence():
  347.     l = Sequence().init('')
  348.     l.concat('abcde')
  349.     print l.value()
  350.  
  351.     l.to_list()         # explode
  352.     print l.value()
  353.  
  354.     l.data[0] = 'z'
  355.     l.to_string()       # implode
  356.     print l.value()
  357.  
  358.     x = Sequence().init()
  359.     print x.value() 
  360.     
  361.     x.add('a'); x.add('b') 
  362.     print x.value()   
  363.     
  364.     x.concat('cd');
  365.     x.concat(['e', 'f'])                    # '+' needs same types
  366.     x.concat(('g', 'h', 'i'))
  367.     print x.value() 
  368.  
  369.     x.to_tuple();     print x.data  
  370.     x.concat('jk');   print x.data  
  371.  
  372.     x.to_string();    print x.data  
  373.     x.concat(['l']);  print x.data 
  374.  
  375.     x.to_list();      print x.data  
  376.     x.show();
  377.  
  378.  
  379.  
  380.  
  381.  
  382. ###############################################################
  383. # use specialization/inheritance;
  384. #
  385. # object type implies single/inits;
  386. # build a new object type to convert 
  387. # types (and reset virtual funcs);
  388. #
  389. # this could be used for a Sequence2:
  390. #    def init(self): return List().init()
  391. # to try and default to a List when 
  392. # a Sequence() is made; we cant' use it 
  393. # here because init() is more complex;
  394. # same expected output as test_sequence()
  395. ###############################################################
  396.  
  397.  
  398.  
  399.  
  400.  
  401. class Sequence2:
  402.     def convert(self, maker):
  403.         new = maker().init() 
  404.         new.concat(self.data)
  405.         return new
  406.  
  407.     def to_string(self): 
  408.         return self.convert(String)        # classes are 1st-class    
  409.  
  410.     def to_list(self):   
  411.         return self.convert(List)
  412.     
  413.     def to_tuple(self):  
  414.         return self.convert(Tuple)
  415.     
  416.     def concat(self, items):                # "in" is generic
  417.         for x in items: self.add(x)         # add is virtual
  418.     
  419.     def init(self, *value):                 # optional value arg
  420.         if not value:                       # '()' is false
  421.             self.data = self.empty()        # empty is virtual
  422.         else:
  423.             self.data = value[0]
  424.         return self
  425.     
  426.     def show(self): 
  427.         for x in self.data: print x
  428.  
  429.  
  430.  
  431.  
  432. class List(Sequence2):
  433.     def empty(self): 
  434.         return []  
  435.  
  436.     def add(self, item): 
  437.         self.data.append(item)                   # faster than '+ [item]'
  438.  
  439.  
  440. class String(Sequence2):
  441.     def empty(self):
  442.         return ''
  443.  
  444.     def add(self, item): 
  445.         self.data = self.data + item
  446.  
  447.  
  448. class Tuple(Sequence2):
  449.     def empty(self):
  450.         return ()
  451.  
  452.     def add(self, item): 
  453.         self.data = self.data + (item,)
  454.  
  455.  
  456.  
  457.  
  458. def test_sequence2():
  459.     l = String().init()
  460.     l.concat('abcde')
  461.     print l.data
  462.  
  463.     l = l.to_list()     # explode
  464.     print l.data
  465.  
  466.     l.data[0] = 'z'
  467.     l = l.to_string()   # implode
  468.     print l.data
  469.  
  470.  
  471.     x = List().init()                  # start as a list
  472.     print x.data                       # drop value() method 
  473.     
  474.     x.add('a'); x.add('b') 
  475.     print x.data   
  476.     
  477.     x.concat('cd');
  478.     x.concat(['e', 'f'])               # '+' needs same types
  479.     x.concat(('g', 'h', 'i'))
  480.     print x.data 
  481.  
  482.     x = x.to_tuple();  print x.data    # must assign new obj  
  483.     x.concat('jk');    print x.data  
  484.  
  485.     x = x.to_string(); print x.data  
  486.     x.concat(['l']);   print x.data 
  487.  
  488.     x = x.to_list();   print x.data  
  489.     x.show();
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496. #############################################
  497. # ok: finally, mapping via explode/implode
  498. #############################################
  499.  
  500.  
  501.  
  502. def map4(string, key, to):
  503.     list = []
  504.     for x in string:
  505.         list.append(to[index(key, x)])
  506.     return implode(list)
  507.  
  508.  
  509. def map5(string, key, to):
  510.     list = explode(string)
  511.     for i in range(len(list)):
  512.         list[i] = to[index(key, list[i])]
  513.     return implode(list)
  514.  
  515.  
  516. def map6(string, key, to):
  517.     list = explode(string); i = 0
  518.     for x in list:
  519.         list[i] = to[index(key, x)]; i = i+1
  520.     return implode(list)
  521.  
  522.  
  523. # does not work: x doesn't "share" with nodes in list
  524. #
  525. # def map7(string, key, to):
  526. #    list = explode(string)
  527. #    for x in list:
  528. #        x = to[index(key, x)]
  529. #    return implode(list)
  530.  
  531.  
  532.  
  533.  
  534.  
  535. ########################################
  536. # a few more coding variations..
  537. ########################################
  538.  
  539.  
  540.  
  541. def roman4(arab):
  542.    equiv = ['','I','II','III','IV','V','VI','VII','VIII','IX']        
  543.    if atoi(arab) <= 0: return None
  544.    res = ''
  545.    while arab != '':
  546.        new, key, to = '', 'IVXLCDM*', 'XLCDM***'
  547.        while res != '':
  548.            new, res = new + to[index(key, res[0])], res[1:]
  549.        res, arab = new + equiv[atoi(arab[0])], arab[1:]
  550.    if '*' in res: return None
  551.    return res
  552.  
  553.  
  554.  
  555.  
  556.  
  557. ##############################################################
  558. # note: illegal to ref undefined names: must 
  559. # explicitly initialize res and new to '' here;
  560. #
  561. # note: this is illegal syntax (cmp stmts require indent):
  562. #   res=''; while arab != '':
  563. #       new=''; while res != '':
  564. #           new, res = ...
  565. #
  566. # ok to subscript literals, but some methods useless:
  567. #   [1,2,3].index(3) ok (2)
  568. #   [1,2,3].append(5) means nothing (append returns no value)
  569. #   'abcd'[1] ok ('b')
  570. #   'abcd'.rjust(3) fails
  571. #   string.rjust('abcd',3) ok (strings not really a class)
  572. #   {'a':1, 'b':2}['a'] ok (1)
  573. ############################################################## 
  574.  
  575.  
  576.  
  577.  
  578. def roman5(arab):
  579.    equiv = ['','I','II','III','IV','V','VI','VII','VIII','IX']        
  580.    if atoi(arab) <= 0: return None
  581.    res = ''; 
  582.    while arab != '':
  583.        new = ''; 
  584.        while res != '':
  585.             new, res = new + 'XLCDM***'[index('IVXLCDM*',res[0])], res[1:]
  586.        res, arab = new + equiv[atoi(arab[0])], arab[1:]
  587.    if '*' in res: return None
  588.    return res
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595. #######################################################
  596. # test all varients: functions are 1st-class objects;
  597. # can test others by hand at interactive prompt:
  598. #   >>> from roman import *
  599. #   >>> seqcon(...
  600. #
  601. #   >>> import roman
  602. #   >>> roman.seqcon(...
  603. #
  604. #   >>> import pdb
  605. #   >>> pdb.run('from roman import *')
  606. #   >>> pdb.run('test_sequence2()')
  607. #
  608. # this code gets run when the module roman.py is 
  609. # imported (loaded);  it must be after the def's
  610. # above, since funcs aren't known till def runs;
  611. #######################################################
  612.  
  613.  
  614.  
  615.  
  616. def doit():
  617.     roman_funcs = [roman1, roman2, roman3, roman4, roman5]
  618.     for x in roman_funcs:
  619.         print '--running function:', roman_funcs.index(x) + 1
  620.         try: 
  621.             romans(x)
  622.         except:
  623.             print sys.exc_type + ':', sys.exc_value
  624.     
  625.  
  626.  
  627. doit()
  628.  
  629.  
  630.