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.
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Given When Syntax
by Laurent_R (Canon) 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: If your actual case is more complicated, then a hash might be the solution: 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: You could replace this with the following dispatch table: and use it as follows: 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. | [reply] [d/l] [select] |
by Deep_Plaid (Acolyte) on Mar 15, 2014 at 20:46 UTC | |
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. | [reply] |
Re: Given When Syntax
by ww (Archbishop) on Mar 15, 2014 at 16:12 UTC | |
When done per the doc, execution (under 5.16/32bit Win7) is as follows:
using your code with addition of line 3, specifying a version = or > 5.14:
Questions containing the words "doesn't work" (or their moral equivalent) will usually get a downvote from me unless accompanied by:
| [reply] [d/l] [select] |
by Deep_Plaid (Acolyte) on Mar 15, 2014 at 16:33 UTC | |
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!" | [reply] |
Re: Given When Syntax
by davido (Cardinal) on Mar 15, 2014 at 18:11 UTC | |
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:
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 | [reply] [d/l] [select] |
by Deep_Plaid (Acolyte) on Mar 15, 2014 at 20:51 UTC | |
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 | [reply] |
by Anonymous Monk on Mar 17, 2014 at 02:02 UTC | |
| [reply] |
by davido (Cardinal) on Mar 17, 2014 at 05:19 UTC | |
...produces...
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:
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:
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 | [reply] [d/l] [select] |
by Anonymous Monk on Mar 17, 2014 at 23:38 UTC | |
Re: Given When Syntax
by hazylife (Monk) on Mar 15, 2014 at 15:53 UTC | |
:-) | [reply] [d/l] |
by Deep_Plaid (Acolyte) on Mar 15, 2014 at 16:12 UTC | |
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! | [reply] |
by ww (Archbishop) on Mar 15, 2014 at 16:29 UTC | |
re hazylife's observation, you need to understand that... Questions containing the words "doesn't work" (or their moral equivalent) will usually get a downvote from me unless accompanied by: | [reply] [d/l] [select] |
by ikegami (Patriarch) on Mar 18, 2014 at 13:28 UTC | |
Re: Given When Syntax
by LanX (Saint) on Mar 15, 2014 at 15:54 UTC | |
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)
editEhm... did you activate switch? use feature "switch"; or use 5.010;
update
| [reply] [d/l] [select] |
by Deep_Plaid (Acolyte) on Mar 15, 2014 at 16:20 UTC | |
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!! | [reply] |
by LanX (Saint) on Mar 15, 2014 at 16:29 UTC | |
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) | [reply] [d/l] |
Re: Given When Syntax
by LanX (Saint) on Mar 15, 2014 at 18:27 UTC | |
a little syntactic sugar with an improvised in operator and given/when has no better readability anymore.¹
¹) 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) | [reply] [d/l] [select] |
Re: Given When Syntax
by kcott (Archbishop) 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:
or
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"):
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.
you'll get
What you actually need is:
Without this, you'll get a warning message for every given and when in your code, i.e. for the code you posted:
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.]
Output:
-- Ken | [reply] [d/l] [select] |
by Deep_Plaid (Acolyte) on Mar 16, 2014 at 16:23 UTC | |
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. | [reply] |
Re: Given When Syntax
by Marshall (Canon) on Mar 16, 2014 at 05:52 UTC | |
The "old" Perl ways are more than adequate to do this.
| [reply] [d/l] |
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:
Once again, many thanks. I appreciate your time. | [reply] [d/l] |
by Marshall (Canon) on Mar 16, 2014 at 15:03 UTC | |
Update: Well I think we both know that this is a simple example. The regex'es aren't complicated. Just to demo the idea. | [reply] [d/l] |
by Deep_Plaid (Acolyte) on Mar 16, 2014 at 16:19 UTC | |
by ww (Archbishop) on Mar 16, 2014 at 15:01 UTC | |
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: | [reply] [d/l] [select] |
by Deep_Plaid (Acolyte) on Mar 16, 2014 at 16:14 UTC | |
by Laurent_R (Canon) on Mar 16, 2014 at 18:03 UTC | |
should be: I would also submit that this: 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: 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: 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: 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: which gives the following results: 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. | [reply] [d/l] [select] |
by tobyink (Canon) on Mar 16, 2014 at 18:17 UTC | |
by Laurent_R (Canon) on Mar 16, 2014 at 20:11 UTC | |
| |
by Deep_Plaid (Acolyte) on Mar 16, 2014 at 18:20 UTC | |
Re: Given When Syntax
by dasgar (Priest) 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. | [reply] |
Re: Given When Syntax
by Lennotoecom (Pilgrim) on Mar 15, 2014 at 19:20 UTC | |
no stricts, no warnings, no use 5** ^_^, come to the dark side of the force mate p.s. guys dont scold me hard please :) | [reply] [d/l] |
by LanX (Saint) on Mar 15, 2014 at 19:56 UTC | |
maybe a misunderstanding, but I thought black is supposed to be beautiful not ugly! :-b
Cheers Rolf ( addicted to the Perl Programming Language) | [reply] [d/l] |
by Lennotoecom (Pilgrim) on Mar 15, 2014 at 20:05 UTC | |
| [reply] |
by LanX (Saint) on Mar 15, 2014 at 20:18 UTC | |
by Lennotoecom (Pilgrim) on Mar 15, 2014 at 20:24 UTC | |
by Deep_Plaid (Acolyte) on Mar 15, 2014 at 21:06 UTC | |
Dark Arts... I like the sound of that. It's so eeeevil. Seriously, though - I appreciate the different perspective and your input. Thanks! | [reply] |
Re: Given When Syntax
by LanX (Saint) on Mar 16, 2014 at 20:44 UTC | |
if its only about translating a number, than using a hash is by far the best solution
Now since this became a general discussion of "switch", for completeness TIMTOWTDI:
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) | [reply] [d/l] [select] |
by Laurent_R (Canon) on Mar 16, 2014 at 22:58 UTC | |
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.And the results: 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. | [reply] [d/l] [select] |
by LanX (Saint) on Mar 17, 2014 at 00:16 UTC | |
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) | [reply] |
by Laurent_R (Canon) on Mar 17, 2014 at 14:39 UTC | |
by LanX (Saint) on Mar 17, 2014 at 15:22 UTC | |
| |
Re: Given When Syntax
by Anonymous Monk on Mar 15, 2014 at 15:56 UTC | |
| [reply] [d/l] |
Re: Given When Syntax
by Bloodnok (Vicar) on Mar 17, 2014 at 14:19 UTC | |
...and I got this from this very portal :-)
A user level that continues to overstate my experience :-))
| [reply] [d/l] [select] |
Re: Given When Syntax
by Laurent_R (Canon) 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: And the results: Have a nice evening. | [reply] [d/l] [select] |