Do you still want to go with a regex solution? :-)
No | Yes, because (in this case at least) the rindex solution is wrong.
How can one say any result is incorrect if the OPer has specified no clear set of requirements for results? I admit this is tricky, but one can say the use of s/// in the OPed example code implies that the string operand should be unchanged if no version substring match is found. _rindex() in the code here fails to do this.
It's easy enough to define an rindex-based function that handles the no-match case (and it might even be a bit faster). But the argument seems to be that one should avoid using and learning about regexes because they are a bit arcane (indeed, regexes are the most counter-intuitive programming construct I know) and may vary a bit from language to language. This argument can be extended to languages themselves: We should not use Perl because it's not Python; not use Python because it's not C++; not use C++ because it's not...
To answer a use-case such as that described in the OP, I tend to reach first for a regex solution because it most clearly represents and achieves the required operation, not because it is the fastest (although sometimes it is). Implementing the required operation in terms of index/rindex, substr, etc., is possible, but may have its own pitfalls and drawbacks in terms of basic correctness, readability and maintainability.
These are all my own very personal preferences; others may differ.
Win8 Strawberry 5.30.3.1 (64) Tue 06/07/2022 17:48:45
C:\@Work\Perl\monks
>perl
use 5.010;
use strict;
use warnings;
use constant {
PATH => 0,
VERSION => 1,
WANT => 2,
};
use Benchmark 'cmpthese';
use Test::More;
my @tests = (
# version found
[qw{
/a/b/version1/c/d
version1
/a/b/version1
}],
# version found
[qw{
/some/path/3.5.2+tcl-tk-8.5.18+sqlite-3.10.0/a/b/c
3.5.2+tcl-tk-8.5.18+sqlite-3.10.0
/some/path/3.5.2+tcl-tk-8.5.18+sqlite-3.10.0
}],
# version not found - no change in path
[qw{
/some/path/3.5.2+tcl-tk-8.5.18+sqlite-3.10.0/a/b/c
9.9.9
/some/path/3.5.2+tcl-tk-8.5.18+sqlite-3.10.0/a/b/c
}],
);
plan tests => 2*@tests;
for my $test (@tests) {
is _regex (@$test[PATH, VERSION]), $test->[WANT];
is _rindex(@$test[PATH, VERSION]), $test->[WANT];
}
cmpthese 0 => {
regex0 => sub { _regex (@{$tests[0]}[PATH, VERSION]); },
regex1 => sub { _regex (@{$tests[1]}[PATH, VERSION]); },
regex2 => sub { _regex (@{$tests[2]}[PATH, VERSION]); },
rindex0 => sub { _rindex (@{$tests[0]}[PATH, VERSION]); },
rindex1 => sub { _rindex (@{$tests[1]}[PATH, VERSION]); },
rindex2 => sub { _rindex (@{$tests[2]}[PATH, VERSION]); },
};
sub _regex {
my ($path, $version) = @_;
$path =~ s/.*\Q$version\E\K.*//s;
return $path;
}
my $r_offset; # variable creation excluded from timing
sub _rindex {
my ($path, $version) = @_;
return ($r_offset = rindex $path, $version) >= 0
? substr $path, 0, $r_offset + length $version
: $path # version not found: return path unchanged
;
}
# sub _rindex {
# my ($path, $version) = @_;
#
# return $path unless ($r_offset = rindex $path, $version) >= 0;
# return substr $path, 0, $r_offset + length $version;
#
# }
# sub _rindex { # fails if version not found
# my ($path, $version) = @_;
#
# $path = substr $path, 0, length($version) + rindex $path, $versi
+on;
#
# return $path;
# }
^Z
1..6
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
Rate regex1 regex0 regex2 rindex1 rindex0 rindex2
regex1 104916/s -- -5% -52% -83% -84% -88%
regex0 109922/s 5% -- -49% -82% -83% -88%
regex2 217207/s 107% 98% -- -64% -66% -76%
rindex1 602123/s 474% 448% 177% -- -6% -33%
rindex0 641287/s 511% 483% 195% 7% -- -29%
rindex2 899759/s 758% 719% 314% 49% 40% --
Update: Rats... Trashed the thrust of the entire post by getting the very first word wrong. Oh, well...
Give a man a fish: <%-{-{-{-<