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 / sets.py < prev    next >
Text File  |  1994-02-04  |  44KB  |  1,460 lines

  1. #
  2. # RelDB
  3. #
  4. # set processing in python
  5. #
  6. # defines 3 items:
  7. #   class Set  -- basic, generic set processing
  8. #   class RSet -- a Set, with relational algebra
  9. #   test_sets  -- a testing function
  10. #
  11. # a set object (Set): 
  12. # (1) is an unordered collection
  13. # (2) allows heterogeneous collections  
  14. # (3) can grow and shrink arbitrarily
  15. #
  16. # a relational set object (RSet):
  17. # (1) is a Set object
  18. # (2) supports relational algebra operations
  19. #
  20. # users can initialize and concatenate
  21. # sets with normal lists, character strings, 
  22. # tuples (in python), other sets, and other 
  23. # user-defined sequence objects;  users  
  24. # can also use any sequence type (built-in
  25. # or user-defined) on the left-hand side
  26. # of most binary set operations;
  27. #
  28. # sets are unordered collections of arbitrary 
  29. # data items: items can be a mixture of any 
  30. # data type (but some operations assume a
  31. # particular node format);  supports most 
  32. # common set operations, as well as some 
  33. # relational algebra operators (via the Rset
  34. # subclass);
  35. #
  36. # sets are implemented using Python lists (but
  37. # we could use other forms), and encapsulated in 
  38. # a class;  there is a conversion to strings; 
  39. #
  40. # there are many ways to represent relational 
  41. # database tuples and tables in Python:
  42. #   --character strings (and split fields apart)
  43. #   --simple lists ([[name,value], [name,value],...])
  44. #   --Pythin tuples ( (value,value,..) or ((name,value), (name,value),..)
  45. #   --lists with seperate name lists (['f1','f2'..], [a,b,..], [c,d,..])
  46. #   --empty classes (class T:  x = T(); x.f1=a, x.f2=b; ...)
  47. #   --dictionaries ( {'name':value, 'name':value,....}, {...}, .. )
  48. #
  49. # we use dictionaries here for clarity, but they're 
  50. # probably not the most efficient alternative (hashing 
  51. # versus indexing);  each tuple is a dictionary, and
  52. # a table is just a list of dictionaries;  we also don't 
  53. # load tables from an external file-- they are assumed to 
  54. # be already present in the program (dictionary constants 
  55. # in python);  dictionaries are slow to copy and compare
  56. # in python, so they're probably a poor representation
  57. # for tuples/records;
  58. #
  59. # notes:
  60. # 1) Uses python's OOP class facility for sets.  We only
  61. # use inheritance to define the RSet() relational set
  62. # subclass of Set(). Since there's little over-riding of 
  63. # methids in Rset, OOP isn't strictly necessary (we could 
  64. # a module of library functions that operate on simple 
  65. # lists instead).
  66. #
  67. # We could, in priciple, also sub-classes to define 
  68. # different internal formats (list sets, string sets, 
  69. # etc) this would require making the concatenation 
  70. # operation virtual, since '+' needs the same type on 
  71. # each side (different singleton syntax).  Since we 
  72. # use lists internally, we can use the faster '.append'
  73. # list method to concatenate onto a set (versus '+').
  74. #
  75. # 2) Python's 'in' and subscripting are generic, but '+'
  76. # requires the same types on left and right.
  77. # Python's '==' (and therefore 'in') handles lists,
  78. # tuples, dictionaries and strings right (its not
  79. # just address comparison ('is' does that): strings
  80. # compare lexically, lists/tuples are traversed 
  81. # recursively, and dictionaries are compared 
  82. # recursively after their fields are sorted.
  83. #
  84. # 3) We do minimal error checking here: you'll get 
  85. # a normal subscripting or dictionary exception if 
  86. # you do something weird with a set;
  87. #
  88. # 4) example usage:
  89. #    python
  90. #    >>> import sets
  91. #    >>> sets.test_sets()
  92. #
  93. #    >>> from sets import *
  94. #    >>> test_sets()
  95. #    >>> import pbd
  96. #    >>> pdb.run('test_sets()')
  97. #
  98. #    >>> x = Set().init('abd')          (interactive)
  99. #    >>> y = Set().init('db')
  100. #    >>> x.intersect(y)
  101. #
  102. #    >>> from sets import *
  103. #    >>> test_sets()
  104. #    >>> from sets import *
  105. #    >>> table1.join(table2).list([])   (tables exported)
  106. ##############################################################  
  107. ##############################################################
  108.  
  109.  
  110.  
  111.  
  112.  
  113. class Set:
  114.  
  115.  
  116.  
  117. #######################################################
  118. # init() takes an optional argument, and allows the
  119. # init value to be a list, character string, tuple,
  120. # another existing set, or any sequence class object;
  121. #######################################################
  122.  
  123.  
  124.  
  125.  
  126.     def init(self, *value):
  127.         self.data = []
  128.         if value: 
  129.             self.concat(value[0])
  130.         return self
  131.  
  132.  
  133.     def display(self):
  134.         print
  135.         print '<',
  136.         for x in self.data: print x,
  137.         print '>'
  138.  
  139.  
  140.     def list(self):
  141.         print
  142.         for x in self.data: print x
  143.  
  144.  
  145.     def to_string(self):
  146.         result = ''
  147.         for x in self.data: result = result + x
  148.         return result
  149.  
  150.  
  151.     def to_list(self):
  152.         return self.data[:]               # copy list
  153.  
  154.  
  155.     def to_RSet(self): 
  156.         return RSet().init(self.data)     # see notes at RSet class
  157.  
  158.  
  159.  
  160.  
  161. #################################################
  162. # note that self.data can be accessed directly;
  163. # we need copy() since assignment just binds a 
  164. # variable to an existing object: changing one
  165. # variable may effect another variable's value;
  166. #################################################
  167.  
  168.  
  169.  
  170.  
  171.     def get(self):        
  172.         return self.data    
  173.     
  174.  
  175.     def set(self, value): 
  176.         self.data = []
  177.         self.concat(value)             # allow any seqence object
  178.  
  179.  
  180.     def copy(self):
  181.         return Set().init(self)        # or copy via self.data[:]
  182.  
  183.  
  184.  
  185.  
  186.  ############################################################
  187.  # insertion, deletion, duplication (in place);
  188.  # concat() allows adding lists, strings, tuples, other 
  189.  # sets, and any other user-defined sequence class object, 
  190.  # to a given set object;
  191.  #
  192.  # concat() uses 'in' to generically iterate over any 
  193.  # sequence type: lists, strings, tuples, and any user-
  194.  # defined sequence class;  any user-defined class
  195.  # which overloads '__len__' and '__getitem__' methods
  196.  # will work with 'in' generation;  our Set class does
  197.  # this too, so 'value' can be another set, and we don't  
  198.  # really need a special type test of the form:
  199.  #      if type(value) == type(self): 
  200.  #           value = value.data
  201.  #
  202.  # notes: 
  203.  # 1) 'x.concat(y.get())' = 'x.concat(y)' = 'x = x + y'
  204.  # 2) 'list.append(x)' is faster than 'list = list + [x]'
  205.  # 3) 'type(self)==type(value)': all class instances 
  206.  #    have the same type, so this only tells us it's an
  207.  #    instance-- we assume its a Set() instance as well;
  208.  #    we need type() since classes can;t over-ride 'in';
  209.  #
  210.  # 4) deletion can be coded many ways in python:
  211.  #     try:
  212.  #         self.data.remove(value)
  213.  #     except: 
  214.  #         pass                      
  215.  #
  216.  #     del self.data[self.data.index(value)]
  217.  #
  218.  #     for i in range(len(self.data)):
  219.  #         if self.data[i] == value
  220.  #             sef.data = self.data[:i] + self.data[i+1:]
  221.  ###########################################################
  222.  
  223.  
  224.  
  225.  
  226.     def concat(self, value):             
  227.         for x in value:                       # generic iterator   
  228.             self.data.append(x)           
  229.                                               
  230.  
  231.     def delete(self, value):
  232.         if value in self.data:
  233.             self.data.remove(value)
  234.  
  235.  
  236.     def delete_all(self, value):
  237.         while value in self.data:
  238.             self.delete(value)                # Set delete, not list 
  239.  
  240.  
  241.     def delete_set(self, sequence):
  242.         for x in sequence:
  243.             self.delete(x)                    # in-place 'difference'
  244.  
  245.                                       
  246.     def repeat(self, copies):
  247.         self.data = self.data * copies
  248.         
  249.  
  250.  
  251.  
  252. ########################################################
  253. # searching (index), function apply and mapping, etc.;
  254. # Pyton has an apply(func,args), but it doesn't 
  255. # apply the funtion to a list;
  256. ########################################################
  257.  
  258.  
  259.  
  260.  
  261.     def search(self, value):              
  262.         for i in range(len(self.data)):     # if value in self.data:  
  263.             if self.data[i] == value:       #    return self.data.index(value)
  264.                 return i              
  265.         return -1                     
  266.  
  267.  
  268.     def apply(self, func):
  269.         for x in self.data: func(x)
  270.  
  271.  
  272.     def map(self, func):
  273.         result = []
  274.         for x in self.data: result.append(func(x))
  275.         return Set().init(result)
  276.  
  277.  
  278.     def accum(self, func, start):
  279.         result = start
  280.         for x in self.data: result = func(result, x)
  281.         return result
  282.  
  283.  
  284.     def accum_op(self, optr, start):
  285.         result = start
  286.         for x in self.data: result = eval(`result` + optr + `x`)
  287.         return result
  288.  
  289.  
  290.     def sum(self):    return self.accum_op('+', 0)
  291.  
  292.     def prod(self):   return self.accum_op('*', 1)
  293.         
  294.     def abs(self):    return self.map(abs)
  295.  
  296.     def neg(self):    return self.map(negate)
  297.     
  298.     def square(self): return self.map(square)
  299.  
  300.  
  301.  
  302.  
  303. ##########################################################
  304. # a set member generator;
  305. #
  306. # these routines are mostly superfluous, since we
  307. # can generate set nodes using 'in' in for loops:
  308. #       for x in set:
  309. #           process(x)
  310. #
  311. # we can do this because we've overloaded len() and
  312. # x[i] to work with set instances (see below);  this
  313. # allows 'in' to generate items in a sequence class;
  314. # makes a copy of the set's list (using [:] slices),
  315. # so that generation ignores set changes between next()
  316. # calls (an index/counter can become wrong);
  317. ##########################################################
  318.  
  319.  
  320.  
  321.  
  322.     def first(self):
  323.         self.tail = self.data[:]            
  324.         return self.next()                  
  325.     
  326.  
  327.     def next(self):
  328.         if self.tail == []:  
  329.             return []
  330.         else:
  331.             head, self.tail = self.tail[0], self.tail[1:]
  332.             return head
  333.  
  334.  
  335.  
  336.  
  337. ############################################
  338. # basic set operations;
  339. # uses very simplistic algorithms;
  340. # also available with operators &, |, ^ ;
  341. #
  342. # note: the right-hand operand to these
  343. # methods ('other') can be another Set,
  344. # or any built-in or user-defined sequence
  345. # type (lists, character strings, tuples.
  346. # other class objects): 'in' generates Set 
  347. # items too;
  348. #
  349. # since 'in' also iterates over the left
  350. # operand ('self'), we don't really need
  351. # to use 'self.data' in the 'if' and 'for'
  352. # statements in the code below (we could
  353. # just use 'self');  but the left must
  354. # be a Set object to call the methods, 
  355. # and using '.data' avoids 1 method call;
  356. ############################################
  357.  
  358.  
  359.  
  360.  
  361.     def intersect(self, other):
  362.         result = []
  363.         for x in self.data:
  364.             if x in other:                     # not 'other.data'
  365.                 result.append(x)
  366.         return Set().init(result)
  367.  
  368.  
  369.     def union(self, other):
  370.         result = self.data[:]                  # copy list 
  371.         for x in other:
  372.             if not x in self.data:
  373.                 result.append(x)
  374.         return Set().init(result)
  375.  
  376.  
  377.     def difference(self, other):
  378.         result = []
  379.         for x in self.data:
  380.             if not x in other:
  381.                 result.append(x)
  382.         return Set().init(result)
  383.     
  384.  
  385.  
  386.  
  387. ################################################################
  388. # set membership, occurrence counts, equivalence, subsumption;
  389. #
  390. # member() and occurs() are not strictly needed:
  391. #
  392. # member -> 
  393. #    'in' works with Set objects, since Set overloads the
  394. #    '__len__' and '__getitem__' methods ('in' works on all
  395. #    sequences generically);  so we can directly code:
  396. #          if value in set:
  397. #               process..
  398. # occurs ->
  399. #    we can simply use the built-in count() list method:  
  400. #          self.data.count(value)
  401. #
  402. # notes:
  403. # 1) 2 sets are 'equivalent' if they have exactly the same 
  404. # elements, but in possibly different orders;
  405. #
  406. # 2) a set 'subsumes' another, if all it contains (at 
  407. # least) all elements in the other set, in any order,
  408. # plus arbitrarily many other items (it must be at least 
  409. # as long as the subsumed set);  this is the same as
  410. # saying the operand is a _subset_ of the object;
  411. ###############################################################
  412.  
  413.  
  414.  
  415.  
  416.     def member(self, value): 
  417.         return (value in self.data)                # boolean 1|0
  418.  
  419.  
  420.     def occurs(self, value):
  421.         i = 0
  422.         for x in self.data: 
  423.             if x == value: i = i+1                 # .count     
  424.         return i
  425.  
  426.  
  427.     def equiv(self, other):
  428.         copy = other.data[:]                       # dont change other
  429.         for x in self.data:
  430.             if not x in copy:                      # other must be a set
  431.                 return 0
  432.             copy.remove(x)
  433.         return (copy == [])
  434.  
  435.  
  436.     def subsume(self, other):
  437.         copy = self.data[:]
  438.         for x in other.data:
  439.             if not x in copy:
  440.                 return 0
  441.             copy.remove(x)
  442.         return 1
  443.  
  444.  
  445.  
  446.  
  447. ############################################################
  448. # bagof() variants: 
  449. #
  450. # create a new set of items composed of items in the 
  451. # set that satisfy/succeed a given testing function;
  452. #
  453. # bagof() is a generic dispatcher; it selects one  
  454. # of 4 bagof methods based on the datatype of the 
  455. # argument and/or the numberof arguments passed
  456. #
  457. # bagof1() takes a function which must take exactly one
  458. # argument (set item), and return a 0/1 boolean result;
  459. #
  460. # bagof2() takes an arbitrary expression (a string),
  461. # which should evaluate to 0/1 boolean result;  in
  462. # the expression string, the variable name 'x' will
  463. # reference the generated set item (eval() evaluates
  464. # an expression in the context of the eval() call);
  465. #
  466. # bagof3() is bagof2(), except that it creates a
  467. # function enclosing the expression, and passes it
  468. # to bagof() (rather than calling eval() repeatedly)
  469. # (exec() is like eval(), but allows full statements)
  470. #
  471. # bagof4() takes a generation variable, a function,
  472. # and a list of arguments to send the function in the 
  473. # form of a tuple;  it binds the generation variable 
  474. # to successive set items, and calls the function 
  475. # with an argument list composed of members of the
  476. # tuple;  the set item can be referenced by using
  477. # the generation variable in the argument list tuple;
  478. #
  479. # bagof5() is like bagof4(), except that is always 
  480. # places the generated item in argument 1, in the 
  481. # call to the testing funtion; this simplifies the
  482. # interface;
  483. #
  484. # notes:
  485. # 1) 'apply' in bagof4() refers to the built-in apply,
  486. # not the Set method (need self.apply to get to method)
  487. #
  488. # 2) The string expression variants must be used with
  489. # care: you must concatenate the string representation 
  490. # of values into the expr;  this requires backquotes
  491. # for lists and tuple;  since Set overloads __repr__, 
  492. # sets can be printed with backquotes, without having
  493. # to call the '.get()' method to get the set's value list
  494. #
  495. # 3) bagof3() must do eval('_f') to get the function it  
  496. # just created;  using 'global _f' does not seem to avoid
  497. # this: '_f' is likely always stored in the local scope
  498. # when 'def' runs, and the '_f'' refence maps to the global
  499. # scope statically (since its not assigned) ???
  500. #
  501. # 4) bagof4() is a bit tricky...
  502. # Python passes parameters by value (copy)-- this
  503. # really means passing by object reference: the formal
  504. # parameter initially 'shares' the object the actual
  505. # parameter is bound to, but the 2 diverge as soon as
  506. # the formal parameter is reassigned in the function.
  507. #
  508. # But, when the formal/actual share a 'mutable' object,
  509. # changes made to it through the formal will be
  510. # reflected in the actual.  In other words, pass by
  511. # reference can be simulated by using a list object
  512. # enclosing the item, and changing the value of
  513. # the list node.
  514. #
  515. # Also, python has no delayed evaluation, except
  516. # for strings: arguments are fully evaluated in a
  517. # call before the call is made, so use a variable
  518. # in a call that we hope will become successively
  519. # bound insode the function [bagof4(x, member, (x,y))]
  520. #
  521. # For both these reasons, bagof4() requires that  
  522. # the generation variable be passed in as a 1-item
  523. # list, and that the test function expect the 1-item
  524. # list to be passed in;  it rebinds list[0], and
  525. # so changes the value of the generation variable;
  526. ############################################################
  527.  
  528.  
  529.  
  530.  
  531.     def bagof(self, *args):
  532.         if len(args) == 1:
  533.             if type(args[0]) == type(''):
  534.                 return self.bagof3(args[0])                # string expr
  535.             else:
  536.                 return self.bagof1(args[0])                # 1-arg function
  537.         
  538.         elif len(args) == 2:
  539.             return self.bagof5(args[0], args[1])           # func+extra-args
  540.         elif len(args) == 3:
  541.             return self.bagof4(args[0], args[1]. args[2])  # gen+func+args
  542.         else:
  543.             raise set_error
  544.  
  545.  
  546.  
  547.  
  548.     def bagof1(self, func):
  549.         res = []
  550.         for x in self.data:
  551.             if func(x): res.append(x)
  552.         return Set().init(res)
  553.  
  554.  
  555.     def bagof2(self, expr):
  556.         res = []
  557.         for x in self.data:
  558.             if eval(expr): res.append(x)
  559.         return Set().init(res)
  560.  
  561.  
  562.     def bagof3(self, expr):
  563.         exec('def _f(x): return ' + expr + '\n')
  564.         return self.bagof1(eval('_f'))
  565.  
  566.  
  567.     def bagof4(self, gen, func, args):
  568.         res = []
  569.         for gen[0] in self.data:
  570.             if apply(func, args): res.append(gen[0])
  571.         return Set().init(res)
  572.  
  573.  
  574.     def bagof5(self, func, args):
  575.         res = []
  576.         for x in self.data:
  577.             if apply(func, (x,) + args): res.append(x)
  578.         return Set().init(res)
  579.  
  580.  
  581.  
  582.  
  583. #
  584. # usage examples
  585. #
  586.  
  587.  
  588.     def intersect1(self, other):
  589.         global global_list
  590.         global_list = other
  591.         return self.bagof(in_global_list)
  592.     
  593.     # def in_global_list(x): return x in global_list
  594.  
  595.  
  596.     def intersect2(self, other):
  597.         return self.bagof2('(x in ' + `other` + ')')
  598.  
  599.  
  600.     def intersect3(self, other):
  601.         return self.bagof3('(x in ' + `other` + ')')
  602.     
  603.  
  604.     def intersect4(self, other):
  605.         gen = [[]]
  606.         return self.bagof4(gen, member2, (gen, other))
  607.  
  608.     # def member2(x,y): return (x[0] in y)
  609.  
  610.  
  611.     def intersect5(self, other):
  612.         return self.bagof5(member, (other,))
  613.  
  614.     # def member(x,y): return (x in y)
  615.  
  616.  
  617.  
  618.  
  619. ###################################################################
  620. # sorting;
  621. #
  622. # 3 sorts: 
  623. # sort() does a simple list sort, and uses the builtin method;
  624. # sort_keyed() sorts a list of tuples on a given field's value; 
  625. # sort_keyed2() is like sort_keyed(), but it builds a comparison
  626. # function so it can use the built-in sort() method 
  627. #
  628. # notes: 
  629. # 1) sort_keyed() uses a simple insertion sort (others are better)
  630. # 2) sort_keyed2() must construct a function, since it must 
  631. #    select field values before calling cmp() (and .sort only
  632. #    alows for the 2 operands as parameters)
  633. # 3) Python has a built-in '.reverse()' method for lists;  we
  634. #    write our own for illustration only
  635. # 4) sort() must make an explicit copy of self's list: we can't
  636. #    just code 'copy = self.data', or else we'd wind up sorting
  637. #    'self's list too (.sort is in-place, and '=' just binds)
  638. # 5) sort_keyed2() must backquote the field name (so it gets
  639. #    quoted), and calleval() to fetch the created function (see
  640. #    the bagof() stuff for more setails)
  641. ###################################################################
  642.  
  643.  
  644.  
  645.  
  646.     def sort(self):
  647.         copy = self.copy()             
  648.         copy.data.sort()                            # list sort, not set sort
  649.         return copy
  650.  
  651.  
  652.     def sort_keyed(self, field):                    # simple insertion sort
  653.         result = []
  654.         for x in self.data:
  655.             i = 0
  656.             for y in result:
  657.                 if x[field] <= y[field]: break
  658.                 i = i+1
  659.             result[i:i] = [x]                       # or, result.insert(i,x)
  660.         return Set().init(result)
  661.  
  662.  
  663.     def sort_keyed2(self, field):
  664.         func = 'def _cmp(x,y): return cmp(x['+`field`+'], y['+`field`+'])\n'
  665.         exec(func)
  666.         copy = self.copy()
  667.         copy.data.sort(eval('_cmp'))       
  668.         return copy                            # cmp() is builtin: -1,0,+1
  669.  
  670.  
  671.  
  672.  
  673. #########################################################
  674. # permutations(): generate all permutations of the set
  675. # reversed(): reverse a list manually (not in place)
  676. # subsets(): geneate all subsets of length n
  677. # combos(): generate all combinations of length n
  678. #
  679. # notes: 
  680. # 1) these methods use recursive functions outside
  681. # the class;  the outside functions avoid creating
  682. # new Set objects at each level of the recursion;
  683. #
  684. # 2) subsets() treats '[a,b]' as equivalent to 
  685. # '[b,a]' (and only generates 1 instance); combos()
  686. # treats the 2 cases as different (and gens both--
  687. # order matters); combos() is really just a length
  688. # (size) limited permutations();
  689. #########################################################
  690.  
  691.  
  692.  
  693.  
  694.     def permutations(self):
  695.         return Set().init(permute(self.data))
  696.  
  697.  
  698.     def reversed(self):
  699.         return Set().init(reverse(self.data))
  700.  
  701.  
  702.     def subsets(self, n):
  703.         return Set().init(subset(self.data, n))
  704.  
  705.  
  706.     def combos(self, n):
  707.         if n > len(self.data):
  708.             return Set().init()
  709.         else:
  710.             return Set().init(combo(self.data, n))
  711.  
  712.  
  713.  
  714.  
  715. ##################################################################
  716. # overload some operators to work with Set instances;
  717. # Python uses special method names to overload operators,
  718. # instead of special syntax (as in C++);
  719. #
  720. # '+' and '*' works for sets as for normal lists, but '+'
  721. # allows appending any sequence type to the set ('+' is not
  722. # generic for built-in types);
  723. #
  724. # notes: 
  725. # 0) it look like difference() and delete_set() are really
  726. # the same (but delete_set( )is done in-place), so '-' and
  727. # '^' are functionally equivalent (oops..);
  728. #
  729. # 1) '+' (and '-' and '*) isn't quite right: 'x = y + value' 
  730. # will also change y;  to be correct, it should make a copy:
  731. #   def __add_(self, value):
  732. #       copy = self.copy()
  733. #       copy.concat(value)
  734. #       return copy
  735. # --> this has now been corrected <-- 
  736. #
  737. # 2) it's critical that we overload __len__ and __getitem__
  738. # (which correspond to len(), and x[i] indexing): this allows 
  739. #
  740. #   (1) 'in' to generate items in the set in 'for' loops, and 
  741. #   (2) 'in' to test membership in conditional expressions
  742. #
  743. # in 'for' loops, 'in' lets us generically iterate over sets 
  744. # just like any other sequence type (lists, strings, tuples,
  745. # other user-defined sequence classes);  this allows us to 
  746. # use just about any sequence type to init or concat onto a
  747. # set, and allows the right-hand-side of a binary set operation
  748. # to be any sequence type;  for example, we can intersect
  749. # a set and a character string:
  750. #
  751. #       x = Set().init(['abcdefg'])
  752. #       x.intersect('aceg')
  753. #
  754. # most binary operations allow non-Set sequence types on the 
  755. # right side, but the left side must be a Set object (or else
  756. # we can't invoke a Set method);
  757. #
  758. # since 'in' also iterates over and test membership in sets, 
  759. # we don't really need to use 'self.data' in the 'if' and 'for'
  760. # statements in most of the code above: we could just use 'self';  
  761. # but, since we know 'self' is a Set (or else we can't call the 
  762. # methods, we can avoid 1 or more method calls by explicitly using 
  763. # the '.data' field name (we avoid calling the __len__ and 
  764. # __getitem__ methods, and instead use the built-in list
  765. # data type's mechanisms (which are presumably faster));
  766. ##################################################################
  767.  
  768.  
  769.  
  770.  
  771.     def __cmp__(self, other): return self.equiv(other)          # ==, <, >
  772.     def __repr__(self):       return `self.data`                # print set
  773.     def __len__(self):        return len(self.data)             # len()
  774.     
  775.     def __getitem__(self, key):    return self.data[key]        # set[i]
  776.     def __setitem__(self, key, x): self.data[key] = x           # set[i]=x
  777.     def __delitem__(self, key):    del self.data[key]           # del set[i]   
  778.     def __getslice__(self, i, j):  return self.data[i:j]        # set[i:j]
  779.     
  780.     def __add__(self, value): 
  781.         res = self.copy(); res.concat(value); return res        #  set + x
  782.     def __sub__(self, value): 
  783.         res = self.copy(); res.delete_set(value); return res    # set - x
  784.     def __mul__(self, value):                 
  785.         res = self.copy(); res.repeat(value); return res        # set * n
  786.  
  787.     def __or__(self, other):  return self.union(other)          # set | set
  788.     def __and__(self, other): return self.intersect(other)      # set & set
  789.     def __xor__(self, other): return self.difference(other)     # set ^ set
  790.  
  791.     def __neg__(self): return self.reversed()                   # -set
  792.     def __pos__(self): return self.sort()                       # +set
  793.  
  794.  
  795.  
  796.  
  797.  
  798.  
  799. ##################################################
  800. # some utility functions called by the methods
  801. # see descriptions at calling methods;
  802. #
  803. # notes:
  804. # 1) 'member' here is global in the module; it
  805. # does not clash with the 'member' Set method,
  806. # since that function can only be reached via
  807. # qualification (instance.member(), Set.member())
  808. ##################################################
  809.  
  810.  
  811.  
  812.  
  813. set_error = 'Set class error'
  814.  
  815. def negate(n): return -n
  816. def square(n): return n * n
  817.  
  818. def member(x,y):  return (x in y)
  819. def member2(x,y): return (x[0] in y)               # for intersect4()
  820. def in_global_list(x): return x in global_list     # for intersect1()
  821.  
  822.  
  823.  
  824.  
  825. def permute(list):
  826.     if list == []:                                  
  827.         return [[]]
  828.     else:
  829.         result = []
  830.         for i in range(len(list)):
  831.             pick = list[i]
  832.             rest = list[:i] + list[i+1:]
  833.             for x in permute(rest):
  834.                 result.append([pick] + x)
  835.         return result
  836.  
  837.  
  838.  
  839.  
  840. def reverse(list): 
  841.     result = []
  842.     for x in list:
  843.         result = [x] + result
  844.     return result                  # or: copy = list; copy.reverse()
  845.  
  846.  
  847.  
  848.  
  849.  
  850. # [help], 4 -> [help]
  851. # [help], 3 -> [hel], [hep], [hlp], [elp]
  852. # [help], 2 -> [he], [hl], [hp], [el], [ep] [lp]
  853. # [help], 1 -> [h], [e], [l], [p]
  854.  
  855.  
  856.  
  857. def subset(list, size):
  858.     if size == 0 or list == []:
  859.         return [[]]
  860.     else:
  861.         result = []
  862.         for i in range(0, (len(list) - size) + 1):
  863.             pick = list[i]
  864.             rest = list[i+1:]                            # drop [:i]
  865.             for x in subset(rest, size - 1):
  866.                 result.append([pick] + x)
  867.         return result
  868.            
  869.  
  870.  
  871.  
  872. # [help], 4 -> [help], [hepl], [hlep], [hlpe], [hpel], ...
  873. # [help], 3 -> [hel], [hep], [hle], [hlp], [hpe], [hpl], [ehl], ...
  874. # [help], 2 -> [he], [hl], [hp], [eh], [el], [ep], [lh], [le], ...
  875. # [help], 1 -> [h], [e], [l], [p]
  876.  
  877.  
  878.  
  879. def combo(list, size):
  880.     if size == 0 or list == []:                                  
  881.         return [[]]
  882.     else:
  883.         result = []
  884.         for i in range(len(list)):
  885.             pick = list[i]
  886.             rest = list[:i] + list[i+1:]
  887.             for x in combo(rest, size-1):
  888.                 result.append([pick] + x)
  889.         return result
  890.  
  891.  
  892.  
  893.  
  894.  
  895.  
  896.  
  897. ##########################################################
  898. ##########################################################
  899. # RSet() subclass: basic relational algebra operations;
  900. # assumes items in the set are 'tuples', which 
  901. # are implemented as Python dictionaries (name:value)
  902. #
  903. # implemented as a subclass instance of the general
  904. # class Set(), since these operations require a 
  905. # specific set item format;  note that RSet 
  906. # inherits all Set methods, including its operator
  907. # overloadings;
  908. #
  909. # adds the following methods:
  910. #    select: returns a new set = old where field = value
  911. #    find: like select, but allow general comparison tests
  912. #    match: a generalized select (keys in another set)
  913. #    join: implements normal 'natural' join on a field
  914. #    product: a simple cartesian product operation
  915. #    unique: removes duplicate tuples from a copied set
  916. #    project: normal 'projection' operation
  917. #    copy_tuple: copies a tuple from a table
  918. #    input_tuple: enters tuple fields, and adds to the table
  919. #    input_list: like input, but field names in a list
  920. #    add_tuple: pass in a list/tuple/string of values
  921. #
  922. # over-rides these methods:
  923. #    init
  924. #    list
  925. #    to_string
  926. #
  927. # notes:
  928. # 1) list() allows an optional argument giving the
  929. # names of the fields to be displayed first (any other
  930. # fields of a tuple are given in any order after 
  931. # the fields in the list() argument).  Passing in an
  932. # empty list forces the [name]=value format.
  933. #
  934. # 2) find() could be more general: use bagof()
  935. # directly for arbitrary selection expressions.
  936. # 'or' can sometimes be simulated by using optr
  937. # 'in' with a list of values, but full-blown
  938. # boolean selection exprs must use bagof();
  939. # find() needs backquotes around the field name
  940. # so it's quoted in the resulting expr, as well
  941. # as around the value;
  942. #
  943. # 3) join() and product() must eplicitly copy each
  944. # tuple dictionary: 'copy = dict' just makes copy
  945. # _share_ the object that dict references, and any
  946. # changes in copy change dict too
  947. #
  948. # 4) we compare tuples and test for tuple membership
  949. # in a table using dictionary equality; 'in' works for 
  950. # lists of dictionaries in python: it uses dictionary 
  951. # equality, which sorts the fields, and compares them 
  952. # leixcographically (and compares their values); this 
  953. # is slow, but works (the 'is' operator tests whether
  954. # 2 variables reference the _same_ object);
  955. #
  956. # 5) product() simply concatenates the 2 tuples,
  957. # renaming same-named fields, by appending '_N' to
  958. # duplicae field names (N uniquely identifies the
  959. # field in the resulting tuple);  join() atually
  960. # deletes same-named fields (in the right operand);
  961. #
  962. # 6) input_tuple() and add_tuple() get the field 
  963. # list from an existing tuple in the table, so the
  964. # table can't be empty (and all tuples should have 
  965. # the same fields in any given table (relational));
  966. # input_tuple() and input_list() allow any type 
  967. # value to be entered for a field: it calls eval()
  968. # with the input string, to force the python parser 
  969. # to determine its type by syntax-- users should type
  970. # values using normal pyth syntax ('abd', 123, [..])
  971. #
  972. # 7) These methods return a RSet object, not a Set
  973. # object, so their results can have further rel ops
  974. # applied to them.  In general, Set and RSet objects
  975. # can be mixed: the left operaand must be an RSet to
  976. # invoke the methods, but the left ('other') can be
  977. # a RSet, a normal Set, or any built-in or user-def'd
  978. # sequence object (list, tuple, other class).  Since
  979. # Python is dynamically typed, Sets and RSets can
  980. # be passed to generic routines;  However, we provide 
  981. # a conversion from Set's to RSet's with a simple Set 
  982. # method:
  983. #
  984. #    def to_RSet(self): return RSet().init(self.data)
  985. #
  986. # This is needed if you use a simple Set() method
  987. # which returns a Set, and want to perform RSet
  988. # operations on the result.  There is no good reason
  989. # for a RSet -> Set conversion (RSet's _are_ Set's)
  990. ##########################################################
  991. ##########################################################
  992.  
  993.  
  994.  
  995.  
  996.  
  997. class RSet(Set):
  998.  
  999.  
  1000.     def init(self, *value):
  1001.         if value:
  1002.             return Set.init(self, value[0])
  1003.         else:
  1004.             return Set.init(self)
  1005.  
  1006.  
  1007.  
  1008.     def list(self, *first): 
  1009.         print 
  1010.         print 'table =>'
  1011.         for x in self.data:
  1012.             if not first:
  1013.                 print '   tuple =>', x
  1014.             else:
  1015.                 print '   tuple =>',
  1016.                 for f in first[0]:
  1017.                     print '['+f+']=' + `x[f]`,             # backquotes
  1018.                 for f in x.keys():
  1019.                     if not f in first[0]:
  1020.                         print '['+f+']=' + `x[f]`,
  1021.                 print
  1022.  
  1023.  
  1024.  
  1025.     def to_string(self):
  1026.         raise rset_error, 'cannot convert to a string'
  1027.  
  1028.  
  1029.  
  1030.     def select(self, field, value):
  1031.         result = []
  1032.         for x in self.data:
  1033.             if x[field] == value:
  1034.                 result.append(x)
  1035.         return RSet().init(result)                  # return RSet, not Set
  1036.  
  1037.  
  1038.  
  1039.     def find(self, field, cmp, value):
  1040.         return \
  1041.             self.bagof('x['+ `field` +'] ' + cmp + ' ' + `value`).to_RSet()
  1042.  
  1043.  
  1044.  
  1045.     def match(self, other, field):
  1046.         result = []
  1047.         for x in self.data:
  1048.             for y in other:
  1049.                 if y[field] == x[field]:
  1050.                     result.append(x)
  1051.         return RSet().init(result)
  1052.  
  1053.  
  1054.  
  1055.     def join(self, other, field):
  1056.         result = []
  1057.         for x in self.data:
  1058.             for y in other:
  1059.                 if y[field] == x[field]:
  1060.                     compos = self.copy_tuple(x)
  1061.                     for k in y.keys(): 
  1062.                         if not x.has_key(k): 
  1063.                             compos[k] = y[k]
  1064.                     result.append(compos)
  1065.         return RSet().init(result)
  1066.  
  1067.  
  1068.  
  1069.     def product(self, other):
  1070.         result = []
  1071.         for x in self.data:
  1072.             for y in other:
  1073.                 compos = self.copy_tuple(x)
  1074.                 for k in y.keys(): 
  1075.                     if not x.has_key(k): 
  1076.                         compos[k] = y[k]
  1077.                     else:
  1078.                         i = 1
  1079.                         while x.has_key(k + '_' + `i`): 
  1080.                             i = i+1
  1081.                         compos[k + '_' + `i`] = y[k]
  1082.                 result.append(compos)
  1083.         return RSet().init(result)
  1084.  
  1085.  
  1086.  
  1087.     def unique(self):
  1088.         result = []
  1089.         for x in self.data:
  1090.             if not x in result:
  1091.                 result.append(x)
  1092.         return RSet().init(result)
  1093.  
  1094.  
  1095.  
  1096.     def project(self, fields):
  1097.         result = []
  1098.         for x in self.data:
  1099.             tuple = {}
  1100.             for y in fields:
  1101.                 if x.has_key(y):
  1102.                     tuple[y] = x[y]
  1103.             if tuple and not tuple in result:
  1104.                 result.append(tuple)
  1105.         return RSet().init(result)
  1106.  
  1107.  
  1108.  
  1109.     def copy_tuple(self, tup):
  1110.         res = {}
  1111.         for field in tup.keys():
  1112.             res[field] = tup[field]
  1113.         return res
  1114.  
  1115.  
  1116.  
  1117.     def input_tuple(self):
  1118.         self.input_list(self.data[0].keys())         # use tuple's fields
  1119.  
  1120.  
  1121.  
  1122.     def input_list(self, fields):
  1123.         tup = {}
  1124.         for x in fields:
  1125.             valstr = raw_input(x + ' => ')
  1126.             tup[x] = eval(valstr)                    # any type: parse it
  1127.         self.data.append(tup)
  1128.  
  1129.  
  1130.  
  1131.     def add_tuple(self, values):                     # types already known
  1132.         tup, i = {}, 0
  1133.         if len(values) != len(self.data[0]):
  1134.             raise rset_error, 'add_tuple length'
  1135.         for x in self.data[0].keys():
  1136.             tup[x], i = values[i], i+1
  1137.         self.data.append(tup)
  1138.  
  1139.  
  1140.  
  1141.  
  1142. rset_error = 'relational set error'
  1143.  
  1144.  
  1145.  
  1146.  
  1147.  
  1148.  
  1149.  
  1150. ##############################################################
  1151. ##############################################################
  1152. #
  1153. # the test driver
  1154. #
  1155. # notes:
  1156. # 1) `['a', 'b']` (backquotes) == '[\'a\', \'b\']'; 
  1157. # since find() already backquotes the search value, 
  1158. # we don't always need o do it below;
  1159. #
  1160. # 2) the 'complete', 'available', and 'object' tables
  1161. # are computed with relational operators statically;
  1162. # we could make these into something like 'schemas'
  1163. # if we store them as a string and eval() them to
  1164. # get their values on demand;  RSet has no real 
  1165. # 'schema' (delayed evaluation) facility as is;
  1166. #
  1167. # 3) test_sets() exports 6 tables to the global
  1168. # scope of the module on exit, so they can be used
  1169. # at the interactive propmt.  The user must do 
  1170. # another 'from sets import *' _after_ test_sets()
  1171. # runs, to get these symbols-- 'import *' statements
  1172. # in python get symbols in the module at the time 
  1173. # the 'import' executes (it does not get symbols
  1174. # added to the module's global scope later on).
  1175. # This complexity could be avoided by coding the
  1176. # tables global (assign them outside test_sets()).
  1177. ##############################################################
  1178. ##############################################################
  1179.  
  1180.  
  1181.  
  1182.  
  1183.  
  1184. def test_sets():
  1185.  
  1186.     x = Set().init('abcdefg')
  1187.     item = x.first()
  1188.     while item:                           # generator
  1189.         print item,
  1190.         item = x.next()
  1191.     print
  1192.  
  1193.     for item in x: print item,            # 'in' overload
  1194.     print
  1195.  
  1196.     print ('d' in x ), ('z' in x )        # 'in' overload
  1197.     
  1198.     x = RSet().init([{'name':'x', 'age':0, 'stats':[1,2,3]}])
  1199.     x.list([])
  1200.     x.add_tuple(('y', 1, [4,5,6]))
  1201.     x.add_tuple(('z', 2, [7,8]))
  1202.     x.list([])
  1203.  
  1204.  
  1205.  
  1206.     #############################
  1207.     # a simple employee database
  1208.     #############################
  1209.  
  1210.  
  1211.     a = RSet().init( \
  1212.             [{'name':'mark',  'job':'engineer'},     \
  1213.              {'name':'amrit', 'job':'engineer'},     \
  1214.              {'name':'sunil', 'job':'manager'},      \
  1215.              {'name':'marc',  'job':'prez'},         \
  1216.              {'name':'martin','job':'architect'},    \
  1217.              {'name':'jeff',  'job':'engineer'},     \
  1218.              {'name':'eve',   'job':'administrator'} \
  1219.             ])
  1220.     
  1221.  
  1222.     b = RSet().init( \
  1223.             [{'job':'engineer', 'pay':(25000,60000)},  \
  1224.              {'job':'manager',  'pay':(50000,'XXX')},  \
  1225.              {'job':'architect','pay':None},           \
  1226.              {'job':'prez',     'pay':'see figure 1'}  \
  1227.             ])
  1228.  
  1229.  
  1230.     c = RSet().init( \
  1231.             [{'name':'mark',  'job':'engineer'},     \
  1232.              {'name':'amrit', 'job':'engineer'},     \
  1233.              {'name':'sunil', 'job':'manager'},      \
  1234.              {'name':'john',  'job':'engineer'},     \
  1235.              {'name':'steve', 'job':'manager'}       \
  1236.             ])
  1237.  
  1238.  
  1239.     a.list()
  1240.     a.select('job', 'engineer').list()
  1241.     a.join(b, 'job').list()
  1242.     a.join(b, 'job').list(['name','pay'])
  1243.  
  1244.     a.project(['job']).list()      
  1245.     a.select('job', 'engineer').project(['name']).list()
  1246.  
  1247.     a.find('job', '>', 'engineer').list(['job'])
  1248.     c.find('job', '!=', 'engineer').list(['job'])
  1249.     a.bagof('x[\'name\'][0] == \'m\'').to_RSet().list(['name'])   
  1250.     a.bagof('x[\'job\'] > \'engineer\'').to_RSet().list([])
  1251.     a.bagof('x[\'job\'] > \'engineer\' or x[\'name\'] == \'eve\'').\
  1252.                                                     to_RSet().list([])
  1253.  
  1254.     a.project(['job']).difference(b.project(['job'])).list()
  1255.     a.join(b, 'job').project(['name', 'pay']).list()
  1256.     a.select('name','sunil').join(b,'job').project(['name', 'pay']).list()
  1257.     
  1258.     b.product(c).list([])
  1259.     a.product(c).list([])
  1260.     a.product(c).unique().list([])
  1261.     a.product(c).project(['name']).list([])
  1262.     a.product(c).project(['name', 'name_1']).list([])
  1263.  
  1264.     a.sort_keyed('name').to_RSet().list(['name'])
  1265.     a.sort_keyed2('name').to_RSet().list(['name'])
  1266.     c.sort_keyed('name').to_RSet().list(['name'])
  1267.  
  1268.     a.difference(c).list()
  1269.     a.intersect(c).list()
  1270.     a.union(c).list()
  1271.  
  1272.     print cmp( ((a ^ c) + (a & c)), a) 
  1273.     print cmp( ((a ^ c) | (a & c)), a)
  1274.     print cmp( ((a ^ c) | c), a)
  1275.  
  1276.     print ((a ^ c) + (a & c)).equiv(a) 
  1277.     print ((a ^ c) | c).equiv(a)
  1278.     print a.difference(c).union(a.intersect(c)).equiv(a)
  1279.     
  1280.  
  1281.  
  1282.     #########################
  1283.     # a languages database
  1284.     #########################
  1285.  
  1286.  
  1287.     langs = RSet().init([ \
  1288.         {'name':'prolog',   'type':'logic',   'rank':5, 'use':'aliens'},  \
  1289.         {'name':'alpha',    'type':'proced',  'rank':3, 'use':'glue'},    \
  1290.         {'name':'python',   'type':'proced',  'rank':1, 'use':'rad'},     \
  1291.         {'name':'icon',     'type':'proced',  'rank':1, 'use':'rad'},     \
  1292.         {'name':'scheme',   'type':'funct',   'rank':2, 'use':'aliens'},  \
  1293.         {'name':'smalltalk','type':'object',  'rank':4, 'use':'oop'},     \
  1294.         {'name':'dylan',    'type':'object',  'rank':7, 'use':'glue'},    \
  1295.         {'name':'self',     'type':'object',  'rank':7, 'use':'oop'},     \
  1296.         {'name':'rexx',     'type':'proced',  'rank':7, 'use':'shell'},   \
  1297.         {'name':'tcl',      'type':'proced',  'rank':6, 'use':'shell'},   \
  1298.         {'name':'d',        'type':'unknown', 'rank':7, 'use':'glue'},    \
  1299.         {'name':'lisp',     'type':'funct',   'rank':3, 'use':'aliens'},  \
  1300.         {'name':'perl',     'type':'proced',  'rank':7, 'use':'shell'}    \
  1301.       ])
  1302.  
  1303.  
  1304.     course = RSet().init([ \
  1305.         {'type':'logic',  'class':'ai'},        \
  1306.         {'type':'funct',  'class':'ai'},        \
  1307.         {'type':'proced', 'class':'compilers'}, \
  1308.         {'type':'logic',  'class':'compilers'}, \
  1309.         {'type':'object', 'class':'simulation'} \
  1310.       ])
  1311.  
  1312.  
  1313.     prof = RSet().init([ \
  1314.         {'class': 'ai',       'prof':'travis'}, \
  1315.         {'class':'compilers', 'prof':'horwitz'} \
  1316.       ])
  1317.  
  1318.  
  1319.     dbase     = langs.find('use', 'in', ['glue', 'rad', 'oop'])
  1320.  
  1321.     complete  = langs.find('name', 'not in', \
  1322.                  ['alpha', 'dylan'])                         # no backquotes
  1323.     
  1324.     objects   = langs.find('name', 'in', \
  1325.                  ['python', 'icon', 'smalltalk', 'self', 'dylan'])
  1326.     
  1327.     available = langs.find('name', 'not in', \
  1328.                  ['dylan', 'self', 'd', 'rexx'])
  1329.  
  1330.  
  1331.     dbase.list(['name'])
  1332.     langs.join(course,'type').project(['name','class']).list([])
  1333.     langs.join(course,'type').join(prof,'class').project(['name','prof']).list()    
  1334.     
  1335.     langs.select('use','aliens').join(course,'type').project(['class']).list()
  1336.     
  1337.     for x in ['<', '==', '>']:
  1338.         set = langs.find('rank', x, 3)
  1339.         set.project(['name', 'rank']).sort_keyed('rank').to_RSet().list([])
  1340.  
  1341.     worst = langs.find('rank', '>=', 5)
  1342.     worst.sort_keyed('rank').to_RSet().list(['name', 'rank'])
  1343.  
  1344.     langs.select('name', 'rad').list([]) 
  1345.     langs.select('use',  'rad').list([])
  1346.     available.list([])
  1347.     objects.list([])
  1348.  
  1349.     x = langs.find('use', 'in', ['rad', 'glue'])
  1350.     (x & complete & objects & available).to_RSet().project(['name']).list([])
  1351.     
  1352.     
  1353.  
  1354.     #######################
  1355.     # miscellaneous tests
  1356.     #######################
  1357.  
  1358.  
  1359.     print
  1360.     x = Set().init('abcdefg')
  1361.     print x.intersect('aczeg')
  1362.     y = Set().init('aceg')
  1363.     print x.intersect(y)
  1364.  
  1365.     z1 = Set().init('gcae')
  1366.     z2 = z1 + 'x'
  1367.     print x.subsume(y), x.subsume(z1), x.subsume(z2)
  1368.     print y.equiv(z1),  y.equiv(z2)
  1369.  
  1370.     x = Set().init(['h', 'e', 'l', 'p'])
  1371.     x.permutations().list()
  1372.  
  1373.     for i in range(0, len(x) + 2):                 # 0..5
  1374.         x.subsets(i).display()
  1375.     for i in range(0, len(x) + 2):
  1376.         x.combos(i).display()
  1377.  
  1378.     x = Set().init()
  1379.     x.set([1,2,3])
  1380.     x.permutations().display()
  1381.  
  1382.     x = Set().init()
  1383.     x.concat('alp')
  1384.     print permute(x.get())
  1385.  
  1386.     x.concat('po')
  1387.     x.display()
  1388.     x.reversed().display()
  1389.     x.sort().display()
  1390.     print x
  1391.     print x.to_string()
  1392.  
  1393.     print
  1394.     x = Set().init([1,2,3,4,5,6])
  1395.     y = Set().init([0,2,4,9,6,8,2])
  1396.     print x.intersect1(y)
  1397.     print x.intersect2(y)
  1398.     print x.intersect3(y)          # test bagof generators
  1399.     print x.intersect4(y)
  1400.     print x.intersect5(y)
  1401.  
  1402.     print x.bagof('x in '+`y`)         # dont need y.get()
  1403.     print x.bagof(member, (y,))        # dont need y.get()
  1404.  
  1405.  
  1406.  
  1407.     ###########################
  1408.     # test operator overloads
  1409.     ###########################
  1410.  
  1411.  
  1412.     print
  1413.     x = Set().init('abcd')
  1414.     y = Set().init('db')
  1415.     print len(x), x + 'efg', x - y, x * 3     # 'x - [..]' uses coerce()
  1416.     
  1417.     print x[0], x[0:2]
  1418.     x[1] = 'z'
  1419.     del x[0]
  1420.     print x
  1421.  
  1422.     print (x == 'abcde'), (x == x)
  1423.     print x.equiv(Set().init('abcde')), x.equiv(x)
  1424.     
  1425.     x.set('abcde')
  1426.     y = Set().init('azce')
  1427.     z = Set().init('axedy')
  1428.     print x | z
  1429.     print x & y
  1430.     print x ^ z
  1431.     print -x + z
  1432.     print x & y & z
  1433.     print x | y | z
  1434.     print x ^ y ^ z     
  1435.     print x + y + z
  1436.     print x - y - z
  1437.     print (x | y - z).display()
  1438.  
  1439.  
  1440.  
  1441.     ######################################
  1442.     # export tables for interactive tests
  1443.     ######################################
  1444.  
  1445.  
  1446.     global table1, table2, table2
  1447.     table1, table2, table3 = a, b, c
  1448.  
  1449.     global langs1, course1, prof1
  1450.     langs1, course1, prof1 = langs, course, prof 
  1451.  
  1452.  
  1453.  
  1454.  
  1455.  
  1456. #  
  1457. # sets.test_sets()
  1458. #  
  1459.