use strict; use warnings; use Algorithm::Diff; my $digest = bless {root => {}, maxLevel => 3}; while () { chomp; next unless length; $digest->add ($_); } $digest->mergeTails (); $digest->print (); sub add { my ($self, $line, $level, $context) = @_; $level ||= 1; $context ||= $self->{root}; if ($level == $self->{maxLevel} or $line !~ s/(\S*?)\s*\W\s+//) { push @{$context->{tails}}, [$line =~ /(\S+)/g]; return; } my $prefix = $1; $context->{$prefix} ||= {}; $context = $context->{$prefix}; $self->add ($line, 1 + $level, $context); } sub mergeTails { my ($self, $context) = @_; $context ||= $self->{root}; unless (exists $context->{tails}) { $self->mergeTails ($context->{$_}) for keys %$context; return; } my @tails = sort {length $a <=> length $b} @{$context->{tails}}; my @groups; push @{$groups[@$_]}, $_ for @tails; @groups = grep {defined $_} @groups; for my $group (@groups) { my @ref = grep {length} @{$group->[-1]}; my @org = @ref; my $count = 1; pop @$group; while (@$group) { my @new = @{pop @$group}; my @diffs = Algorithm::Diff::diff (\@ref, \@new); for my $change (@diffs) { next unless $change->[0][0] eq '-'; $ref[$change->[0][1]] = ''; } ++$count; } for (0 .. $#ref) { next if length $ref[$_]; $org[$_] = '*****'; } push @{$context->{digest}}, [join (' ', @org), $count]; } } sub print { my ($self, $context, $indent) = @_; $context ||= $self->{root}; $indent ||= ''; if (exists $context->{digest}) { print "$indent($_->[1]) \t$_->[0]\n" for sort {$a->[0] cmp $b->[0]} @{$context->{digest}}; return; } for (sort keys %$context) { print "$indent$_\n"; $self->print ($context->{$_}, $indent . ' '); } } __DATA__