File Coverage

blib/lib/List/Sliding/Changes.pm
Criterion Covered Total %
statement 40 40 100.0
branch 8 12 66.6
condition 6 8 75.0
subroutine 10 10 100.0
pod 0 2 0.0
total 64 72 88.8


line stmt bran cond sub pod time code
1             package List::Sliding::Changes;
2 3     3   62378 use strict;
  3         7  
  3         113  
3              
4 3     3   17 use Exporter;
  3         6  
  3         138  
5 3     3   16 use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  3         11  
  3         285  
6 3     3   17 use Carp qw(croak);
  3         13  
  3         1305  
7              
8             $VERSION = 0.03;
9             @ISA = qw (Exporter);
10              
11             @EXPORT_OK = qw ( &find_new_indices &find_new_elements );
12              
13             =head1 NAME
14              
15             List::Sliding::Changes - Extract new elements from a sliding window
16              
17             =head1 SYNOPSIS
18              
19             =for example begin
20              
21 1     1   26600 use strict;
  1         2  
  1         34  
22 1     1   6 use Tie::File;
  1         2  
  1         30  
23 1     1   18 use List::Sliding::Changes qw(find_new_elements);
  1         2  
  1         187  
24              
25             my $filename = 'log.txt';
26             my @log;
27             tie @log, 'Tie::File', $filename
28             or die "Couldn't tie to $filename : $!\n";
29              
30             # See what has happened since we last polled
31             my @status = get_last_20_status_messages();
32              
33             # Find out what we did not already communicate
34             my (@new) = find_new_elements(\@log,\@status);
35             print "New log messages : $_\n"
36             for (@new);
37              
38             # And update our log with what we have seen
39             push @log, @new;
40              
41             =for example end
42              
43             =head1 DESCRIPTION
44              
45             This module allows you to easily find elements that were appended
46             to one of two lists. It is intended to faciliate processing wherever
47             you don't have a log but only a sliding window for events, such as
48             a status window which only displays the 20 most recent events,
49             without timestamp.
50              
51             The module assumes that the update frequency is high and will always
52             respect the longest overlap between the two sequences. To be a bit
53             faster with long lists, it searches the first list from the end,
54             assuming that the first list will be much longer than the second list.
55              
56             =head1 PUBLIC METHODS
57              
58             find_new_indices( \@OLDLIST, \@NEWLIST [, EQUALITY] )
59              
60             Returns the list of indices that were added since the last time @OLDLIST
61             was updated. This is convenient if you want to modify @NEWLIST afterwards.
62             The function accepts an optional third parameter, which should be a
63             reference to a function that takes two list elements and compares them
64             for equality.
65              
66             find_new_elements( \@OLDLIST, \@NEWLIST [, EQUALITY] )
67              
68             Returns the list of the elements that were added since the last time @OLDLIST
69             was updated.
70              
71             =cut
72              
73             sub find_new_indices {
74 32     32 0 7034 my ($old,$new,$equal) = @_;
75 32 50       74 croak "First parameter to find_new_elements() must be a reference, not $old" unless ref $old;
76 32 50       64 croak "Second parameter to find_new_elements() must be a reference, not $new" unless ref $new;
77 32   50 80   200 $equal ||= sub { $_[0] eq $_[1] };
  80         322  
78              
79 32         63 my ($new_offset,$old_offset) = (0,scalar @$old - scalar @$new);
80 32 100       74 $old_offset = 0 if $old_offset < 0;
81              
82 32         71 while ($old_offset < scalar @$old) {
83 40         45 $new_offset = 0;
84 40   66     235 while (($old_offset+$new_offset < scalar @$old)
      100        
85             and ($new_offset < scalar @$new)
86             and ($equal->($old->[$old_offset+$new_offset],$new->[$new_offset]))) {
87 68         284 $new_offset++;
88             };
89 40 100       96 last if ($old_offset+$new_offset == scalar @$old);
90 12         27 $old_offset++;
91             };
92              
93 32         168 ($new_offset .. $#$new)
94             };
95              
96             sub find_new_elements {
97 20     20 0 47 my ($old,$new,$equal) = @_;
98 20 50       55 croak "First parameter to find_new_elements() must be a reference, not $old" unless ref $old;
99 20 50       67 croak "Second parameter to find_new_elements() must be a reference, not $new" unless ref $new;
100              
101 20         40 (@{$new}[ find_new_indices($old,$new,$equal) ])
  20         97  
102             };
103              
104             1;
105             __END__