in reply to Switch statement?

I'd like to expand a bit on Corion's fine response. In general, you should take it as a big red flag whenever you have many (and in your case, MANY) separate variables, which are all somehow related. Perhaps they share a common prefix, or are an incrementing pattern (such as your code). This red flag should make you consider using a datastructure that actually relates the values together by more than just a name.

Another big red flag is repetition. Having 36 lines of code that are only different by a few characters should make you stop and think. Copying and pasting things many times is not something you should be doing very often. Subroutines often help avoid this (all-too-common) syndrome.

Both of these things are present in your program, and in very large doses. Corion gives a nice example of how to clean this specific code up, but the ideas are more general. Use real data structures, use loops, and use subroutines. Copying and pasting is almost always wrong. :-)

Update: cleaned up some wording.

Replies are listed 'Best First'.
Re^2: Switch statement?
by Anonymous Monk on Jul 19, 2004 at 13:55 UTC
    Thank you... I suppose I should be ashamed that I wrote a 300 line script that didn't work while corian made one that worked in 20 lines... I was trying indeed to find a way around all that repitition. I know c fairly well, and beleive me, I don't write c like that. But I couldn't figure out how to save all of those in one variable. I was thinking about an array, but I'm just not yet comfortable enough with perl to scripts a function to do that, etc. Thanks again. -Jack C jack@crepinc.com
      Well, I've used the code corian posted. And, o course, it works perfectly. Except, I edited it in places, and now wierd stuff happens. :(. Basically, when I use the -c or -r option, it works great. But when I use the -a option, it tells me I've left off the option, but doesn't give me the error until AFTER it prints SOME of the data. Strange. The other thing is, why does the order change everytime the script is run?
      #!/usr/bin/perl -w use strict; my $infile = $ARGV[0]; my $act = $ARGV[1]; my $included = "n"; my $lvar =""; my @validchars = ("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"); if ($#ARGV < 1) { print "Usage: $0 <file> <chars> Where <chars> can be -a or -c: -a: show all charactors. (ie, spaces, etc.) -c: show only regular charactors, but keep case. (ie, x is diff +erent than X.) -r: show only regular charactors, disregard case. (ie, t and T +are the same.)\n"; exit; } my $count = 0; system("cls"); #win system("clear"); #*nix open INPUT, "<$infile" or die "Couldn't open '$infile' : $!"; my $input = do { local $/; <INPUT> }; my %histogram; for my $char (split //, $input) { $included = "n"; if ($act eq "-a") { $histogram{$char}++; $count++; } elsif ($act eq "-c") { foreach $lvar (@validchars){ if ($lvar eq uc($char)) {$included = "y";} } if ($included eq "y") { $histogram{$char}++; $count++; } } elsif ($act eq "-r") { foreach $lvar (@validchars){ if ($lvar eq uc($char)) {$included = "y";} } if ($included eq "y") { $histogram{uc($char)}++; $count++; } } else { print "Bad charactor option: '$act'.\n"; exit; } }; print "Of $count charactors:\n\n"; for (keys %histogram) { if ($_ eq "\n") { print "'\\n' occurred $histogram{$_} times. (".(($ +histogram{$_}/$count)*100)."%)\n"} else { print "'$_' occurred $histogram{$_} times. (".(($histogram{$ +_}/$count)*100)."%)\n" } };
      Thanks again, -Jack C jack@crepinc.com

        The reason that your order of keys is different every time is that a hash has no predefined order of keys (due to the mechanics of how hashes work, and due to the fact that Perl randomizes that order some more). So you will maybe want to sort the keys before printing them:

        for (sort keys %histogram) { ... };

        I find a style issue with your @validchars usage - normally, if you want to quickly check whether something is contained in a list in Perl, you use a hash:

        my %validchars = map { $_ => uc $_ } (qw( A B C D E F ... Z a b c d .. +. z )); # this sets # $validchars{A} = A # ... # $validchars{Z} = Z # $validchars{a} = A # ... # and the later: if (exists $validchars{$_}) { my $target = $validchars{$_}; $histogram{$target}++ };

        For your different switches, you should simply initialize %validchars to different values instead of rechecking your switch values every time. This will clean up your inner loop as the program behaviour is centralized.

        my %validchars; if ( $arg eq '-a' ) { %validchars = map { $_ => uc $_ } ( \x00 .. \xff ); } elsif ( $arg eq '-c' ) { %validchars = map { $_ => $_ } ( 'A' .. 'Z', 'a'..'z' ); };

        Whenever you are repeating code, think about how you can avoid that repetition.

      There is no shame in learning something. The shame is if you don't learn it.