File Coverage

blib/lib/Set/Toolkit.pm
Criterion Covered Total %
statement 193 256 75.3
branch 49 82 59.7
condition 12 27 44.4
subroutine 37 47 78.7
pod 16 16 100.0
total 307 428 71.7


line stmt bran cond sub pod time code
1             package Set::Toolkit;
2 6     6   178132 use strict;
  6         15  
  6         248  
3 6     6   31 use warnings;
  6         13  
  6         679  
4              
5 6     6   32 use Carp qw(carp cluck croak confess);
  6         27  
  6         642  
6              
7 6     6   49 use vars qw($VERSION);
  6         25  
  6         12170  
8             $VERSION = '0.11';
9              
10             sub new {
11 24     24 1 19563 my $class = shift;
12 24         45 my $self = {};
13 24         59 bless $self, $class;
14 24         63 return $self;
15             }
16              
17             sub __do_boolean {
18 77     77   94 my $self = shift;
19 77         88 my $field = shift;
20 77         82 my $default = shift;
21              
22 77 100       169 if (@_) {
23 1 50       5 $self->{$field} = $_[0] ? 1 : 0;
24             }
25              
26 77 100       263 $self->{$field} = $default if (not exists $self->{$field});
27 77         261 return $self->{$field};
28             }
29              
30             sub is_ordered {
31 25     25 1 483 my $self = shift;
32 25         61 return $self->__do_boolean('is_ordered', 0, @_);
33             }
34              
35             sub is_unique {
36 52     52 1 63 my $self = shift;
37 52         111 return $self->__do_boolean('is_unique', 1, @_);
38             }
39              
40             ### is=>ro
41             sub _data {
42 124     124   171 my $self = shift;
43            
44 124 100 66     841 if (not exists $self->{_data}
45             or ref($self->{_data}) ne 'ARRAY') {
46 23         68 $self->{_data} = [];
47             }
48              
49 124         473 return $self->{_data};
50             };
51              
52             sub size {
53 14     14 1 1017 my $self = shift;
54 14         37 my @els = $self->elements();
55 14         79 return scalar(@els);
56             };
57              
58             sub _filter_duplicates {
59 41     41   53 my $self = shift;
60            
61 41         78 my %lookup = ();
62 41         49 my @unique = ();
63              
64 41         74 foreach my $el (@_) {
65 177 100       346 if (not exists $lookup{$el}) {
66 165         234 push @unique, $el;
67 165         376 $lookup{$el} = $el;
68             }
69             }
70              
71 41         205 return @unique;
72             }
73              
74              
75             ### Instead of returning the elements themselves, this returns the indecies
76             ### of the elements. This has no real use (probably) in the case of non-
77             ### unique sets, but in unique sets it allows for the data to be manipulated
78             ### without altering the underlying order.
79             sub _element_indecies {
80 0     0   0 my $self = shift;
81              
82 0 0       0 if ($self->is_ordered) {
83 0         0 return $self->_ordered_element_indecies;
84             } else {
85 0         0 return $self->_unordered_element_indecies;
86             }
87             }
88              
89             sub _ordered_element_indecies {
90 8     8   10 my $self = shift;
91              
92             ### Grab our data array.
93 8         12 my @data = @{$self->_data};
  8         26  
94              
95 8 50       20 if ($self->is_unique) {
96             ### Create a lookup of the values in the data set.
97 8         21 my %lookup = ();
98 8         12 my $size = scalar(@data);
99            
100             ### Loop through the data set pushing unique entries into @unique.
101 8         15 my @unique = ();
102 8         23 for (my $i = 0; $i < $size; $i++) {
103 57 100       125 if (not exists $lookup{$data[$i]}) {
104 54         291 push @unique, $i;
105 54         1404 $lookup{$data[$i]} = 1;
106             }
107             }
108            
109             ### Now @unique contains a list of indecies of unique elements. At this point,
110             ### we can just return them.
111 8         50 return @unique;
112             } else {
113             ### If we're not looking for unique, since we *are* looking for ordered,
114             ### just create an array that's 0-n (n=last index).
115 0         0 return (0 .. (scalar(@data)-1));
116             }
117             }
118              
119             sub _unordered_element_indecies {
120 0     0   0 my $self = shift;
121            
122             ### Grab our data array.
123 0         0 my @data = @{$self->_data};
  0         0  
124              
125 0 0       0 if ($self->is_unique) {
126             ### Create a lookup of the values in the data set.
127 0         0 my %lookup = ();
128 0         0 my $size = scalar(@data);
129            
130             ### Loop through the data set pushing unique entries into @unique.
131 0         0 my @unique = ();
132 0         0 for (my $i = 0; $i < $size; $i++) {
133 0 0       0 if (not exists $lookup{$data[$i]}) {
134 0         0 push @unique, $i;
135 0         0 $lookup{$data[$i]} = 1;
136             }
137             }
138            
139             ### Now @unique contains a list of indecies of unique elements. At this point,
140             ### we can just return them.
141 0         0 @data = @unique;
142             } else {
143             ### If we're not looking for unique, since we *are* looking for ordered,
144             ### just create an array that's 0-n (n=last index).
145 0         0 @data = (0 .. (scalar(@data)-1));
146             }
147              
148             ### Create our randomizer.
149 0         0 my %randomizer = ();
150 0         0 @randomizer{@data} = @data;
151 0         0 return values(%randomizer);
152             }
153              
154             sub elements {
155 24     24 1 37 my $self = shift;
156              
157 24 100       52 if ($self->is_ordered) {
158 1         3 return $self->ordered_elements;
159             } else {
160 23         65 return $self->unordered_elements;
161             }
162             }
163              
164             sub ordered_elements {
165 17     17 1 884 my $self = shift;
166 17 50       34 if ($self->is_unique) {
167 17         23 return $self->_filter_duplicates(@{$self->_data});
  17         53  
168             } else {
169 0         0 return @{$self->{_data}}
  0         0  
170             }
171             }
172              
173             sub unordered_elements {
174 24     24 1 531 my $self = shift;
175            
176             ### Determine whether we need the unique subset of the data and get the
177             ### relevant entries..
178 24         39 my @data = ();
179 24 50       46 if ($self->is_unique) {
180 24         35 @data = $self->_filter_duplicates(@{$self->_data});
  24         51  
181             } else {
182 0         0 @data = @{$self->_data};
  0         0  
183             }
184              
185             ### Randomize our subset and return it.
186 24         47 my %randomizer = ();
187 24         104 @randomizer{@data} = @data;
188 24         149 return values(%randomizer);
189             }
190              
191             sub insert {
192 42     42 1 2143 my $self = shift;
193 42         88 my @elements = @_;
194              
195 42         46 push @{$self->_data}, @elements;
  42         90  
196             }
197              
198             sub _items_match {
199 4     4   7 my ($a, $b) = @_;
200              
201             ### If only one is a ref, they don't match.
202 4 50 33     28 if (ref($a) and not ref($b)
      33        
      33        
203             or ref($b) and not ref($a)) {
204 0         0 return 0;
205             }
206              
207             ### If neither is a ref, just do an eq comparison.
208 4 50 33     19 if (not ref($a) and not ref($b)) {
209 4         15 return ($a eq $b);
210             }
211            
212             ### They're both refs. If they aren't refs to the same thing, return false.
213 0 0       0 if (ref($a) ne ref($b)) {
214 0         0 return 0;
215             }
216              
217             ### I don't know how else they could be different...
218 0         0 return 1;
219             }
220              
221             sub remove {
222 2     2 1 13 my $self = shift;
223 2         5 my @elements = @_;
224              
225 2         5 LIST: foreach my $el (@elements) {
226 2         4 ELEMENTS: for (my $i = 0; $i < scalar(@{$self->_data}); $i++) {
  4         8  
227             ### If these two items match, do the deed.
228 4 100       11 if (_items_match($el,$self->_data->[$i])) {
229             ### Splice it out of the element list.
230 2         5 splice(@{$self->_data}, $i--, 1);
  2         6  
231             ### Save a little time if we know this is a unique set. In that
232             ### case, we can just skip to examining the next item in the list
233             ### of requested removals.
234 2 50       7 next LIST if ($self->is_unique);
235             }
236             }
237             }
238             }
239              
240             sub first {
241 8     8 1 44 my $self = shift;
242 8         21 my @els = $self->ordered_elements;
243 8         28 return $els[0];
244             }
245              
246             sub last {
247 7     7 1 29 my $self = shift;
248 7         20 my @els = $self->ordered_elements;
249 7         49 return $els[-1];
250             }
251              
252             ### Returns all matches (a set) or an empty set
253             sub search {
254 4     4 1 12 my $self = shift;
255            
256 4         5 my $condition;
257 4 50 66     23 if (scalar(@_) == 1 and ref($_[0]) eq 'HASH') {
    100          
258             ### This is a hashref, so search by it.
259 0         0 $condition = $_[0];
260             } elsif (scalar(@_) == 1) {
261             ### We got a scalar value only... we'll want to compare the value of
262             ### the stored thing against it.
263 1         3 $condition = $_[0];
264             } else {
265 3         11 my %args = @_;
266 3         7 $condition = \%args;
267             }
268              
269 4         10 my $resultset = __PACKAGE__->new();
270              
271             ### Loop through the elements in the current set, pushing matches into the
272             ### result set.
273 4         13 foreach my $obj (@{$self->_data}) {
  4         9  
274 20 100       31 if (_obj_matches_properties($obj, $condition)) {
275 6         14 $resultset->insert($obj);
276             }
277             }
278              
279             ### Always returns a Set::Object thing, meaning we can do chaining.
280 4         16 return $resultset;
281             }
282              
283             ### Returns the first matched object or undef.
284             sub find {
285 7     7 1 1070 my $self = shift;
286            
287 7         10 my $condition;
288 7 50 66     47 if (scalar(@_) == 1 and ref($_[0]) eq 'HASH') {
    100          
289             ### This is a hashref, so search by it.
290 0         0 $condition = $_[0];
291             } elsif (scalar(@_) == 1) {
292             ### We got a scalar value only... we'll want to compare the value of
293             ### the stored thing against it.
294 6         10 $condition = $_[0];
295             } else {
296 1         4 my %args = @_;
297 1         2 $condition = \%args;
298             }
299              
300             ### Loop through the elements in the current set, returning the first one
301             ### that matches completely.
302 7         10 foreach my $obj (@{$self->_data}) {
  7         15  
303 30 100       58 if (_obj_matches_properties($obj, $condition)) {
304 4         15 return $obj;
305             }
306             }
307              
308             ### No matches. Return undef. ->find does *not* chain.
309 3         10 return undef;
310             }
311              
312             sub _obj_matches_properties {
313 50     50   57 my $obj = shift;
314 50         51 my $opt = shift;
315              
316             ### If the option we're matching against is not a ref, then we're trying to
317             ### compare against a scalar value.
318 50 100       105 if (not ref($opt)) {
    100          
319 30 100       48 if (not ref($obj)) {
320 18         77 return ($obj eq $opt);
321             } else {
322 12         36 return 0;
323             }
324             } elsif (not ref($obj)) {
325             ### If the constraint *is* a ref, but the thing stored isn't, return false.
326 9         27 return 0;
327             }
328              
329             ### Ok, so our constraint is a ref. We need to assume it's a hashref and
330             ### search by property.
331 11         24 foreach my $field (keys(%$opt)) {
332             ### First, if our constraint is a hashref, then we need to test against
333             ### the object's properties. This would look like this:
334             ### $set->find(a=>4);
335 11 50       25 if (ref($opt->{$field}) eq 'HASH') {
336 0 0       0 if (not _obj_matches_properties($obj->{$field}, $opt->{$field})) {
337 0         0 return 0;
338             }
339             } else {
340 11         16 my $opt_version = $opt->{$field};
341 11         12 my $obj_version = undef;
342             ### Ok, so we're not comparing the value to a hashref -- that means
343             ### we just want to compare the values directly. In that case, we
344             ### want to *prefer* to check against the output of a method, and
345             ### fall back to a hash key if necessary (and possible).
346 11         12 my $can_do = 0;
347 11         14 eval {$can_do = $obj->can($field)};
  11         64  
348              
349             ### If we got a die error, then this object isn't really an object,
350             ### it's probably just a hashref that can't do methods. In that case
351             ### let's just check if it has the property.
352 11 50       27 if ($@) {
353             ### Assume $obj is a hash ref. If it's not, we want to know that
354             ### bad data is being inserted into our set; perl will barf for us.
355 11 50       21 if (exists $obj->{$field}) {
356             ### If there's such a field in this hashref, set it.
357 11         19 $obj_version = $obj->{$field};
358             } else {
359             ### If no such field, we know it's not a match, so return false.
360 0         0 return 0;
361             }
362             } else {
363 0         0 $obj_version = $obj->{$field};
364             }
365              
366 11 50 33     160 if (not defined $opt_version and not defined $obj_version) {
    50 33        
    100          
367             ### Do nothing, this counts as a match.
368             } elsif (not defined $opt_version or not defined $obj_version) {
369             ### Only one is undef ... no match.
370 0         0 return 0;
371             } elsif ($opt_version ne $obj_version) {
372 5         19 return 0;
373             }
374             }
375             }
376              
377 6         19 return 1;
378             }
379              
380              
381              
382             ######################################################################
383             ### Overloading
384             ######################################################################
385              
386             ### Borrowed from Set::Object.
387             use overload
388             '""' => \&as_string,
389             '@{}' => \&as_array,
390              
391             ### In a boolean context, report whether or not we're an empty set.
392             'bool' => sub {
393 2     2   51 my $self = shift;
394 2 100       6 return ($self->size) ? 1 : 0;
395             },
396             # '+' => \&op_union,
397             # '*' => \&op_intersection,
398             # '%' => \&op_symm_diff,
399             # '/' => \&op_invert,
400             # '-' => \&difference,
401             # '==' => \&equal,
402             # '!=' => \¬_equal,
403             # '<' => \&proper_subset,
404             # '>' => \&proper_superset,
405             # '<=' => \&subset,
406             # '>=' => \&superset,
407             # '%{}' => sub { my $self = shift;
408             # my %h = {};
409             # tie %h, $self->tie_hash_pkg, [], $self;
410             # \%h },
411 6     6   11325 fallback => 1;
  6         7374  
  6         95  
412              
413              
414             ### In a boolean context, the set reports whether or not it's empty.
415             sub is_empty {
416 0     0 1 0 my $self = shift;
417 0 0       0 return ($self->size) ? 0 : 1;
418             }
419              
420             ### In string context, it should return something like the output of ref() ...
421             ### Set::Toolkit(a b c HASH(0x8894880) d e)
422             sub as_string {
423 4     4 1 1653 my $self = shift;
424            
425             ### Check if we're calling this as a method on the
426 4         7 eval {
427 4 50       26 my $isa = $self->isa(__PACKAGE__)
428             or die 'Tried to use as_string on something other than a Set::Toolkit object';
429             };
430 4 50       11 croak ($@) if ($@);
431              
432 4         14 ref($self).'(' . (join ' ', $self->elements) . ')'
433             }
434              
435             ### In an array context, it's tied to an array under the hood.
436             sub as_array {
437 12     12 1 5124 my $self = shift;
438 12         21 my @arr = ();
439 12         81 tie @arr, __PACKAGE__.'::TieArray', $self;
440 12         72 return \@arr;
441             }
442              
443             ### Allow for array versions.
444 0     0   0 sub _tie_array_pkg { "Set::Toolkit::TieArray" };
445             { package Set::Toolkit::TieArray;
446 6     6   1785 use Carp qw(carp croak cluck confess);
  6         132  
  6         27234  
447              
448             sub toolkit {
449 25     25   31 my $self = shift;
450 25 50       55 $self->{toolkit} = $_[0] if (@_);
451 25         114 return $self->{toolkit};
452             }
453              
454             sub TIEARRAY {
455 12     12   17 my $class = shift;
456 12         19 my $toolkit = shift;
457              
458             ### Instead of just flattening the data into an array, we keep a copy of
459             ### the toolkit object around. This means that changes in context (i.e.
460             ### uniqueness or orderedness constraints) are respected in the array
461             ### treatment.
462 12         34 my $self = {toolkit => $toolkit};
463              
464             ### Return the blessed version.
465 12         31 bless $self, $class;
466 12         31 return $self;
467             }
468            
469             sub FETCH {
470 4     4   8 my $self = shift;
471 4         7 my $index = shift;
472              
473             ### Grab a list of the indecies of each element. This is context
474             ### sensitive.
475 4         13 my @indecies = $self->toolkit->_ordered_element_indecies;
476              
477             ### The value of $indecies[$index] is the index of the value in the
478             ### original data.
479 4         13 return $self->toolkit->_data->[$indecies[$index]];
480             }
481              
482             sub STORE {
483 0     0   0 my $self = shift;
484 0         0 my $index = shift;
485 0         0 my $value = shift;
486            
487             ### Grab a list of the indecies of each element. This is context
488             ### sensitive.
489 0         0 my @indecies = $self->toolkit->_ordered_element_indecies;
490              
491             ### If we're setting an element that's bigger than our list, tack it on
492             ### to the end of the toolkit's data array.
493 0 0       0 if ($index >= scalar(@indecies)) {
494 0         0 push @{$self->toolkit->_data}, $value;
  0         0  
495             } else {
496             ### The value of $indecies[$index] is the index of the value in the
497             ### original data.
498 0         0 $self->toolkit->_data->[$indecies[$index]];
499             }
500              
501             }
502              
503             sub FETCHSIZE {
504 4     4   6 my $self = shift;
505             ### Return the size of the elements under consideration (context
506             ### sensitive).
507 4         12 return scalar($self->toolkit->elements);
508             }
509              
510             sub STORESIZE {
511 0     0   0 my $self = shift;
512 0         0 my $count = shift;
513             ### Does nothing.
514 0         0 carp("Setting sizes in array context is not yet supported.");
515 0         0 return;
516             }
517              
518             sub EXTEND {
519 0     0   0 my $self = shift;
520 0         0 my $count = shift;
521             ### Does nothing.
522 0         0 return;
523             }
524              
525             sub EXISTS {
526 0     0   0 my $self = shift;
527 0         0 my $index = shift;
528 0         0 my @els = $self->toolkit->elements;
529 0 0       0 return (defined $els[$index]) ? 1 : 0;
530             }
531              
532             sub DELETE {
533 0     0   0 my $self = shift;
534 0         0 my $index = shift;
535 0         0 return $self->STORE($index,'');
536             }
537              
538             sub PUSH {
539 1     1   2 my $self = shift;
540 1         4 $self->toolkit->insert(@_);
541             }
542              
543             sub POP {
544 1     1   2 my $self = shift;
545            
546             ### Grab a list of the indecies of each element. This is context
547             ### sensitive.
548 1         3 my @indecies = $self->toolkit->_ordered_element_indecies;
549            
550 1         3 my $index = pop(@indecies);
551 1         2 my $element = $self->toolkit->_data->[$index];
552            
553 1         2 splice(@{$self->toolkit->_data}, $index, 1);
  1         4  
554 1         4 return $element;
555             }
556              
557             sub CLEAR {
558 0     0   0 my $self = shift;
559 0         0 $self->toolkit->_data = [];
560             }
561              
562             sub SHIFT {
563 1     1   2 my $self = shift;
564            
565             ### Grab a list of the indecies of each element. This is context
566             ### sensitive.
567 1         3 my @indecies = $self->toolkit->_ordered_element_indecies;
568            
569 1         4 my $index = shift(@indecies);
570 1         5 my $element = $self->toolkit->_data->[$index];
571            
572 1         3 splice(@{$self->toolkit->_data}, $index, 1);
  1         4  
573 1         6 return $element;
574             }
575              
576             sub UNSHIFT {
577 2     2   4 my $self = shift;
578 2         4 my $unshifted = unshift @{$self->toolkit->_data}, @_;
  2         6  
579 2         10 return $unshifted;
580             }
581              
582             sub SPLICE {
583 2     2   5 my $self = shift;
584 2         7 my ($pos, $rem, @els) = @_;
585              
586 2         6 my @indecies = $self->toolkit->_ordered_element_indecies;
587 2         4 my $index = $indecies[$pos];
588            
589 2         4 splice(@{$self->toolkit->_data}, $index, $rem, @els);
  2         6  
590             }
591             }
592              
593             =head1 NAME
594              
595             Set::Toolkit - searchable, orderable, flexible sets of (almost) anything.
596              
597             =head1 VERSION
598              
599             Version 0.11
600              
601             =head1 SYNOPSIS
602              
603             The Set Toolkit intends to provide a broad, robust interface to sets of
604             data. Largely inspired by Set::Object, a default set from the Set Toolkit
605             should behave similarly enough to those created by Set::Object that
606             interchanging between the two is fairly easy and intuitive.
607              
608             In addition to the set functionality already available around the CPAN,
609             the Set Toolkit provides the ability to perform fairly complex, chained
610             searches against the set, ordered and unordered considerations, as well
611             as the ability to enforce or relax a uniqueness constraint (enforced by
612             default).
613              
614             use Set::Toolkit;
615              
616             $set = Set::Toolkit->new();
617             $set->insert(
618             'a',
619             4,
620             {a=>'abc', b=>123},
621             {a=>'abc', b=>456, c=>'foo'},
622             {a=>'abc', b=>456, c=>'bar'},
623             '',
624             {a=>'ghi', b=>789, c=>'bar'},
625             {
626             x => {
627             y => "hello",
628             z => "world",
629             },
630             },
631             );
632              
633             die "we didn't add enough items!"
634             if ($set->size < 4);
635              
636             ### Find single elements.
637             $el1 = $set->find(a => 'ghi');
638             $el2 = $set->find(x => { y=>'hello' });
639              
640             ### Print "Hello, world!"
641             print "Hello, ", $el2->{x}->{z}, "!\n";
642              
643             ### Search for result sets.
644             ### $resultset will contain:
645             ### {a=>'abc', b=>456, c=>'foo'},
646             ### {a=>'abc', b=>456, c=>'bar'},
647             $resultset => $set->search(a => 'abc')
648             ->search(b => 456);
649              
650             ### $bar will be: {a=>'ghi', b=>789, c=>'bar'},
651             $bar = $set->search(a => 'abc')
652             ->search(b => 456)
653             ->find(c => 'bar');
654              
655             ### Get the elements in the order they were inserted. These are equivalent:
656             @ordered = $set->ordered_elements;
657              
658             $set->is_ordered(1);
659             @ordered = $set->elements;
660            
661             ### Get the elements in hash-random order. These two are equivalent:
662             @unordered = $set->unordered_elements
663              
664             $set->is_ordered(0);
665             @unordered = $set->elements;
666              
667             =head1 DESCRIPTION
668              
669             This module implements a set objects that can contain members of (almost)
670             any type, and provides a number of attached helpers to allow set and element
671             manipulation at a variety of levels. By "almost", I mean that it won't let
672             you store C as a value, but not for a good reason: that's just how
673             L did it, and I haven't had a chance to think about the pros
674             and cons yet. Probably in the future it'll be a settable flag.
675              
676             The set toolkit is largely inspired by the work done in Set::Object, but with
677             some notable differences: this package ...
678              
679             =over
680              
681             =item * ... provides for I sets
682              
683             =item * ... is pure perl.
684              
685             =item * ... is slower for the above reasons (and more!)
686              
687             =item * ... provides mechanisms for searching set elements.
688              
689             =item * ... does not flatten scalars to strings.
690              
691             =item * ... probably some other stuff.
692              
693             =back
694              
695             In general, take a look at L first to see if it will suit your
696             needs. If not, give Set::Toolkit a spin.
697              
698             By default, this package's sets are intended to be functionally identical
699             to those created by Set::Object (or close to it). That is, without specifying
700             differently, sets created from the Set::Toolkit will be an I
701             collection of things I.
702              
703             =head1 EXPORT
704              
705             None at this time.
706              
707             =head1 FUNCTIONS
708              
709             =head2 Construction
710              
711             =head3 new
712              
713             Creates a new set toolkit object. Right now it doesn't take parameters,
714             because I have not codified how it should work.
715              
716             =head2 Set manipulation
717              
718             =head3 B
719              
720             Insert new elements into the set.
721              
722             ### Create a set object.
723             $set = Set::Toolkit->new();
724            
725             ### Insert two scalars, an array ref, and a hash ref.
726             $set->insert('a', 'b', [2,4], {some=>'object'});
727              
728             Duplicate entries will be silently ignored when the set's B
729             constraint it set. (This behavior is likely to change in the future. What
730             will probably happen later is the element will be added and masked. That
731             will probably be a setting =)
732              
733             =head3 B
734              
735             Removes elements from the set.
736              
737             ### Create a set object.
738             $set = Set::Toolkit->new();
739            
740             ### Insert two scalars, an array ref, and a hash ref; the set size will
741             ### be 4.
742             $set->insert('a', 'b', [2,4], {some=>'object'});
743              
744             ### Remove the scalar 'b' from the set. The set size will be 3.
745             $set->remove('b');
746              
747             Note that removing things removes I of it (this only really
748             matters in non-unique sets).
749              
750             Removing references might catch you off guard: though you can B
751             object literals, you can't remove them. That's because each time you create
752             a new literal, you get a new reference. Consider:
753              
754             ### Create a set object.
755             $set = Set::Toolkit->new();
756            
757             ### Insert two literal hashrefs.
758             $set->insert({a => 1}, {a => 2});
759              
760             ### Remove a literal hashref. This will have no effect, because the two
761             ### objects (inserted and removed) are *different references*.
762             $set->remove({a => 1});
763              
764             However, the following should work instead
765              
766             ### Create a set object.
767             $set = Set::Toolkit->new();
768            
769             ### Create our two hashes.
770             ($hash_a, $hash_b) = ({a=>1}, {a=>2});
771              
772             ### Insert the two references.
773             $set->insert($hash_a, $hash_b);
774              
775             ### Remove a hash reference. This will work; it's the same reference as
776             ### what was inserted.
777             $set->remove($hash_a);
778              
779             Obviously the same applies for all references.
780              
781             =head2 Set inspection
782              
783             =head3 B
784              
785             Returns a list of the elements in the set. The content of the list is
786             sensitive to the set context, defined by B, B, and
787             possibly other settings later.
788              
789             =head3 B
790              
791             Returns a list of the elements in insertion order, regardless of whether the
792             set thinks its ordered or unordered. This can be thought of as a temporary
793             coercion of the set to ordered for the duration of the fetch, only.
794              
795             =head3 B
796              
797             Returns a list of the elements in a random order, regardless of whether the
798             set thinks its ordered or unordered. This can be thought of as a temporary
799             coercion of the set to unordered for the duration of the fetch, only.
800              
801             The random order of the set relies on perl's treatment of hash keys
802             and values. We're using a hash under the hood.
803              
804             =head3 B
805              
806             This method will simply tell you if your set is empty. Returns 0 or 1.
807              
808             =head3 B and B
809              
810             The twin methods C and C do not take any arguments, they simply
811             report the first or last element of the set. Be aware that I
812             order!> Consider:
813              
814             my $set = Set::Toolkit->new();
815             $set->insert(qw(a b c d e f));
816             $set->is_ordered(0);
817              
818             ### prints something like "c a d e b f"
819             print join(' ', @$set);
820              
821             ### prints "a .. f"
822             print $set->first, ' .. ', $set->last;
823              
824             The first element in an I set would be an ephemeral, ever-changing
825             value and, therefore, useless (I think =) So C and C are always
826             performed with the I constraint that C<$set-Eis_ordered(1)>.
827              
828             =head3 B and B
829              
830             Searching allows you to find subsets of your current set that match certain
831             criteria. Some effort has been made to make the syntax as simple as possible,
832             though some complexity is present in order to provide some power.
833              
834             Searches take one argument, a constraint, that can be specified in two primary
835             ways:
836              
837             =over
838              
839             =item * As a scalar value
840              
841             =item * As a hash reference
842              
843             =back
844              
845             =head4 Scalar searches
846              
847             Specifying a constraint as a scalar value makes a very simple check against
848             any scalar values contained in your set (and only such values). Thus, if you
849             search for "b", you will get a subset of the parent set that contains one
850             string "b" for each such occurrance in the super set.
851              
852             Consider the following:
853            
854             ### Create a new set.
855             $set = Set::Toolkit->new();
856              
857             ### Insert some values.
858             $set->insert(qw(a b c d e));
859              
860             ### Do a search, and then a find.
861              
862             ### $resultset is now a set object with one entry: 'b'
863             $resultset = $set->search('b');
864            
865             ### $resultset is now an empty set object (because we didn't insert any
866             ### strings "x").
867             $resultset = $set->('x');
868              
869             For scalars, it probably won't generally be useful to use search. You'll
870             probably want to use find() instead, which simply returns the value sought,
871             rather than a set of matches:
872              
873             ### Using the set above, $match now contains 'b'.
874             my $match = $set->find('b');
875              
876             However, there is a case in which you might want to use scalar searches:
877             in sets that are not enforcing uniqueness.
878              
879             ### Turn off the uniqueness constraint.
880             $set->is_unique(0);
881              
882             ### Add some more letters.
883             $set->insert(qw(a c e g i j));
884              
885             ### Now do some searches:
886              
887             ### $resultset will contain <'c','c'>
888             $resultset->search('a');
889              
890             This may be useful for counting occurrances, such as:
891              
892             print "There are ", $set->search('a')->size, " occurances of 'a'.\n";
893              
894             =head4 Property searches
895              
896             On the other hand, searching by property values will probably be useful
897             more often. Consider the following set:
898              
899             ### Create our set.
900             $works = Set::Toolkit->new();
901              
902             ### Insert some complex values:
903             $works->insert(
904             { name => {first=>'Franz', last=>'Kafka'},
905             title => 'Metamorphosis',
906             date => '1915'},
907              
908             { name => {first=>'Ovid', last=>'unknown'},
909             title => 'Metamorphosis',
910             date => 'AD 8'},
911              
912             { name => {first=>'Homer', last=>undef},
913             title => 'The Iliad',
914             date => 'unknown'},
915              
916             { name => {first=>'Homer', last=>undef},
917             title => 'The Odyssey',
918             date => 'unknown'},
919              
920             { name => {first=>'Ted', last=>'Chiang'},
921             title => 'Understand',
922             date => '1991'},
923              
924             { name => {first=>'John', last=>'Calvin'},
925             title => 'Institutes of the Christian Religion',
926             date => '1541'},
927             );
928              
929             We can perform an arbitrarily complex subsearch of these fields, as follows:
930              
931             ### $homeric_works is now a set object containing the same hash references
932             ### as the superset, "works", but only those that matched the first name
933             ### "Homer" and the last name *undef*.
934             my $homeric_works = $authors->search({
935             name => {
936             first => 'Homer',
937             last => undef,
938             });
939              
940             ### We can get a specific work, "The Oddysey," for example, by a second
941             ### "search" (or "find"):
942              
943             ### $oddysey_works is now a set of one.
944             my $oddysey_works = $homeric_works->search(title=>'The Odyssey');
945              
946             ### We can get the instance (instead of a set) with a "find":
947             my $oddysey_work = $homeric_works->find(title=>'The Odyssey');
948              
949             ### Which we could have gotten more easily by issuing a "find" on the
950             ### original set:
951             my $oddysey_work = $works->find(title=>'The Odyssey');
952              
953             Searches can also be chained, if that's desirable for any reason, and
954             B can be included in the chain, as long as it is the last link.
955              
956             Note that this is I a speed-optimized scan at this point (but it
957             shouldn't be brutally slow in most cases).
958              
959             ### Get a resultset of one.
960             my $resultset = $works->search(name=>{first=>'Homer'})
961             ->search(title=>'The Iliad');
962            
963             And you can search against multiple values:
964              
965             ### Search against title and date to get Ovid's "Metamorphosis" (yeah, I
966             ### realize his was plural, but give me a break here =)
967              
968             ### Get the set.
969             my $resultset = $works->search(
970             title => 'Metamorphosis',
971             date => 'AD 8'
972             );
973              
974             ### Get the item.
975             my $result = $works->find(
976             title => 'Metamorphosis',
977             date => 'AD 8'
978             );
979              
980             =head3 B
981              
982             Returns the size of the set. This is context sensitive:
983              
984             $set = Set::Toolkit->new();
985             $set->is_unique(0);
986             $set->insert(qw(d e a d b e e f));
987              
988             ### Prints:
989             ### The set size is 8!
990             ### The set size is 5!
991             print 'The set size is ', $set->size, '!';
992             $set->is_unique(1);
993             print 'The set size is ', $set->size, '!';
994              
995             =head2 Set introspection
996              
997             =head3 B
998              
999             Returns a boolean value depending on whether the set is currently considering
1000             itself as ordered or unordered. Also a setter to change the set's context.
1001              
1002             =head3 B
1003              
1004             Returns a boolean value depending on whether the set is currently considering
1005             itself as unique or duplicable (with respect to its elements). Also a setter
1006             to change the set's context.
1007              
1008             =head2 Contextual considerations
1009              
1010             =head3 B
1011              
1012             Sets can be taken in a boolean context (v0.10). This can be done implicitly
1013             by using it in a boolean context. Empty sets are considered I, while
1014             sets with elements are considered I. Thus, in boolean contexts, the set
1015             answers the question, "Does this set have members?"
1016              
1017             my $set = Set::Toolkit->new();
1018            
1019             if ($set) {
1020             print "The set has members!";
1021             } else {
1022             print "The set is empty!";
1023             }
1024              
1025             Under the hood, this just returns
1026              
1027             return ($self->size) ? 1 : 0;
1028              
1029             =head3 B
1030              
1031             =over
1032              
1033             =item B
1034              
1035             =back
1036              
1037             Sets can be manipulated in an array context as well. An array context
1038             enforces set order, since an array without order is just ... well, a set =)
1039             That means that for all array considerations, B
1040             C>. Normal context will return when considering the array as
1041             a set toolkit.
1042              
1043             The examples below use sets with simple alphanumeric scalars. You can, of
1044             course, feel free to use objects or refs of any kind.
1045              
1046             Let's look at some code.
1047            
1048             B
1049              
1050             my $set = Set::Toolkit->new();
1051             $set->insert(qw(a b c d e f));
1052              
1053             B
1054              
1055             ### Prints: a, b, c, d, e, f
1056             print join(', ', @$set);
1057              
1058             B
1059              
1060             ### $first is now 'a'. This is the same as $set->first, except that
1061             ### shifting is destructive.
1062             my $first = shift @$set;
1063              
1064             ### $first will now be 'x'
1065             unshift @$set, 'x';
1066             $first = $set->first;
1067              
1068             B
1069              
1070             ### $last is now 'f'. This is the same as $set->last, except that
1071             ### popping is destructive.
1072             my $last = pop @$set;
1073              
1074             ### $last will now be 'z'
1075             push @$set, 'z';
1076             $last = $set->last;
1077              
1078             B
1079              
1080             my $before = $set->[3]; ### $set->[3] is 'd'.
1081             $set->[3] = 8; ### Set it to '8'.
1082             my $after = $set->[3]; ### Now it's '8'.
1083              
1084             B (Note that setting the size is not yet
1085             supported. You'll get a warning if you try to do it.)
1086              
1087             ### These are equivalent.
1088             my $size = $set->size;
1089             my $scalar = scalar(@$set);
1090              
1091             B
1092              
1093             ### Remove the letter 'c' (position 2)
1094             splice(@$set, 2, 1);
1095              
1096             ### Replace the letter 'e' (now position 3) with 'm', 'n', 'o'
1097             splice(@set, 3, 1, qw(m n o));
1098              
1099             =head3 B (B)
1100              
1101             =over
1102              
1103             =item B
1104              
1105             =back
1106              
1107             In string context, the array is printed in a manner reminiscent of how refs
1108             are printed. For example, a hash C<$hash = {a=E1}> may print as
1109             C. Similarly, a toolkit will print
1110             C, where the ellipsus stands for a space-delimited
1111             list of the set's contents.
1112              
1113             For example,
1114              
1115             my $set = Set::Toolkit->new();
1116             $set->insert(qw(a b c));
1117              
1118             ### Prints, for example: "Set::Toolkit(a c b)"
1119             print "$set";
1120              
1121             The above example is using an unordered set, so the print order is unordered.
1122             References will be treated by Perl's native ref stringification:
1123              
1124             my $set = Set::Toolkit->new();
1125             $set->insert('a', {b=>2}, 4);
1126              
1127             ### Prints something like: "Set::Toolkit(HASH(0x9301880) 4 a)"
1128             print "$set";
1129              
1130             =head1 When should this module be used?
1131              
1132             You might want to use this module if the following are generally true:
1133              
1134             =over
1135              
1136             =item * You aren't desparate for speed.
1137              
1138             =item * You want to be able to search (and subsearch!) your sets easily.
1139              
1140             =item * You want I sets.
1141              
1142             =back
1143              
1144             =head1 When shouldn't this module be used?
1145              
1146             This module probably isn't right for you if you:
1147              
1148             =over
1149              
1150             =item * Need it fast, fast, fast!
1151              
1152             =item * You don't care about searching your sets.
1153              
1154             =item * You don't care about ordering your sets.
1155              
1156             =back
1157              
1158             In these are true, I would take a look at Set::Object instead.
1159              
1160             =head1 NOTES
1161              
1162             Set::Toolkit sets contain "things" or "members" or "elements". I've avoided
1163             saying "objects" because you can really store anything in these sets, from
1164             scalars, to objects, to references.
1165              
1166             Set::Toolkit does not currently support "weak" sets as defined by Set::Object.
1167              
1168             Because uniqueness is not enforced by keying into a hash, scalars are not
1169             flattened into strings and will not lose their magicks.
1170              
1171             =head1 SPECIAL DISCLAIMER
1172              
1173             This is the first module I've released. I'm open to constructive critiques,
1174             bug reports, patches, doc patches, requests for documentation clarification,
1175             and so forth. Be gentle =)
1176              
1177             =head1 AUTHOR
1178              
1179             Sir Robert Burbridge, C<< >>
1180              
1181             =head1 BUGS
1182              
1183             Please report any bugs or feature requests to C, or through
1184             the web interface at L. I
1185             will be notified, and then you'll automatically be notified of progress on your bug as I
1186             make changes.
1187              
1188             =head1 TODO
1189              
1190             =over
1191              
1192             =item * There are some gaps in the tests. I've tested for common use cases, but they
1193             could certainly be more robust.
1194              
1195             =item * More inline code comments.
1196              
1197             =back
1198              
1199             =head1 SUPPORT
1200              
1201             You can find documentation for this module with the perldoc command.
1202              
1203             perldoc Set::Toolkit
1204              
1205             =over 4
1206              
1207             =item * RT: CPAN's request tracker
1208              
1209             L
1210              
1211             =item * AnnoCPAN: Annotated CPAN documentation
1212              
1213             L
1214              
1215             =item * CPAN Ratings
1216              
1217             L
1218              
1219             =item * Search CPAN
1220              
1221             L
1222              
1223             =back
1224              
1225             =head1 ACKNOWLEDGEMENTS
1226              
1227             Thanks to Jean-Louis Leroy and Sam Vilain, the developers/maintainers of
1228             Set::Object, for lots of concepts, etc. I'm not actually using any borrowed
1229             code under the hood, but I plan to in the future.
1230              
1231             =head1 COPYRIGHT & LICENSE
1232              
1233             Copyright 2010 Sir Robert Burbridge, all rights reserved.
1234              
1235             This program is free software; you can redistribute it and/or modify it
1236             under the same terms as Perl itself.
1237              
1238             =cut
1239              
1240             1; # End of Set::Toolkit