Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Given When Syntax

by Deep_Plaid (Acolyte)
on Mar 15, 2014 at 15:34 UTC ( #1078449=perlquestion: print w/ replies, xml ) Need Help??
Deep_Plaid has asked for the wisdom of the Perl Monks concerning the following question:

I have run across a situation where I need a Switch statement. After reading up on this online, it is my understanding that there really is no native Switch statement for PERL, and for later versions you should use the given/when construct (I'm using PERL v5.18.1 for Windows).

The problem is I can't seem to get the syntax right, even when using a dumbed down example I got from the web. Here's the code I'm trying to run:

use strict; use warnings; sub test2 { my $var = 2; my $i; given ($var){ when(1) { $i = "One"; } when(2) { $i = "Two"; } when(3) { $i = "Three"; } default { $i = "Other"; } } print "$var is $i"; } test2();

That gives me the following errors:

syntax error at D:/Eclipse_std_kepler_ws/StudyBuildReport/TestGiven.pl + line 9, near ") {" Global symbol "$i" requires explicit package name at D:/Eclipse_std_ke +pler_ws/StudyBuildReport/TestGiven.pl line 9. Global symbol "$i" requires explicit package name at D:/Eclipse_std_ke +pler_ws/StudyBuildReport/TestGiven.pl line 10. Global symbol "$i" requires explicit package name at D:/Eclipse_std_ke +pler_ws/StudyBuildReport/TestGiven.pl line 11.

If I can't get this to work, I'll be stuck using an if/elsif construct, illustrated below (this example works fine):

use strict; use warnings; sub test1 { my $var1 = 2; my $b; if ($var1 == 1) { $b = "One"; } elsif ($var1 == 2) { $b = "Two"; } elsif ($var1 == 3) { $b = "Three"; } print "$var1 is $b"; } test1();

Any advice you can give would be deeply appreciated. Thanks.

Comment on Given When Syntax
Select or Download Code
Re: Given When Syntax
by hazylife (Monk) on Mar 15, 2014 at 15:53 UTC
    use feature 'switch';

    :-)

      Hi HazyLife. I did consider using the Switch module, but I had read that it is being deprecated and that I should be using given/when, and that is is more efficient. No other reason I can't or won't - I was just curious why I'm getting the syntax errors for given/when. Also, this is a part of a much larger script that has a long processing time and I'm trying to be as efficient as possible. Thanks for your input!

        re hazylife's observation, you need to understand that...
              use feature "switch";" is NOT EQUAL to use Switch;.


        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.
Re: Given When Syntax
by LanX (Canon) on Mar 15, 2014 at 15:54 UTC
    I'm mobile and can't spot or test your problem.

    Normally I recommend replacing given/when with for/if-constructs.

    But in this particular case I don't understand why you are not simply using an array or a hash, holding the number's names.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

    edit

    Ehm... did you activate switch?

     use feature "switch"; or use 5.010;

    update

    DB<103> @num=qw/Zero One Two Three Four/ => ("Zero", "One", "Two", "Three", "Four") DB<104> $var=2; print "$var is $num[$var]"; => 1 2 is Two

      Hi Rolf. As I mentioned to HazyLife, I could have used Switch and I also thought of using an array, as you have demonstrated above, but in the actual program I'm running I'm already in the middle of hash. I will try if I can do this there, however. I'm trying to do this the most efficient way and I don't have a whole lot of PERL experience. Thanks for replying!!

        I think you are getting it wrong, Given/When are activated by the feature pragma with the keyword switch or with an indication of the first Perl version which supports it.(not a Switch module)

        The deprecation also affects Given/When, because of the built-in use of smartmatch. We had a discussion recently, should be easily found.

        In the post above I updated a link to a thread showing how to easily emulate Given/When with For/If!

        HTH! :)

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: Given When Syntax
by Anonymous Monk on Mar 15, 2014 at 15:56 UTC
Re: Given When Syntax
by ww (Bishop) on Mar 15, 2014 at 16:12 UTC
    Read the friendly manual more thoroughly:

    perlsyn: "The experimental "given" statement is *not automatically enabled*; see "Switch Statements" below for how to do so, and the attendant caveats."

    Rough digest of the referenced matter: From 5.10.1 on one can say use feature "switch"; and from 5.14 onward, you can enable switch (given/when) by specifying the Perl version.

    When done per the doc, execution (under 5.16/32bit Win7) is as follows:

    C:\>perl 1078449.pl 2 is Two C:\>

    using your code with addition of line 3, specifying a version = or > 5.14:

    use strict; use warnings; use 5.016; sub test2 { my $var = 2; my $i; given ($var){ when(1) { $i = "One"; } when(2) { $i = "Two"; } when(3) { $i = "Three"; } default { $i = "Other"; } } print "$var is $i"; } test2();

    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.

      Hi WW. I did read the manual but did not read it thoroughly enough - you are correct (serves me right - I'm constantly admonishing others for not reading my own documentation carefully enough). When I declare my PERL version, the script does execute, although with "experimental" warnings. This is a good enough reason to avoid it.

      That being said, do you have any advice on the best approach from a performance standpoint? Thanks for your input - it's greatly appreciated!"

Re: Given When Syntax
by Laurent_R (Parson) on Mar 15, 2014 at 16:39 UTC

    The switch (given/when) feature is an experimental feature that should probably be avoided.

    From the Perl documentation ( http://perldoc.perl.org/perlsyn.html#Experimental-Details-on-given-and-when) : As previously mentioned, the "switch" feature is considered highly experimental; it is subject to change with little notice. In particular, when has tricky behaviours that are expected to change to become less tricky in the future. Do not rely upon its current (mis)implementation. Before Perl 5.18, given also had tricky behaviours that you should still beware of if your code must run on older versions of Perl.

    It is probably not wise to use this feature for anything else than a one-off program that you know will never have to be used again in the future, as your syntax might break in the next Perl version.

    Using an array is probably the simplest alternative for your very simple case:

    my @array = qw /One Two Three/; my $var = 2; print $array[$var], "\n";
    If your actual case is more complicated, then a hash might be the solution:
    my %hash = ( 1 => One, 2 => Two, 3 => Three); my $var = 2; print "$hash{$var}\n";
    For more complicated things, such as taking actions on the basis of the value of a variable, you might want to use a dispatch table (essentially a hash in which the values are references to subs). For example, the following is using a long list of if /elsif expressions to determine what series of actions to do depending on some parameters received as arguments:
    my ($oper, $rest) = shift, join ' ', @_; my $stop = 'stop'; my $force_stop = 'force_stop'; # ... if ($oper eq $stop) { run_stop ($rest); cleanup(); } elsif ($oper eq $force_stop) { run_force_stop ($rest); cleanup(); } elsif ($oper eq 'start') { run_start ($rest); } elsif ($oper eq 'force_start') { run_stop ($rest); cleanup(); run_start ($rest); } ... else { die "Unknown operation: $oper \n"; }
    You could replace this with the following dispatch table:
    my %dispatch_table = ( stop => sub {run_stop($rest); cleanup();}, force_stop => sub {run_force_stop($rest); cleanup();}, start => /&run_start(@rest), force_start => sub {run_stop ($rest); cleanup() ; run_start ($res +t);}, );
    and use it as follows:
    my ($oper, $rest) = shift, join ' ', @_; if (defined $dispatch_table{$oper}) { $dispatch_table{$oper}->($rest); } else { die "Unknown operation: $oper \n"; }
    If even that is not OK, then you can also go for either the for/if constructs referred to in the link provided by LanX above (http://www.perlmonks.org/?node_id=826710) or, if worse come to worse, to the series of if/elsif.

      Hi Laurent. These are all excellent examples - I think in my case I can get away with the array construct you demonstrated, but I will experiment to see if the hash is more appropriate. I also appreciate your advice on dealing with new features like this one - very good to know. Thanks so much for taking the time - this is great stuff! Cheers, DP.

Re: Given When Syntax
by dasgar (Deacon) on Mar 15, 2014 at 16:50 UTC

    You could try one of the modules that provides a switch functionality. Recently I came across Switch::Plain. I personally found it very simple and easy to use.

Re: Given When Syntax
by davido (Archbishop) on Mar 15, 2014 at 18:11 UTC

    I have run across a situation where I need a Switch statement.

    That need is of your own creation. There is no situation where the lack of a switch statement will prohibit the project from moving forward with efficiency and clarity. Perl5 was a dozen years old before it got a switch statement (called given/when), and the addition of that syntax didn't make any application of the Perl language that previously would have been impossible suddenly become possible. In fact, Perl's switch statement has been mostly repealed (or flagged as experimental) in modern releases of Perl.

    given/when is a programmer convenience, not a language necessity. Use if/elsif/else, or a hash table filled with code refs, or if you're concerned about efficiency, use an array-table filled with coderefs, and assign constants that refer to the elements. Even if you're trying to make your decision based on pattern matching, that's not difficult:

    use constant { RE_TRIGGER => 0, CODEREF => 1, }; my @dispatch = ( [ qr/this/, \&do_that ], [ qr/those, \&do_these ], ); DISPATCH: for ( @dispatch ) { $string =~ $_->[RE_TRIGGER] && do{ $_->[CODEREF]->(); last DISPATCH; }; }

    In this example I'm populating an array with a series of patterns and actions to take if a given pattern matches. I have control over which match is tested first, because I'm using an array for the dispatch table. And I have control over whether to continue on to the next test, or abort upon success by the use (or omission) of last DISPATCH. I can even add a default by including a regex test as the last element in @dispatch that will always succeed.

    Is it more clear than a series of if/elsif/else statements? I guess that depends on how many tests are being conducted. If it's just a small few, chained if/elsif's are probably clearer. If there are a lot of options, a table works well.

    If you make the first element a subref instead of a regexp object, you have even more control, and a more generalized solution.


    Dave

      Hi Dave. Thank you very much for the examples and detailed explanations you provided. This was exactly what I was looking for, and although I didn't ask directly, I thought there must be a good reason why PERL doesn't have a native switch and your explanation makes sense in this regard. I greatly appreciate your input! DP

      While hashes in other languages are more expensive computationally, a hash lookup in Perl costs the same as an array lookup. Efficiency advantages of arrays manifest when using shift/unshift/push/pop. This is explicitly stated in 'Learning Perl.'

        use Benchmark 'cmpthese'; use List::Util 'sum'; our %hash = ( one => 1, two => 2, three => 3, four => 4, five => 5, six => 6, seven => 7, eight => 8, nine => 9, ten => 10, ); our @array = ( 1 .. 10 ); use constant { ONE => 0, TWO => 1, THREE => 2, FOUR => 3, FIVE => 4, SIX => 5, SEVEN => 6, EIGHT => 7, NINE => 8, TEN => 9, }; cmpthese ( -5, { hash => q/ my $n = sum( @hash{ 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten' } ); /, array => q/ my $n = sum( @array[ ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN ] ); /, } );

        ...produces...

        Rate hash array hash 1673833/s -- -46% array 3103809/s 85% --

        I've got the 2nd and 6th edition of Learning Perl, and haven't been able to find anywhere in either edition where the book states that the efficiency advantages of arrays versus hashes manifest themselves when using shift/unshift/push//pop. Nor have I found anywhere in the book that states that a hash lookup in Perl costs the same as an array lookup. If you know of somewhere in that book I should be reading, please provide the exact quote, along with which edition I might find it in.

        I think you're probably referring to this paragraph:

        Some implementations of hashes (such as in the original awk language, where Larry borrowed the idea from) slow down as the hashes get larger and larger. This is not the case in Perl—it has a good, efficient, scalable algorithm. So, if a hash has only three key-value pairs, it’s very quick to “reach into the barrel” and pull out any one of those. If the hash has three million key-value pairs, it should be just about as quick to pull out any one of those. A big hash is nothing to fear.

        That paragraph makes an assertion that the computational complexity of hash inserts or lookups is approximately constant, as the size of the hash grows toward infinity. And the same could be said of arrays. But order of growth in computational complexity says nothing about the amount of computational complexity there is that stays constant as the data set grows.

        To understand this concept, look at the following two simple scenarios:

        • Find the numeric value 100 in an unsorted array of unique integers: We know that as the array grows, the growth in computational complexity is linear.
        • Find the string "David" in an unsorted array of strings.: We know that as the array grows, the growth in computational complexity is linear.

        So those are both O(n) searches. However, to find numeric 100, we do a series of numeric comparisons while iterating over the elements of the array. To find "David", we must go to the first element and see if the first character in the first element is a "D". If it is, then we compare the second character and see if it is an "a". Each letter of the string results in another char comparison... essentially an integer comparison for each character of the string (short-circuiting as soon as there's a mismatch)... repeated for each element in the data set.

        So it's pretty easy to see in this example that the string comparisons have an overhead per element that doesn't exist for the numeric comparisons.

        Now look at what goes on in hash lookups versus array lookups. To look up a single element in an array, internally, Perl must first start with the base address of the array. Then it must add an integral offset to the array's base address. This offset will be the element number times the size of the element's structure. This is a simple mathematical calculation; base + offset * size. Once that calculation is performed, Perl can dereference a pointer contained in the element, and fetch the element's scalar entity.

        Now for a Perl hash. First Perl must examine the string of some arbitrary length, and using a hashing algorithm to compute the bucket. That bucket may contain more than one element in the form of a linked list. The linked list would then be walked to arrive at the specific element being searched for. Fortunately the linked list chains are always kept pretty short, so we don't worry about their growth. But now instead of a simple base+offset*size equation, we feed a (usually) multi-byte string to a hashing algorithm, that gives us an offset, that leads us to a short chain of elements to scan through.

        So the array lookup has some constant overhead, and the hash lookup has some constant overhead (overhead that doesn't change as the size of the structure grows). But the amount of constant overhead for the array is smaller than the amount of constant overhead for the hash.

        We mostly don't worry about that. If we were looking for bleeding edge performance we would be using C or C++ to begin with. But the point to my mention of efficiency in this case was this: Sometimes function tables are used inside of tight loops. And infrequently, those tight loops become a performance problem. In such cases, switching from a hash based dispatch table to an array one will bring some performance improvement.

        The more important point is that if one needs to assure true "switch-like" behavior, where the order of comparisons is predictable, an array is more appropriate; a hash based dispatch table doesn't guarantee a predictable order in which cases will be tested for a match.


        Dave

Re: Given When Syntax
by LanX (Canon) on Mar 15, 2014 at 18:27 UTC
    > If I can't get this to work, I'll be stuck using an if/elsif construct

    a little syntactic sugar with an improvised in operator and given/when has no better readability anymore.¹

    use strict; use warnings; sub in { for my $val (@_) { return 1 if $_ eq $val; } return; } sub test1 { my ($var) = @_; my $i; for ($var) { if ( in 1 ) { $i = "One" } elsif ( in 2 ) { $i = "Two" } elsif ( in 3,4,5 ) { $i = "Three, Four or Five" } else { $i = "Other" } } print "$var is $i\n"; } sub test2 { my ($var) = @_; my $i; for ($var) { if ( in 1 ) { $i = "One" ;next} if ( in 2 ) { $i = "Two" ;next} if ( in 3,4,5 ) { $i = "Three, Four or Five" ;next} $i = "Other" } print "$var is $i\n"; } test1($_) for 1..6; test2($_) for 1..6;

    ¹) though I think it's still faster, it might be able to automatically optimize to hash-lookups

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Given When Syntax
by Lennotoecom (Pilgrim) on Mar 15, 2014 at 19:20 UTC
    you can use the dark arts mate ^_^
    no stricts, no warnings, no use 5** ^_^,
    come to the dark side of the force mate
    $switch = chr(int(rand(3)+97)); $switch->(); sub a {print "one\n";} sub b {print "two\n";} sub c {print "three\n";}
    p.s. guys dont scold me hard please :)
      > use the dark arts mate

      maybe a misunderstanding, but I thought black is supposed to be beautiful not ugly! :-b

      sub switch ($$); for my $var (1..4) { my $i; switch $var => { 1 => sub { $i = "One"}, 2 => sub { $i = "Two"}, 3 => sub { $i = "Three"}, '' => sub { $i = "Other"}, }; print "$var is $i\n"; } sub switch ($$) { my ($case, $href) =@_; if ( exists $href->{$case} ) { $href->{$case}->(); } else { $href->{''}->(); } }

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        symbolic references = dark arts no?

      Dark Arts... I like the sound of that. It's so eeeevil. Seriously, though - I appreciate the different perspective and your input. Thanks!

Re: Given When Syntax
by kcott (Abbot) on Mar 16, 2014 at 00:19 UTC

    G'day Deep_Plaid,

    I see you've already been advised about either: specifying a version (with use) which automatically loads the feature you want; or, specifying the feature by itself. For "switch", that's one of:

    use 5.010;

    or

    use feature 'switch';

    However, as you're using 5.18.1, you'll now get warnings about experimental features unless you also switch them off like this (as explained in "perl5180delta: New mechanism for experimental features"):

    no if $] >= 5.018, warnings => "experimental::feature_name";

    Furthermore, that's somewhat counter-intuitive because, if you used what you might think would be the correct way to to do this, i.e.

    no if $] >= 5.018, warnings => "experimental::switch";

    you'll get

    Unknown warnings category 'experimental::switch' at ... BEGIN failed--compilation aborted at ...

    What you actually need is:

    no if $] >= 5.018, warnings => "experimental::smartmatch";

    Without this, you'll get a warning message for every given and when in your code, i.e. for the code you posted:

    given is experimental at ... when is experimental at ... when is experimental at ... when is experimental at ...

    At this point, you may have decided not to use experimental features. They're certainly not a good idea in production code. Here's some additional information on both Switch and switch: "Re: switch statement".

    Finally, here's a version of your code which generates no errors or warnings (I used 5.18.1 — the same version as you're running). In addition, while I see you've been provided with many alternatives to given/when, here's two more. [Note: this is just example code that includes no argument checking.]

    #!/usr/bin/env perl -l use 5.010; use strict; use warnings; no if $] >= 5.018, warnings => "experimental::smartmatch"; print '*** test1() ***'; print "$_ is ", test1($_) for 0 .. 4; print '*** test2() ***'; print "$_ is ", test2($_) for 0 .. 4; print '*** test3() ***'; print "$_ is ", test3($_) for 0 .. 4; sub test1 { my ($var) = @_; my $i; given ($var) { when (1) { $i = 'One' } when (2) { $i = 'Two' } when (3) { $i = 'Three' } default { $i = 'Other' } } return $i; } sub test2 { my ($var) = @_; return $var == 1 ? 'One' : $var == 2 ? 'Two' : $var == 3 ? 'Three' : 'Other'; } sub test3 { state $word_for = {qw{1 One 2 Two 3 Three}}; return $word_for->{+shift} || 'Other'; }

    Output:

    *** test1() *** 0 is Other 1 is One 2 is Two 3 is Three 4 is Other *** test2() *** 0 is Other 1 is One 2 is Two 3 is Three 4 is Other *** test3() *** 0 is Other 1 is One 2 is Two 3 is Three 4 is Other

    -- Ken

      Hi Ken. Thanks for taking the time for this detailed post . I was so caught up in things yesterday that I missed it and didn't respond sooner. It was very helpful and I learned a great deal - the more examples the better. Cheers, DP.

Re: Given When Syntax
by Marshall (Prior) on Mar 16, 2014 at 05:52 UTC
    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; }

      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.

        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.
        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 ("3.abc"), "\n"; __END__ prints: One UNKOWN Three
        Update:
        Well I think we both know that this is a simple example.
        The regex'es aren't complicated. Just to demo the idea.
        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:
        test2(1.00.000);
        should be:
        test2("1.00.000");
        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 test_if.pl 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.
Re: Given When Syntax
by LanX (Canon) on Mar 16, 2014 at 20:44 UTC
    this thread is bloated b/c you didn't make clear what your real problem is and where your priorities are.

    if its only about translating a number, than using a hash is by far the best solution

    use strict; use warnings; my %trans = (1 => "One", 2 => "Two", 3 => "Three"); my $var = '2.123.45.6'; if ( $var =~ /^(\d)\./ ) { print "$var starts with $trans{$1}"; } else { print "$var malformed"; }

    Now since this became a general discussion of "switch", for completeness TIMTOWTDI:

    use strict; use warnings; my $i; for my $case (1..4) { eval { goto "_$case" } or $i = "Other"; next; _1: $i = "One" ; next; _2: $i = "Two" ; next; _3: $i = "Three"; next; } continue { print "$case is $i\n"; }

    Hashes with dispatch tables (slow sub calls) and if-elsif-chains (slow linear testing) shouldn't be as fast as this approach.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      Hashes with dispatch tables (slow sub calls) and if-elsif-chains (slow linear testing) shouldn't be as fast as this approach.

      Dear Rolf, I know that you are interested in functional programming possibilities in Perl, so please stop taking for granted what HOP opponents are hammering constantly. Subroutine calls of course add some time penalty, but not as much as many people think or say. In fact, as soon as the routine is really doing something, the sub call penalty becomes almost anecdotal or even almost negligible compared to the rest or the processing. In the benchmark below, the dispatch table ranks second best, immediately after direct array search, although it does really nothing more than returning a value.
      use strict; use warnings; use Benchmark qw/cmpthese/; my @translation = qw / Zero One Two Three/; my %trans = (1 => "One", 2 => "Two", 3 => "Three"); my @dispatch = ( sub {return "Zero"}, sub {return "One"}, sub {return +"Two"}, sub {return "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)]; } sub test4 { my $var = shift; return $trans{$1} if $var =~ /^(\d)\./ ; } sub test5 { my $var = substr shift, 0, 1; eval { goto "_$var" } or return "Other"; _1: return "One" ; _2: return "Two" ; _3: return "Three"; } sub test6 { return $dispatch[(substr shift, 0, 1)]->(); } cmpthese( -1, { _linear_1 => q {test1("2.01.000")}, _linear_2 => q {test2("2.01.000")}, _array => q {test3("2.01.000")}, _hash_regex => q {test4("2.01.000")}, _goto => q {test5("2.01.000")}, _dispatch => q {test6("2.01.000")}, } )
      And the results:
      $ perl test_if.pl Rate _hash_regex _goto linear_1 linear_2 _dispatc +h _array _hash_regex 831494/s -- -29% -46% -52% -54 +% -70% _goto 1173439/s 41% -- -24% -32% -36 +% -57% _linear_1 1538868/s 85% 31% -- -10% -1 +6% -44% _linear_2 1714704/s 106% 46% 11% -- - +6% -37% _dispatch 1827212/s 120% 56% 19% 7% - +- -33% _array 2735932/s 229% 133% 78% 60% 50 +% --
      The dispatch table approach, with its sub call penalty, is 33% slower than the direct array access, but still quicker than any of the other tested approaches.
        Dear Laurent,

        Thanks for providing timings, I appreciate this!

        I said "should" because I was too busy to measure it myself.

        But maybe someone has the time to do these benchmarks properly? :)

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: Given When Syntax
by Bloodnok (Vicar) on Mar 17, 2014 at 14:19 UTC
    I have to admit to using the following structure when in need of a switch compatible statement in perl 5.14 and earlier i.e. when feature, as mentioned elsewhere in this discussion, is not available...
    SWITCH: { <cond 1> && do { . . last SWITCH; }; <cond 2> && do { . . last SWITCH; }; # default . . }
    ...and I got this from this very portal :-)

    A user level that continues to overstate my experience :-))
Re: Given When Syntax
by Laurent_R (Parson) on Mar 17, 2014 at 23:24 UTC

    A new benchmark trying to measure only the effect of the data structure being used. No need for further comments except that it is an entirely different benchmark:

    use strict; use warnings; use Benchmark qw/cmpthese/; my @translation = qw / Zero One Two Three Four Five Six/; my %trans = (1 => "One", 2 => "Two", 3 => "Three", 4 => "Four", 5 => " +Five", 6 => "Six"); my @dispatch = ( sub {return "Zero"}, sub {return "One"}, sub {return +"Two"}, sub {return "Three"}, sub {return "Four"}, sub {return "Five" +}, sub {return "Six"} ); sub test2 { my $var2 = shift; return ("One") if ($var2 == 1 ); return ("Two") if ($var2 == 2 ); return ("Three") if ($var2 == 3 ); return ("Four ") if ($var2 == 4 ); return ("Five") if ($var2 == 5 ); return undef; } sub test3 { my $var2 = shift; return $translation[$var2]; } sub test4 { my $var = shift; return $trans{$var} ; } sub test5 { my $var = shift; eval { goto "_$var" } or return "Other"; _1: return "One" ; _2: return "Two" ; _3: return "Three"; _4: return "Four"; _5: return "Five"; } sub test6 { my $var = shift; return $dispatch[($var)]->(); } cmpthese( -1, { _linear_2 => q {test2("5")}, _array => q {test3("5")}, _hash => q {test4("5")}, _goto => q {test5("5")}, _dispatch => q {test6("5")}, } )
    And the results:
    $ perl test_if.pl Rate _goto _linear_2 _dispatch _hash _array _goto 1247485/s -- -17% -26% -50% -52% _linear_2 1509714/s 21% -- -11% -40% -42% _dispatch 1688450/s 35% 12% -- -33% -36% _hash 2513434/s 101% 66% 49% -- -4% _array 2621427/s 110% 74% 55% 4% --
    Have a nice evening.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1078449]
Approved by ww
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (6)
As of 2014-12-22 10:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (116 votes), past polls