Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Re: Given When Syntax

by Marshall (Canon)
on Mar 16, 2014 at 05:52 UTC ( #1078505=note: print w/replies, xml ) Need Help??

in reply to Given When Syntax

I don't think that given()when() is any big deal.
The "old" Perl ways are more than adequate to do this.
use strict; use warnings; sub test1 #An if/else implementation.... { my ($var1) = @_; # $var1 = shift; # is slightly faster with one var # my ($var1, $var2) = @_; # slightly faster than than 2 shifts # normally this minor difference doesn't matter. # Perl can implement the if/else idea very code efficiently. return ("One") if ($var1 == 1); return ("Two") if ($var1 == 2); return ("Three") if ($var1 == 3); return undef; } #Using a hash table #Perl will not re-evaluate %hash for every entry into the sub #that table will "still be around to use". sub test2 { my $in = shift; my %hash = ( 1 => "One", 2 => "Two", 3 => "Three", ); return ($hash{$in}) if exists ($hash{$in}); return undef; }

Replies are listed 'Best First'.
Re^2: Given When Syntax
by Deep_Plaid (Acolyte) on Mar 16, 2014 at 14:09 UTC

    Hi Marshall. Thanks for including both an if/else and hash example. I wasn't aware of the more efficient way of using IF and that is what I was looking for (hard to find that stuff by just searching online). Also these make it a lot easier to me to try different solutions. Unfortunately I couldn't get your examples to work. I'm sure I'm overlooking something simple, but when I pass a value to the subroutines I'm not getting any output (see example below for hash). I thought the return would print it to the screen. Please pardon my newbness.

    Beyond that, the example I created for the question is rather simplified. In real life I have to deal with a string where I am only concerned with the first number. E.g., n.yy.zzz, where "n" is the number I need to map to. I can get this number simply enough with regex, but I'm not sure how to implement that within your example (can't test because of the output issue). Here's an attempt with an illustration of what I'm trying to do:

    use strict; use warnings; #Using a hash table #Evaluate on the first digit of the string { my ($var2) = @_; # $var1 = shift; # is slightly faster with one var # my ($var1, $var2) = @_; # slightly faster than than 2 shifts # normally this minor difference doesn't matter. # Perl can implement the if/else idea very code efficiently. return ("One") if ($var2 =~ /^1/ ); return ("Two") if ($var2 == /^2/ ); return ("Three") if ($var2 == /^3/ ); return undef; } # I want to evaluate the number 1.00.000 # I thought I would just have to pass it # to the subroutine to get a return value. test2(1.00.000);

    Once again, many thanks. I appreciate your time.

      Try this:
      sub test2 { my ($var2) = shift; # Perl can implement the if/else idea very code efficiently. return ("One") if ($var2 =~ /^1\./ ); return ("Two") if ($var2 =~ /^2\./ ); return ("Three") if ($var2 =~ /^3\./ ); return ("UNKOWN"); } print test2 ("1.00.000"), "\n"; print test2 ("11.00.000"), "\n"; print test2 (""), "\n"; __END__ prints: One UNKOWN Three
      Well I think we both know that this is a simple example.
      The regex'es aren't complicated. Just to demo the idea.

        Thanks, Marshal. That did the trick. I was an idiot for not seeing the subroutine was not identified in your code snippet as it was in my example. Thanks again for your time and patience.

      1. You have no sub test2 { ...}
      2. You show use of strict and warnings but you should have seen a message that return is not valid as your code presents it: "Can't return outside a subroutine..."
      3. You don't have any mechanism for output -- no print of the return value, were the return actually valid.

      Further, inclusion of comments which have no relevance to your problem merely makes your code more verbose, and thus, less likely to be a candidate for a reply by Monks who have other demands on their time. You shouldn't waste it if you really "appreciate (our) time."

      So here's an Rx: make sure you have your fundamentals down... and use that knowledge to recognize when you've been given an outline; not a line-for-line code solution. (Offering that kind of response is wholly in keeping with the Monastery ethos: we're here to help you learn; not to spoonfeed you with solutions!)

      Updated by addition of thoughts in last para following the ellipsis.

      Update2/clarification/correction: (yeah, my remark re comments is too broad, in insufficiently precise.) My intent -- now "better phrased" I hope -- was to point out that non-code info on the problem belongs in the narrative -- not in the code -- and that code that's been commented out should be omitted unless it casts some NEW light on the problem -- which is not the case with OP's reposting of Marshall's well-taken comment on the efficiency of the construct shown in the node to which Deep_Plaid is addressing himself.

      Questions containing the words "doesn't work" (or their moral equivalent) will usually get a downvote from me unless accompanied by:
      1. code
      2. verbatim error and/or warning messages
      3. a coherent explanation of what "doesn't work actually means.

        Sorry to waste your time, WW. I will be more thoughtful in the future. I do appreciate the time and help I have received from people on this site. I try to do as much research as I can before posting so I don't waste people's time. I am also taking a PERL course through Pluralsite and I haven't gotten through the course yet - real time deadlines have gotten in the way.

      In addition to the errors that have already been pointed out to you (especially the fact that you don't have a test2 subroutine), please note that you should pass a string as a parameter to your sub:
      should be:
      I would also submit that this:
      sub test2 { my ($var2) = @_; return ("One") if ($var2 =~ /^1/ ); return ("Two") if ($var2 == /^2/ ); return ("Three") if ($var2 == /^3/ ); return undef; }
      is not correct for input values starting with 2 and 3 and is not very efficient in terms of performance, nor in terms of coding simplicity. Immediate correction of the error is to replace == with =~ for cases 2 and 3:
      sub test2 { my ($var2) = @_; return ("One") if ($var2 =~ /^1/ ); return ("Two") if ($var2 =~ /^2/ ); return ("Three") if ($var2 =~ /^3/ ); return undef; }
      Note that Marshall corrected these two errors, but I thought it would be useful to point these out to you for your benefit. An additional improvement would be to remove the triple regex and to extract the first digit from the string only once:
      sub test2 { my $var2 = substr shift, 0, 1; return ("One") if $var2 == 1 ; return ("Two") if $var2 == 2 ; return ("Three") if $var2 == 3 ; return undef; }
      Doing the extraction of the first digit only once is cleaner, removes the risk of the error I pointed out just above and is likely to be faster if that matters (although it is true that an anchored regex is pretty fast). And it paves the way for yet another improvement, the use of an array rather than multiple evaluations. The full program may now be this:
      use strict; use warnings; my @translation = qw / Zero One Two Three/; sub test2 { return $translation[(substr shift, 0, 1)]; } print test2("1.00.000");
      Now, assuming you have a very large amount of data and performance matters, we may want to benchmark this against your (corrected) triple regex version and an intermediate solution extracting the first digit only once:
      use strict; use warnings; use Benchmark qw/cmpthese/; my @translation = qw / Zero One Two Three/; sub test1 { my $var2 = shift; return ("One") if ($var2 =~ /^1/ ); return ("Two") if ($var2 =~ /^2/ ); return ("Three") if ($var2 =~ /^3/ ); return undef; } sub test2 { my $var2 = substr shift, 0, 1; return ("One") if ($var2 == 1 ); return ("Two") if ($var2 == 2 ); return ("Three") if ($var2 == 3 ); return undef; } sub test3 { return $translation[(substr shift, 0, 1)]; } cmpthese( -1, { test_1 => sub {test1("3.01.000")}, test_2 => sub {test2("3.01.000")}, test_3 => sub {test3("3.01.000")}, } )
      which gives the following results:
      $ perl Rate test_1 test_2 test_3 test_1 1294050/s -- -11% -51% test_2 1451608/s 12% -- -45% test_3 2642856/s 104% 82% --
      As you can see, the array solution is about twice faster. Having said that, performance is often not so important (it is often fast enough anyway), and I am using quite regularly solutions similar to Marshall's proposals.

        You're using a constant input which starts with a "3" though, which unfairly penalizes test1 and test2 (it's the final situation they check for). For inputs starting with a "1", test3 is still the fastest, but the difference between it and the other tests is much smaller.

        Also, I'd recommend running your benchmarks like this:

        cmpthese(-1, { test_1 => q{ test1("3.01.000") }, test_2 => q{ test2("3.01.000") }, test_3 => q{ test3("3.01.000") }, });

        ... using q{ ... } instead of sub { ... }. If you use sub { ... } you're wrapping each iteration in an extra sub call layer. For micro-optimization benchmarks like this, that extra layer can make a significant difference to the results.

        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

        Hello again, Laurent. Let me just start by saying "I'm not worthy! I'm not worthy!" This is great stuff. I had asked about performance and you replied. This is huge because the amount of data I'm dealing with is significant. I probably won't be able to fully examine your notes until later today or tomorrow (I'm under some deadlines), but just wanted to let you know your contribution is highly valued. Hope you are having a smashing weekend. Cheers, DP.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1078505]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (10)
As of 2022-08-11 11:26 GMT
Find Nodes?
    Voting Booth?

    No recent polls found