File Coverage

blib/lib/String/CommonPrefix.pm
Criterion Covered Total %
statement 18 56 32.1
branch 4 26 15.3
condition n/a
subroutine 5 6 83.3
pod 2 2 100.0
total 29 90 32.2


line stmt bran cond sub pod time code
1             package String::CommonPrefix;
2              
3 2     2   482766 use 5.010001;
  2         6  
4 2     2   10 use strict;
  2         4  
  2         43  
5 2     2   9 use warnings;
  2         7  
  2         126  
6              
7 2     2   17 use Exporter qw(import);
  2         5  
  2         1184  
8              
9             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
10             our $DATE = '2025-08-02'; # DATE
11             our $DIST = 'String-CommonPrefix'; # DIST
12             our $VERSION = '0.021'; # VERSION
13              
14             our @EXPORT_OK = qw(
15             common_prefix
16             majority_prefix
17             );
18              
19             sub common_prefix {
20 6 50   6 1 435811 return undef unless @_; ## no critic: Subroutines::ProhibitExplicitReturnUndef
21 6         12 my $i;
22             L1:
23 6         22 for ($i=0; $i < length($_[0]); $i++) {
24 6         24 for (@_[1..$#_]) {
25 9 50       23 if (length($_) < $i) {
26 0         0 $i--; last L1;
  0         0  
27             } else {
28 9 100       49 last L1 if substr($_, $i, 1) ne substr($_[0], $i, 1);
29             }
30             }
31             }
32 6         40 substr($_[0], 0, $i);
33             }
34              
35             sub majority_prefix {
36             #my $opts = ref($_[0]) eq 'HASH' ? {%{shift}} : {};
37 0 0   0 1   return undef unless @_; ## no critic: Subroutines::ProhibitExplicitReturnUndef
38              
39 0           my @excluded = map {0} @_;
  0            
40 0           my $min_items_for_majority = int(@_/2) + 1;
41 0           my $prefix_len = 0;
42 0           while (1) {
43 0           my @items;
44             # take the next character from items that are still eligible
45 0           for my $i (0..$#_) {
46 0           my $char;
47 0 0         if ($excluded[$i]) {
    0          
48 0           $char = undef;
49             } elsif (length($_[$i]) < $prefix_len) {
50 0           $excluded[$i]++;
51 0           $char = undef;
52             } else {
53 0           $char = substr($_[$i], $prefix_len, 1);
54             }
55 0           push @items, $char;
56             }
57             #use DD; dd \@excluded;
58             # determine whether there's still a character that belong to the
59             # majority
60 0           my %freqs;
61 0 0         for (@items) { next unless defined; $freqs{$_}++ }
  0            
  0            
62 0 0         last unless keys %freqs;
63             #use DD; dd \%freqs;
64 0           my @freqs = sort { $freqs{$b} <=> $freqs{$a} } keys %freqs;
  0            
65 0           my $majority_char = $freqs[0];
66 0 0         last if $freqs{$majority_char} < $min_items_for_majority;
67 0 0         for (0 .. $#items) { next unless defined $items[$_]; unless ($items[$_] eq $majority_char) { $excluded[$_]++ } }
  0 0          
  0            
  0            
68 0           $prefix_len++;
69             #say "D: prefix_len=$prefix_len";
70             }
71              
72 0           my $first_included;
73 0 0         for (0 .. $#_) { unless ($excluded[$_]) { $first_included = $_; last } }
  0            
  0            
  0            
74 0 0         return undef unless defined $first_included; ## no critic: Subroutines::ProhibitExplicitReturnUndef
75             #say "D: first_included=$first_included";
76 0           substr($_[$first_included], 0, $prefix_len);
77             }
78              
79             1;
80             # ABSTRACT: Find prefix common to all strings
81              
82             __END__