home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / ada / setl2 / samples / bags.stl next >
Text File  |  1991-11-16  |  18KB  |  713 lines

  1. --
  2. --  BAGS
  3. --
  4. --  A bag, or multiset, is similar to a set except that we do not require
  5. --  all elements to be unique.  This has been one of the most-often
  6. --  requested additions to SETL, so it is a good application for the
  7. --  demonstration of objects.
  8. --
  9. --  An easy, but unacceptable, implementation is to store all the
  10. --  elements of bags in SETL2 tuples.  But a basic premise of bags is
  11. --  that membership tests should be fairly fast.  Testing for membership
  12. --  in a tuple is a linear time operation, and we would like to be closer
  13. --  to constant time if at all possible.
  14. --
  15. --  An obvious solution is to use maps to store bags, where the domain of
  16. --  the map is the unique elements of the bag, and the image is the count
  17. --  of the occurrences.  A problem with this idea concerns the possible
  18. --  use of bags in map contexts.  If b is a bag of pairs, should we be
  19. --  able to write b(x) or b{x}, and should the results be returned
  20. --  quickly?  The meaning of b{x} seems clear (it is the bag of the
  21. --  second components of pairs whose first component is x), but b(x) is
  22. --  not.  Since it makes a better demonstration of classes if we include
  23. --  this operation, we'll assume b(x) is the count of the occurrences of
  24. --  x in b.
  25. --
  26. --  Given all these decisions, we use two maps to represent bags.  One
  27. --  map contains all ordered pair values, and is stored as a multi-valued
  28. --  map of maps.  The domain of the outer map is the set of left hand
  29. --  elements of the pairs.  The image of each domain element is a map
  30. --  sending each domain element into its associated right-hand elements
  31. --  and counts.
  32. --
  33. --  The second map contains all non-pair values, and is just a map of
  34. --  values and counts.
  35. --
  36. --  We maintain the cardinality explicitly rather than recalculating it
  37. --  repeatedly, since it is an expensive calculation.
  38. --
  39. --  Many of these methods are quite similar, depending on a few
  40. --  expressions used repeatedly, so it's worth looking at them before
  41. --  examining the code.  The expression producing the count of a pair
  42. --  [x,y] is pairs{x}(y).  This expression never fails, but will produce om
  43. --  when the count is zero, so we generally use it as (pairs{x}(y) ? 0).
  44. --  The count of a non-pair x is others(x).  Again, this produces om when
  45. --  the count is zero so we use it as (others(x) ? 0).
  46. --
  47. --  We iterate over all elements in a bag with the following two loops:
  48. --
  49. --     for right_map in pairs{left}, count = right_map(right) loop
  50. --        -- produces successive left, right, count from pairs
  51. --     end loop;
  52. --
  53. --     for count = others(left) loop
  54. --        -- produces successive left, count from others
  55. --     end loop;
  56. --
  57. --  If you fully understand why these expressions work, the code should
  58. --  be quite clear.
  59. --
  60.  
  61. class bag;
  62.  
  63.    procedure create(source);
  64.  
  65. end bag;
  66.  
  67. class body bag;
  68.  
  69.    var  pairs          := {},         -- pair values
  70.         others         := {},         -- non-pair values
  71.         cardinality    := 0;          -- cardinality
  72.  
  73.    --
  74.    --  CREATE
  75.    --
  76.    --  The create function requires some kind of aggregate we can iterate
  77.    --  over with a for loop.  Each item produced will be placed in either
  78.    --  pairs or others, depending on whether or not it is a tuple of
  79.    --  length two.
  80.    --
  81.  
  82.    procedure create(source);
  83.  
  84.       cardinality := #source;
  85.  
  86.       for x in source loop
  87.          if is_tuple(x) and #x = 2 then
  88.             [left,right] := x;     
  89.             pairs{left}(right) := (pairs{left}(right) ? 0) + 1;
  90.          else
  91.             others(x) := (others(x) ? 0) + 1;         
  92.          end if;
  93.       end loop;
  94.  
  95.    end create;
  96.  
  97.    --
  98.    --  CARDINALITY
  99.    --
  100.    --  The pound sign operation returns the cardinality of the bag.
  101.    --
  102.  
  103.    procedure # self;
  104.  
  105.       return cardinality;
  106.  
  107.    end;
  108.  
  109.    --
  110.    --  ARB
  111.    --
  112.    --  ARB returns an arbitrary element of the bag.
  113.    --
  114.  
  115.    procedure arb self;
  116.  
  117.       if cardinality = 0 then
  118.          return om;
  119.       end if;
  120.  
  121.       if #pairs > 0 then
  122.          [left, [right, -]] := arb pairs;
  123.          return [left,right];
  124.       else
  125.          [operand, -] := arb others;
  126.          return operand;
  127.       end if;
  128.  
  129.    end;
  130.  
  131.    --
  132.    --  POW
  133.    --
  134.    --  POW returns the power set (kind of) of a bag.  We first form a
  135.    --  tuple of the elements in the bag, with a pair representing each
  136.    --  element.  The left element of the pair contains a flag, with 1
  137.    --  indicating that the element is in the current subset and a zero
  138.    --  indicating it does not.  We keep manipulating these flags as if we
  139.    --  were incrementing a binary number.
  140.    --
  141.  
  142.    procedure pow self;
  143.  
  144.       power_array := [[0,x] : x in self];
  145.       powerset := {};
  146.  
  147.       loop
  148.  
  149.          powerset with := bag([e : [s,e] in power_array | s = 1]);
  150.          
  151.          if not (exists n in [1 .. #power_array] | power_array(n)(1) = 0) then
  152.             exit;
  153.          end if;
  154.  
  155.          for i in [1 .. n - 1] loop
  156.             power_array(i)(1) := 0;
  157.          end loop;
  158.  
  159.          power_array(n)(1) := 1;
  160.  
  161.       end loop;
  162.  
  163.       return powerset;
  164.  
  165.    end;
  166.          
  167.    --
  168.    --  DOMAIN
  169.    --
  170.    --  DOMAIN returns the set of all the left elements of pairs.
  171.    --
  172.  
  173.    procedure domain self;
  174.  
  175.       if #others /= 0 then
  176.          abort("May not find domain of non-map BAG:\n"+str(self));
  177.       end if;
  178.  
  179.       return domain(pairs);
  180.  
  181.    end;
  182.          
  183.    --
  184.    --  RANGE
  185.    --
  186.    --  RANGE returns the set of all the right elements of pairs.
  187.    --
  188.  
  189.    procedure range self;
  190.  
  191.       if #others /= 0 then
  192.          abort("May not find domain of non-map BAG:\n"+str(self));
  193.       end if;
  194.  
  195.       return {right : [-, [right, -]] in pairs};
  196.  
  197.    end;
  198.          
  199.    --
  200.    --  UNION
  201.    --
  202.    --  Addition of bags corresponds to union of sets.  We just union both
  203.    --  the pairs and others instance variables.
  204.    --
  205.  
  206.    procedure self + right_bag;
  207.  
  208.       if type(right_bag) /= "BAG" then
  209.          abort("Invalid operands for +:\nLeft => "+str(self)+
  210.                "\nRight => "+str(right_bag));
  211.       end if;
  212.  
  213.       result := self;
  214.       result.cardinality +:= right_bag.cardinality;
  215.  
  216.       for right_map = right_bag.pairs{left}, count = right_map(right) loop
  217.          result.pairs{left}(right) := (result.pairs{left}(right) ? 0) + count;
  218.       end loop;
  219.  
  220.       for count = right_bag.others(left) loop
  221.          result.others(left) := (result.others(left) ? 0) + count;
  222.       end loop;
  223.  
  224.       return result;
  225.  
  226.    end;
  227.  
  228.    --
  229.    --  DIFFERENCE
  230.    --
  231.    --  Subtraction of bags is like set difference.
  232.    --
  233.  
  234.    procedure self - right_bag;
  235.  
  236.       if type(right_bag) /= "BAG" then
  237.          abort("Invalid operands for -:\nLeft => "+str(self)+
  238.                "\nRight => "+str(right_bag));
  239.       end if;
  240.  
  241.       result := bag([]);        -- prepare to insert into empty bag
  242.  
  243.       for right_map = pairs{left}, count = right_map(right) loop
  244.          if (count := count - (right_bag.pairs{left}(right) ? 0)) > 0 then 
  245.             result.pairs{left}(right) := count;
  246.             result.cardinality +:= count;
  247.          end if;
  248.       end loop;
  249.  
  250.       for count = others(left) loop
  251.          if (count := count - (right_bag.others(left) ? 0)) > 0 then 
  252.             result.others(left) := count;
  253.             result.cardinality +:= count;
  254.          end if;
  255.       end loop;
  256.  
  257.       return result;
  258.  
  259.    end;
  260.  
  261.    --
  262.    --  INTERSECTION
  263.    --
  264.    --  Multiplication corresponds to intersection of bags.  We minimize
  265.    --  the counts of each value found in both bags.
  266.    --
  267.  
  268.    procedure self * right_bag;
  269.  
  270.       if type(right_bag) /= "BAG" then
  271.          abort("Invalid operands for *:\nLeft => "+str(self)+
  272.                "\nRight => "+str(right_bag));
  273.       end if;
  274.  
  275.       result := bag([]);
  276.       for right_map = right_bag.pairs{left}, count = right_map(right) loop
  277.          if (count := (pairs{left}(right) ? 0) min count) > 0 then 
  278.             result.pairs{left}(right) := count;
  279.             result.cardinality +:= count;
  280.          end if;
  281.       end loop;            
  282.  
  283.       for count = right_bag.others(left) loop
  284.          if (count := (others(left) ? 0) min count) > 0 then 
  285.             result.others(left) := count;
  286.             result.cardinality +:= count;
  287.          end if;
  288.       end loop;
  289.  
  290.       return result;
  291.  
  292.    end;
  293.  
  294.    --
  295.    --  SYMMETRIC DIFFERENCE
  296.    --
  297.    --  MOD for bags means symmetric difference, just like sets.  We're
  298.    --  lazy here, and just call the union, intersection, and difference
  299.    --  procedures.  We could have done this more efficiently, but
  300.    --  symmetric difference isn't used much.
  301.    --
  302.  
  303.    procedure self mod right_bag;
  304.  
  305.       if type(right_bag) /= "BAG" then
  306.          abort("Invalid operands for MOD:\nLeft => "+str(self)+
  307.                "\nRight => "+str(right_bag));
  308.       end if;
  309.  
  310.       return (self + right_bag) - (self * right_bag);
  311.  
  312.    end;
  313.  
  314.    --
  315.    --  NPOW
  316.    --
  317.    --  NPOW is the only commutative operator which ordinarily accepts
  318.    --  different types.  We need two versions.
  319.    --
  320.    --  The procedure we follow here is identical to the POW operator,
  321.    --  except that we only keep subsets with the correct cardinality.
  322.    --
  323.  
  324.    procedure self npow right;
  325.  
  326.       if not is_integer(right) then      
  327.          abort("Invalid operands for NPOW\nLeft => "+str(self)+
  328.                "\nRight => "+str(right));
  329.       end if;
  330.  
  331.       return npower(right);
  332.  
  333.    end;
  334.  
  335.    procedure left npow self;
  336.  
  337.       if not is_integer(left) then      
  338.          abort("Invalid operands for NPOW\nLeft => "+str(left)+
  339.                "\nRight => "+str(self));
  340.       end if;
  341.  
  342.       return npower(left);
  343.  
  344.    end;
  345.  
  346.    procedure npower(i);
  347.  
  348.       power_array := [[0,x] : x in self];
  349.       powerset := {};
  350.  
  351.       loop
  352.  
  353.          if +/{c : [c,-] in power_array} = i then
  354.             powerset with := bag([e : [s,e] in power_array | s = 1]);
  355.          end if;
  356.  
  357.          if not (exists n in [1 .. #power_array] | power_array(n)(1) = 0) then
  358.             exit;
  359.          end if;
  360.  
  361.          for i in [1 .. n - 1] loop
  362.             power_array(i)(1) := 0;
  363.          end loop;
  364.  
  365.          power_array(n)(1) := 1;
  366.  
  367.       end loop;
  368.  
  369.       return powerset;
  370.  
  371.    end npower;
  372.          
  373.    --
  374.    --  WITH
  375.    --
  376.    --  WITH just inserts its operand into the bag.
  377.    --
  378.  
  379.    procedure self with operand;
  380.  
  381.       result := self;
  382.       result.cardinality +:= 1;
  383.  
  384.       if is_tuple(operand) and #operand = 2 then
  385.          [left,right] := operand;     
  386.          result.pairs{left}(right) := (pairs{left}(right) ? 0) + 1;
  387.       else
  388.          result.others(operand) := (others(operand) ? 0) + 1;         
  389.       end if;
  390.  
  391.       return result;
  392.  
  393.    end;
  394.  
  395.    --
  396.    --  LESS
  397.    --
  398.    --  LESS removes its operand from the bag.
  399.    --
  400.  
  401.    procedure self less operand;
  402.  
  403.       result := self;
  404.  
  405.       if is_tuple(operand) and #operand = 2 then
  406.          [left,right] := operand;     
  407.          if (count := pairs{left}(right) ? 0) > 1 then 
  408.             result.pairs{left}(right) := count - 1;         
  409.             result.cardinality -:= 1;
  410.          elseif count = 1 then
  411.             result.pairs{left} less:= [right,1];
  412.             result.cardinality -:= 1;
  413.          end if;            
  414.       else
  415.          if (count := others(operand) ? 0) > 1 then
  416.             result.others(operand) := count - 1;
  417.             result.cardinality -:= 1;
  418.          elseif count = 1 then
  419.             result.others(operand) := om;
  420.             result.cardinality -:= 1;
  421.          end if;
  422.       end if;
  423.  
  424.       return result;
  425.  
  426.    end;
  427.  
  428.    --
  429.    --  LESSF
  430.    --
  431.    --  LESSF removes each pair with a given left element.
  432.    --
  433.  
  434.    procedure self lessf operand;
  435.  
  436.       if #others /= 0 then
  437.          abort("Attempt to use LESSF on non-map BAG:\n"+str(self));
  438.       end if;
  439.  
  440.       result := self;
  441.  
  442.       result.cardinality -:= +/{count : [count, -] in pairs{operand}};
  443.       result.pairs{operand} := {};
  444.  
  445.       return result;
  446.  
  447.    end;
  448.  
  449.    --
  450.    --  FROM
  451.    --
  452.    --  From deletes and returns an arbitrary element from the bag.
  453.    --
  454.  
  455.    procedure from self;
  456.  
  457.       if cardinality = 0 then
  458.          return om;
  459.       end if;
  460.  
  461.       cardinality -:= 1;
  462.  
  463.       if #pairs > 0 then
  464.          [left, [right, count]] from pairs;
  465.          if count > 1 then
  466.             pairs{left}(right) := count - 1;
  467.          end if;
  468.          return [left,right];
  469.       elseif #others > 0 then
  470.          [operand, count] from others;
  471.          if count > 1 then
  472.             others(operand) := count - 1;
  473.          end if;
  474.          return operand;
  475.       end if;
  476.  
  477.    end;
  478.  
  479.    --
  480.    --  IN
  481.    --
  482.    --  In for bags is a membership test.
  483.    --
  484.  
  485.    procedure operand in self;
  486.  
  487.       if is_tuple(operand) and #operand = 2 then
  488.          [left,right] := operand;
  489.          return pairs{left}(right) /= om;
  490.       else
  491.          return others(operand) /= om;
  492.       end if;
  493.  
  494.    end;
  495.  
  496.    --
  497.    --  SUBSET (subbag?)
  498.    --
  499.    --  Less than is a subset test for bags.
  500.    --
  501.  
  502.    procedure self < right_bag;
  503.  
  504.       if type(right_bag) /= "BAG" then
  505.          abort("Invalid operands for <:\nLeft => "+str(self)+
  506.                "\nRight => "+str(right_bag));
  507.       end if;
  508.  
  509.       if cardinality >= right_bag.cardinality then
  510.          return false;
  511.       end if;
  512.  
  513.       for right_map = pairs{left}, count = right_map(right) loop
  514.          if count > right_bag.pairs{left}(right) ? 0 then 
  515.             return false;
  516.          end if;
  517.       end loop;
  518.  
  519.       for count = others(left) loop
  520.          if count > right_bag.others(left) ? 0 then 
  521.             return false;
  522.          end if;
  523.       end loop;
  524.  
  525.       return true;
  526.  
  527.    end;
  528.  
  529.    --
  530.    --  COUNT REFERENCE
  531.    --
  532.    --  We use map reference syntax, i.e. f(x) to denote count reference.
  533.    --
  534.  
  535.    procedure self(element);
  536.  
  537.       if is_tuple(element) and #element = 2 then
  538.          [left, right] := element;
  539.          return pairs{left}(right) ? 0;
  540.       else
  541.          return others(element) ? 0;
  542.       end if;
  543.  
  544.    end;
  545.  
  546.    --
  547.    --  COUNT ASSIGNMENT
  548.    --
  549.    --  We use map assignment syntax, i.e. f(x) := y to denote count
  550.    --  assignment.
  551.    --
  552.  
  553.    procedure self(element) := count;
  554.  
  555.       if is_tuple(element) and #element = 2 then
  556.          [left, right] := element;
  557.          cardinality -:= pairs{left}(right) ? 0;
  558.          pairs{left}(right) := count;
  559.          cardinality +:= count;
  560.       else
  561.          cardinality -:= others(element) ? 0;
  562.          others(element) := count;
  563.          cardinality +:= count;
  564.       end if;
  565.  
  566.    end;
  567.  
  568.    --
  569.    --  IMAGE SET REFERENCE
  570.    --
  571.    --  We return a bag of the image of an element.
  572.    --
  573.  
  574.    procedure self{element};
  575.  
  576.       if #others /= 0 then
  577.          abort("May not reference image set in non-map BAG:\n"+str(self));
  578.       end if;
  579.  
  580.       result := bag([]);
  581.      
  582.       for count = pairs{element}(right) loop
  583.          if is_tuple(right) and #right = 2 then
  584.             [x,y] := right;     
  585.             result.pairs{x}(y) := (result.pairs{x}(y) ? 0) + count;
  586.          else
  587.             result.others(right) := (result.others(right) ? 0) + count;         
  588.          end if;
  589.          result.cardinality := count;
  590.       end loop;
  591.  
  592.       return result;
  593.  
  594.    end;
  595.  
  596.    --
  597.    --  IMAGE SET ASSIGNMENT
  598.    --
  599.    --  We assign the right elements of pairs with a given left element.
  600.    --
  601.  
  602.    procedure self{left} := value;
  603.  
  604.       if type(value) /= "BAG" then
  605.          abort("Invalid value for f{x} assignment\nValue => "+str(value));
  606.       end if;
  607.  
  608.       if #others /= 0 then
  609.          abort("May not assign image set to non-map BAG:\n"+str(self));
  610.       end if;
  611.  
  612.       for count = pairs{left}(right) loop   -- remove old image set
  613.          cardinality -:= count;
  614.       end loop;
  615.       pairs{left} := {};
  616.  
  617.       for right in value loop               -- and install a new one
  618.          pairs{left}(right) := (pairs{left}(right) ? 0) + 1;
  619.          cardinality +:= 1;
  620.       end loop;
  621.  
  622.    end;      
  623.    
  624.    --
  625.    --  ITERATION
  626.    --
  627.    --  Iteration over a bag is nearly identical to iteration over a set.
  628.    --
  629.  
  630.    procedure iterator_start;
  631.  
  632.       null;
  633.  
  634.    end iterator_start;
  635.  
  636.    procedure iterator_next;
  637.  
  638.       if cardinality = 0 then
  639.          return om;
  640.       end if;
  641.  
  642.       cardinality -:= 1;
  643.  
  644.       if #pairs > 0 then
  645.          [left, [right, count]] from pairs;
  646.          if count > 1 then
  647.             pairs{left}(right) := count - 1;
  648.          end if;
  649.          return [[left,right]];
  650.       else
  651.          [operand, count] from others;
  652.          if count > 1 then
  653.             others(operand) := count - 1;
  654.          end if;
  655.          return [operand];
  656.       end if;
  657.  
  658.    end iterator_next;
  659.       
  660.    procedure set_iterator_start;
  661.  
  662.       if #others /= 0 then
  663.          abort("Attempt to iterate over non-map BAG:\n"+str(self));
  664.       end if;
  665.  
  666.    end set_iterator_start;
  667.  
  668.    procedure set_iterator_next;
  669.  
  670.       if #pairs = 0 then
  671.          return om;
  672.       end if;
  673.  
  674.       result := bag([]);
  675.       [left, [right, count]] from pairs;
  676.       result.others := {[right,count]};
  677.       result.cardinality := count;
  678.       return [[left,result]];
  679.  
  680.    end set_iterator_next;
  681.       
  682.    --
  683.    --  PRINT STRING
  684.    --
  685.    --  Our print string looks like a set, except we use the following
  686.    --  delimiters:  {> <}.
  687.    --
  688.  
  689.    procedure selfstr;
  690.  
  691.       first_element := true;
  692.       for x in self loop
  693.          if is_string(x) then
  694.             x := "\""+x+"\"";
  695.          end if;
  696.          if first_element then
  697.             first_element := false;
  698.             result := "{> "+str(x);
  699.          else
  700.             result +:= ", "+str(x);
  701.          end if;
  702.       end loop;
  703.       if first_element then
  704.          return "{> <}";
  705.       else
  706.          return result+" <}";
  707.       end if;
  708.  
  709.    end selfstr;
  710.  
  711. end bag;
  712.  
  713.