http://qs321.pair.com?node_id=485384

punch_card_don has asked for the wisdom of the Perl Monks concerning the following question:

Mobious Monks,

Simple style question.

I have a count variable that is to be set depending on a condition:

if ($some_variable eq 'x') { $count = 14; } else { $count = 9; } for $i (0 .. $count) { do some stuff }
Wherein '$some_variable' can have the values 'x' and 'y' only.
Coding the actual values right there in the conditonal effectively buries that way down in the bowels of a long program, but uses the minimum lines of code and is highly readbable once you find that snippet.

Setting two value holders at the top of the script make for easier maintenance when the values need to be changed some day:

#constant declaration area at top of script $value_1 = 14; $value_2 = 9; ...many lines of code later... if ($some_variable eq 'x') { $count = $value_1; } else { $count = $value_2; } for $i (0 .. $count) { do some stuff }
but it creates double referencing and extra lines of code.

A third way would be to declare a hash:

#constant declaration area at top of script $count{'x'} = 14; $count{'y'} = 9; ...many lines of code later... for $i (0 .. $count{$some_variable}) { do some stuff }
Any feelings on which is preferable from a coding style and/or efficiency point of view?

Forget that fear of gravity,
Get a little savagery in your life.

Replies are listed 'Best First'.
Re: Style: buried variables or double referencing?
by merlyn (Sage) on Aug 20, 2005 at 14:38 UTC
    Obviously, this is an abstract example, but if you choose meaningful variable names (and not just "count"), I don't see anything wrong no matter what way you do it. If you have to introduce a synthetic variable (something that is meaningful only to work around a programming issue, and doesn't map into the problem space), then be sure to add comments and keep its scope to an absolute minimum.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: Style: buried variables or double referencing?
by gryphon (Abbot) on Aug 20, 2005 at 15:41 UTC

    Greetings punch_card_don,

    Each of your suggested styles has merit depending on the particulars of the situation. Context is everything. There's nothing in your code examples that would make me shiver if I had to maintain it. (Although, "...many lines of code later..." does cause some pause, I have to admit.) A couple key things to consider:

    • Use very clear nouns for variable names that are short and accurately describe the data
    • Keep relevant code together; don't pre-define variables too early; in other words, don't my a scalar on line 4 then use it for the first time on line 100

    I tend to write really compact code, but I understand that it can be more difficult for someone else to maintain. Here's what I'd be tempted to do:

    for (0 .. (($some_variable eq 'x') ? 14 : 9 )) { print $_, "\n"; # or whatever goes on in here... }

    TMTOWTDI, and you've already posted a few good ways.

    gryphon
    Whitepages.com Development Manager (DSMS)
    code('Perl') || die;

      Although, "...many lines of code later..." does cause some pause, I have to admit.

      "many lines of code" = about 1,500

      and that's another style question. It's that big because it performs four phases of a multi-phase user inquiry to a database. That is, offer some options in a form, receive user selections, search database for more options that follow loically from the user's selections, and repeat total of four times. All phases use a large collection of identical constants, many identical sub-routines, but do a lot of crunching that's unique to that phase. So Having all four phases in one script with a phase-sorter at the beginning means:

      • reduced total code lines because common elements are not rerpoduced in separate scripts
      • during development, a change to any common element only has to be made once instad of four times.
      At the same time, I've sometimes wondered - is this considered a large script? Unweildy? Does it impact peformance to have to load one large script?

      Forget that fear of gravity,
      Get a little savagery in your life.

        "many lines of code" = about 1,500
        How do you create your html? If it is generated with print and heredocs within the script, you probably can reduce its size by using a templating mechanism and moving the html to separate files.

        Also, if you have such a big number of constants in your script, I would put them in some kind of configuration file, so you can change them without having to edit the script.
        • reduced total code lines because common elements are not rerpoduced in separate scripts
        • during development, a change to any common element only has to be made once instad of four times.
        There is another way to achieve this. Put your common code into modules.


        holli, /regexed monk/
        So Having all four phases in one script
        All in one script? How do you test that? I would have broken it into modules the moment I hit 500 lines. Especially since there is common code, and a large amount of it, that receives varying parameters: perfect thing to want to test that formally so that I can isolate a problem between called subroutines and calling code.
        reduced total code lines because common elements are not rerpoduced in separate scripts
        Uh, that's what a module is for. If you have cut-n-paste worry, then you aren't using modules right
        a change to any common element only has to be made once instad of four times
        Again, a sign that you don't understand modular development.

        I'm betting I would cringe looking at your "script". Mostly because I've seen far too many already in my life, and I think I've nearly used up my quota. {grin}

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

Re: Style: buried variables or double referencing?
by gam3 (Curate) on Aug 20, 2005 at 15:47 UTC
    This would be my solution.
    use constant LONG_COUNT => 14; use constant SHORT_COUNT => 9; my $some_variable; # ... my $loop_count = $some_variable eq 'x' ? LONG_COUNT : SHORT_COUNT; for $i (0 .. $loop_count) { # do some stuff print $i; }
    You might even get rid of loop_count and use (0 .. $some_variable eq 'x' ? LONG_COUNT : SHORT_COUNT) directly.

    Now LONG_COUNT and SHORT_COUNT will be easy to find and you can document them at the use constant.

    -- gam3
    A picture is worth a thousand words, but takes 200K.
      TheDamian's Best Practices gives several good reasons why constant should be deprecated in favour of Readonly. In which case you would have:
      use Readonly; Readonly my $LONG_COUNT => 14; Readonly my $SHORT_COUNT => 9; # ...
      I fully agree with his recommend since I never saw that constant buys me anything useful.

        Depends. Readonly is slow, and not everyone can use Readonly::XS. The following code takes 48 seconds:

        use Readonly; Readonly $C1 => 1; my $t0 = time(); my $a = 2; for (1 .. 10000000) { if ($a ++ == $C1) { ; } } print time() - $t0;

        When this takes 4 second:

        use constant C1 => 1; my $t0 = time(); my $a = 2; for (1 .. 10000000) { if ($a ++ == C1) { ; } } print time() - $t0;

        If performance is important, use constant.

        TheDamian's <cite>Best Practices</cite> gives several good reasons why constant should be deprecated in favour of Readonly:

        Readonly my $LONG_COUNT => 14; Readonly my $SHORT_COUNT => 9;

        But Damian also says in his book that you should use hashes for look-up tables, rather than if/else tests. And that's definitely a look-up table.

        So how about something like:

        Readonly \my %THWAPP_COUNT => ( x => 14, y => 9, );

        (Adjective describing what sort of a count this is courtesy of Acme::MetaSyntactic::batman.)

        Smylers

Re: Style: buried variables or double referencing?
by wfsp (Abbot) on Aug 20, 2005 at 16:15 UTC
    Personally I like your last option. Keep all your config/constants/options where you can see 'em!

    You may need others, they may grow 'til you need to put them in a separate file. If they're all together in one place this will be easier.

    I would also check the variable.

    "'$some_variable' can have the values 'x' and 'y' only."
    Are you sure! Check it anyway. :-)
    #!/usr/bin/perl use strict; use warnings; my $config = { count => { x => 9, y => 14, }, other => { a => 1, b => 2, } }; my $some_variable = 'x'; die "bad count" unless exists $config->{count}{$some_variable}; for my $i (0 .. $config->{count}{$some_variable}) { #do some stuff; }
Re: Style: buried variables or double referencing?
by davidrw (Prior) on Aug 20, 2005 at 16:16 UTC
    good comments above -- i too immediately thought of the ternary operator, but i like your hash solution, too. Note there is a significant difference between the hash and the conditional (if/else or ternary), and that is if $some_variable for some reason is not 'x' or 'y'. With the hash, you'll get undef, and with the conditional you will by defauly get the value corresponding to 'y'. This may or not may be a big deal--depends on the context.

    also note that you can combine the hash and 'use constant' solutions and do:
    use constant COUNTS => { x => 14, y =>9, }; ... foreach my $i ( 0 .. ${&COUNTS}{$some_variable} ){ ... }
Re: Style: buried variables or double referencing?
by pg (Canon) on Aug 20, 2005 at 15:49 UTC

    As long as the language supports constant, my policy is not to use constant directly later in the body of the program, and I always pre-define it in the "header":

    use constant C1 => 14; use constant C2 => 9; my $a = 'x'; if ($a eq 'x') { $count = C1; } else { $count = C2; } print $count;
Re: Style: buried variables or double referencing?
by Roger (Parson) on Aug 20, 2005 at 22:58 UTC
    Good programming style: High Cohesion, low coupling. That doesn't only apply across modules, but also applies to functions and code within the same module.

    I will use a small sub to return the count so that how you actually implement the count is abstracted from the caller's logic:

    for $i (0..varcount($some_variable)) { ... } sub varcount { # you can implement the count any way you like # and change the implementation any time you like, # the caller will not get affected, as long as the # interface to varcount is unchanged }

Re: Style: buried variables or double referencing?
by rjbs (Pilgrim) on Aug 20, 2005 at 20:00 UTC

    Using Readonly::XS is nice and all, but I generally just use $NAMING_STYLE to tell the difference. It's not foolproof, but it's simple. (I'm sure that now I will go write code that breaks itself by assigning to a fake constant.)

    What surprises me is that lack of mentions of putting those data into configuration. I'm not sure what kind of efficiency you're working for, but things like constants are often best moved to options or configuration. You can change them all you like without having to worry about rev'ing the code.

    rjbs
      > lack of mentions of putting those data into configuration

      holli did, above in Re^3: Style: buried variables or double referencing?. I really resonate with that suggestion. External config files, external templates... the rule is simple -- anything and everthing external to the code should be kept externally.

      --

      when small people start casting long shadows, it is time to go to bed
Re: Style: buried variables or double referencing?
by Anonymous Monk on Aug 22, 2005 at 10:10 UTC
    I'd write that as:
    use Readonly; Readonly my $LOW => 9; Readonly my $HIGH => 14; ... my $count = $some_variable eq 'x' ? $HIGH : $LOW; for $i (0 .. $count) { ... }
    Or
    use Readonly; Readonly my %VALUES => (x => 14, y => 9); ... for $i (0 .. $VALUE{$some_variable}) { ... }
    a bit depending on whether you need $count elsewhere, and what 9, 14, $some_variable, 'x', 'y' actually stand for.

    The main drive of programming it this way is that I strongly dislike 'magical' literals (specially numbers) in the middle of the code. Unless I know the bigger picture, I can't see what the 9 and 14 stand for. Nor do I know whether their values can be changed without having to change the value elsewhere in the program as well. Defining them as constants helps solving this problem.

Re: Style: buried variables or double referencing?
by tomazos (Deacon) on Aug 22, 2005 at 02:45 UTC
    And while we are at it how about a...

    use Carp::Assert; assert ($some_variable eq 'x' or $some_variable eq 'y') if DEBUG;

    ...before we enter this piece of code.

    -Andrew.


    Andrew Tomazos  |  andrew@tomazos.com  |  www.tomazos.com