I'd probably simplify that a bit, with a possible (minor) speed benefit.
First, we should conceptualise how to approach the problem. As a human. (My experience with Perl is that it's even better than many other languages at doing what a human does, which improves both readability and reliability.) So, what I would do, as a human, would be to look for each letter in the target string, and if all of them are there, then I have a match. That "all" is a big clue - checking modules for such behaviour, I find it in List::MoreUtils. (Side note: since other monks have pointed me to List::MoreUtils, I've found more and more use for it, so much so that I'm now wishing it were core!)
The advantage, of course, of using such a tool is that it short-circuits your code (returning at the first failure rather than continuing), and, in this case, it's written in XS, so should be faster than a simple perl duplicate. In the worst case, where all letters are present (or all but the last letter), this should perform basically as well as yours (except I don't need to create a list to count it), and on average, it should perform better. However, that should all be pretty minimal - instead, the real benefit is doing what a human does exactly the same way. And that makes it more likely to work in weird circumstances.
So, after all that build-up, I had better show some code. Note that I've renamed the function because I like my boolean-returning functions to sound, well, boolean. "Check for letters" doesn't tell me what it returns (sounds like it doesn't have a return value, actually)...
#! /usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw(all);
sub hasAllLetters
{
my ($sentence, $letters) = @_;
# all we're doing is checking for each letter.
all { $sentence =~ $_ } split //, $letters;
# same as above, but with index which I think is less readable.
#all { index($sentence, $_) >= $[ } split //, $letters;
}
# tests
for my $t (
[ "abxcd zwe rrv", "zxv" ],
[ "abxcd zwe rrv", "qxv" ],
)
{
if (hasAllLetters(@$t))
{
print "YES [$t->[0]] ~~ [$t->[1]]\n";
}
else
{
print "NO [$t->[0]] !~ [$t->[1]]\n";
}
}
(If performance was really an issue, I'd not only benchmark it, but I'd use @_ directly instead of giving them readable names.) | [reply] [d/l] |
Nice!
I was shooting for a "base perl" solution, or I would also have reached for List::MoreUtils. You're right, it's a very useful module (and I'm not saying that just because the author sits 10 meters away from me :) ).
It'd be interesting to benchmark the match vs. index operations. I suspect that benchmarking the match case might actually lead to a useful use of "study"... (Edit: it doesn't: Benchmarking "Are all these characters in this sentence?")
| [reply] |