First of all, I think it is very good that you used reduce
, as it clearly shows how the algorithm works. At least, to a reader who understands functional idioms.
One problem in your code is that you keep using $a
and $b
. These two names do not convey any meaning. We could do instead:
my $prefix = reduce {
my ($current_prefix, $string) = ($a, $b);
...
Then, I would shorten the $current_prefix
until it is at the beginning of the other $string
:
until (0 == index $string, $current_prefix) {
chop $current_prefix;
}
I did not use the statement modifier form of until
or while
– I don't believe that they make code much easier to read the way a postfix if
or for
can do. Note that the empty string occurs at the beginning of any string, so the termination condition of that loop is safe.
If you do not like index
or chop
, you could equivalently use the less efficient
until ($string =~ /\A\Q$current_prefix/) {
$current_prefix =~ s/.\z//s;
}
Either way, the concept of shortening the prefix by removing one character at a time until it fits should be easier to read than all the substr
inging you used.
Cutting the prefix to the length of the string would just be an optimization:
$current_prefix = substr $current_prefix, 0, length $string;
Put together, we would get the following code:
my $prefix = reduce {
my ($current_prefix, $string) = ($a, $b);
# the prefix cannot be longer than the string
$current_prefix = substr $current_prefix, 0, length $string;
# remove characters from the prefix until it occurs at the beginning.
# "" is always a prefix, so the loop properly terminates.
until (0 == index $string, $current_prefix) {
chop $current_prefix;
}
return $current_prefix;
} @strings;
The code might be easier to understand for people who don't know reduce
if you express it in the imperative form:
my ($prefix, @strings) = @original_strings;
for my $string (@strings) {
# the prefix cannot be longer than the string
$prefix = substr $prefix, 0, length $string;
# remove characters from the prefix until it occurs at the beginning.
# "" is always a prefix, so the loop properly terminates.
until (0 == index $string, $prefix) {
chop $prefix;
}
}
# now $prefix is the prefix of all @original_strings
Oh look, it's shorter too!
@lines
and$preamble
sample? \$\endgroup\$$preamble
is shorter string. \$\endgroup\$reduce
with foreach equivalent. \$\endgroup\$