in reply to Re^2: Control Structures
in thread Control Structures

You certainly don't need goto to do switch/case. One auxiliary function will give you the functionality you outlined above, and more. (I've included the ability to check regexen; you could do other things if you were inclined.) The syntax reads pretty well, IMO.
sub case_new { my $matched = 0; return sub { my $check = shift; return $matched if $matched; if (ref $check eq 'Regexp') { return $matched = /$check/; } else { return $matched = $_ eq $check; } } } sub clean_up_values {print "Clean up values\n"} sub do_whatever_you_need_to {print "Whatever you need\n"} sub do_something_else {print "Something else\n"} sub do_some_default_thing {print "Default\n"} for my $value ('testing', 'needs_slight_cleaning', 'good value', 'tota +lly unrelated') { print "Checking $value..."; my $case = case_new; for ($value) { $case->('needs_slight_cleaning') && &clean_up_values; $case->('good value') && do { &do_whatever_you_need_to; last; }; $case->('totally unrelated') && do { &do_something_else; last; }; default: &do_some_default_thing; } }
Writing these things has become something of a hobby for me. :-)

Caution: Contents may have been coded under pressure.

Replies are listed 'Best First'.
Re^4: Control Structures
by jhourcle (Prior) on May 10, 2005 at 13:48 UTC

    Um...the goto bit was a joke. (although, it does come in handy for these sort of logic issues)

    But your example structure still doesn't behave like switch/case in C (okay, I did more LPC than I've ever done C, and that was all a long time ago). The important thing is that matching 'needs_slight_cleaning' does not mean that it matches 'good_value', or that it will match after &clean_up_values is called.

    Because I've been doing without switch for so long, it's difficult to think of examples when these structures are really useful. Once you see the logic, it makes perfect sense, though.

    Okay, here's a logic situation -- you have a program that's monitoring servers. You based on a given alert, you need to decide how to escallate.

    switch ($machine_name) { # master LDAP server: case 'einstein': &alert_management(); # LDAP replicas case 'joule': case 'boltzman': case 'hawking': &alert_ldap_sysadmin(); # the mail systems. (that authenticate off of LDAP) case 'feynman': case 'faraday'; case 'curie': case 'newton': &alert_mail_sysadmin(); # helpdesk gets notified of everything not development default: &alert_helpdesk(); # development servers: case 'teller': case 'penn': case 'houdini': case 'copperfield': &log(); }

    Blah...submitted too early...anyway, the point is, that in that example, if 'einstein' is true, we alert management, ldap sysadmin, mail sysadmin, helpdesk, and it gets logged. (okay, I could've moved '&log' outside of the switch statement). But flow continues until there's a break...and in this case, there's no break. It gets even tricker when you have multiple paths like that through the system. (this was just ldap and mail servers... imagine if I add in web servers, and some of those webservers are also LDAP authenticating... okay, well, at that point, switch might not be so good for that one.... but we could alert the web admins as part of the ldap replica section, and then have a seperate section for the web servers.

    I can do this in perl. (either using goto, or by using much more complicated lookup tables to tell how things propogate ... which may be needed, as the rules for notification can't be flattened to one dimension)

      your example structure still doesn't behave like switch/case in C
      Yes, it does implement fall-through. Try it. The example program I gave demonstrated it. See the two lines of output for needs_slight_cleaning? It doesn't have to match 'good value' (how could it?) to get that second output line.
      Checking testing...Default Checking needs_slight_cleaning...Clean up values Whatever you need Checking good value...Whatever you need Checking totally unrelated...Something else
      If I didn't want to implement fall-through, I would have just had a bunch of ifs. If it doesn't encounter last, it's going to keep calling $case->(), which will short-circuit true if it has previously matched (that is the purpose of the $matched variable in the closure). last acts like break.

      Update: To stack up things like you did here, just have a series of calls to $case-() as separate statements (or you could join them up with ors), and follow the last one with && whatever.


      Caution: Contents may have been coded under pressure.

        Well, that's what I get for posting before the Mt. Dew has kicked in -- I completely missed what the closure was doing. (and of course, I didn't actually run it).

        But well, that being said, there are cases where switch is useful. Yes, it can be done through work arounds, but if you're going to do this sort of thing often enough, it's annoying to have to do extra work when other languages have it built in, and you know that the one part you're dealing may be easier to implement in some other language.

        I know there are modules, and plenty of different ways to handle switch-like syntax in Perl, possibly because your dispatch tables would be tuned differently depending on your exact needs. Sometimes, I just want something to work, no matter how inefficiently, for those times when my time is more valuable than the execution time of the program --

        I don't want to research which module best fits my needs, and doesn't have known issues, then go and download the module through CPAN, only to find that it has some other dependancy (that I'm going to need to install on whatever other machines I may try to run this script on, etc.). Modules may be great, but there's always the assumption that something has been more thoroughly tested if it's packaged with the main distribution, or if it's a builtin function, and it's going to be less of a headache to maintain in a multiple system environment.