in reply to check if all certain chars are found in a sentence

tallulah, your current answer does the job pretty well.

You should certainly break out "checkForLetters" as a subroutine, and pass in the sentence and the wanted letters.

That would make it easier to test your code, which is always a plus.

You could replace your for(;;) loop with a foreach(@a), which might make things a bit more readable.

But apart from that, I don't see any major improvements. You could do the whole thing as one grep statement, but I don't think that would add any clarity.

Edit: In the TMTOWTDI spirit, here's an (almost, except for the split) regex-free implementation:

sub checkForLetters { my ($sentence, $wantedLetters)=@_; # don't need this variable (or any of them, in # fact -- they're just here for clarity. # we could work straight out of @_ if we wanted # this terser # Also, the $[ check is just pedantic - if someone # changes $[, shoot them. my $foundLetters=scalar (grep index($sentence,$_)>=$[, split //,$wantedLetters); return length($wantedLetters)==$foundLetters; }

Mike

Replies are listed 'Best First'.
Re^2: check if all certain chars are found in a sentence
by Tanktalus (Canon) on Aug 27, 2008 at 17:34 UTC

    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.)

      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?")


      Mike