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

I'm trying to build a script that checks link availability and licence costs for given frequencies, channel widths and antenna lengths. Some antenna lengths are not compatible with certain frequencies. The purpose of this section of code is to compare all frequency, channel width and antenna length combination and calculate the availability but not running the subroutines for availability and price calculations if the frequency does not match the antenna length.

$FREQUENCY_QUERY = $dbh->prepare("SELECT `id`, `frequencies` FROM `microwave-frequencies` ORDER BY `frequencies` DESC"); $CHANNEL_QUERY = $dbh->prepare("SELECT `channel` FROM `microwave-modulation-lookup` WHERE `rate` = '$DATA_RATE'"); $SITE_A_ANTENNA_LENGTH_QUERY = $dbh->prepare("SELECT `length` FROM `microwave-antenna-length-lookup`"); $SITE_B_ANTENNA_LENGTH_QUERY = $dbh->prepare("SELECT `length` FROM `microwave-antenna-length-lookup`"); $FREQUENCY_QUERY->execute( ); while ( ($FREQUENCY_ID) = @FREQUENCY_QUERY = $FREQUENCY_QUERY- +>fetchrow_array() ) { $FREQUENCY_ID = "$FREQUENCY_QUERY[0]"; $FREQUENCY = "$FREQUENCY_QUERY[1]"; $CHANNEL_QUERY->execute( ); while ( ($CHANNEL_SIZE) = @CHANNEL_QUERY = $CHANNEL_QUERY- +>fetchrow_array() ) { $CHANNEL_SIZE = "$CHANNEL_QUERY[0]"; $SITE_A_ANTENNA_LENGTH_QUERY->execute( ); foreach ( ($SITE_A_ANTENNA_LENGTH) = @SITE_A_ANTENNA_L +ENGTH_QUERY = $SITE_A_ANTENNA_LENGTH_QUERY->fetchrow_array() ) { $SITE_A_ANTENNA_LENGTH = "$SITE_A_ANTENNA_LENGTH_QUERY +[0]"; $SITE_B_ANTENNA_LENGTH_QUERY->execute( ); foreach ( ($SITE_B_ANTENNA_LENGTH) = @SITE_B_ANTEN +NA_LENGTH_QUERY = $SITE_B_ANTENNA_LENGTH_QUERY->fetchrow_array() ) { $SITE_B_ANTENNA_LENGTH = "$SITE_B_ANTENNA_LENG +TH_QUERY[0]"; &conflict_check; &calculate_rsl; &calculate_oav_h; &calculate_oav_v; &calculate_licence_fee; $tb->addRow( "$TABLE_ROW_COUNT", "$DATA_RATE Mbps", "$ +FREQUENCY GHz", "$CHANNEL_SIZE MHz", "$SITE_A_ANTENNA_LENGTH", "$SITE +_B_ANTENNA_LENGTH", "$OVERALL_AVAILABILITY_H_RESULT %", "$OVERALL_AVA +ILABILITY_V_RESULT %", "&pound;$ANNUAL_LICENCE_FEE", "<a href='ofcom- +document-generator.cgi?LINKDETAILS=True&FREQUENCY=$FREQUENCY&CHANNEL_ +SIZE=$CHANNEL_SIZE&SITE_A_ANTENNA_LENGTH=$SITE_A_ANTENNA_LENGTH&SITE_ +B_ANTENNA_LENGTH=$SITE_B_ANTENNA_LENGTH'>Link Details</a>" ); $TABLE_ROW_COUNT++; if ($LINK_TYPE eq 'Core Infrastructure') { if ($OVERALL_AVAILABILITY_H_RESULT >= 99.99) { $tb->setCellClass ($TABLE_ROW_COUNT, 7, 'tbrowgree +n'); } elsif ($OVERALL_AVAILABILITY_H_RESULT < 99.99) { $tb->setCellClass ($TABLE_ROW_COUNT, 7, 'tbrowerro +r'); } if ($OVERALL_AVAILABILITY_V_RESULT >= 99.99) { $tb->setCellClass ($TABLE_ROW_COUNT, 8, 'tbrowgree +n'); } elsif ($OVERALL_AVAILABILITY_V_RESULT < 99.99) { $tb->setCellClass ($TABLE_ROW_COUNT, 8, 'tbrowerro +r'); } } elsif ($LINK_TYPE eq 'Customer Link') { if ($OVERALL_AVAILABILITY_H_RESULT >= 99.95) { $tb->setCellClass ($TABLE_ROW_COUNT, 7, 'tbrowgree +n'); } elsif ($OVERALL_AVAILABILITY_H_RESULT < 99.95) { $tb->setCellClass ($TABLE_ROW_COUNT, 7, 'tbrowerro +r'); } if ($OVERALL_AVAILABILITY_V_RESULT >= 99.95) { $tb->setCellClass ($TABLE_ROW_COUNT, 8, 'tbrowgree +n'); } elsif ($OVERALL_AVAILABILITY_V_RESULT < 99.95) { $tb->setCellClass ($TABLE_ROW_COUNT, 8, 'tbrowerro +r'); } } } } } }

For some reason, the two inner most foreach's, if I change them to whiles, seem to ignore the &conflict_check; next statement and throw up a logarithmic error due to the fact that the frequency does not work with the antenna length (Can't take log of -0.364299 at /var/www/dev/ofcom-document-generator.cgi line 3358.). A little example of the &conflict_check; :

sub conflict_check { if (($FREQUENCY eq '7') && ($SITE_A_ANTENNA_LENGTH eq '0.3')) { next; } if (($FREQUENCY eq '7') && ($SITE_B_ANTENNA_LENGTH eq '0.3')) { next; } if (($FREQUENCY eq '11') && ($SITE_A_ANTENNA_LENGTH eq '0.3')) { next; } if (($FREQUENCY eq '11') && ($SITE_B_ANTENNA_LENGTH eq '0.3')) { next; } if (($FREQUENCY eq '28') && ($SITE_A_ANTENNA_LENGTH eq '0.8')) { next; } if (($FREQUENCY eq '28') && ($SITE_B_ANTENNA_LENGTH eq '0.8')) { next; } ...and so on...

It is of my understanding that the next; should exit the current while (or foreach) and so should not process any of the other subroutine calls, therefore should not attempt to `log` the number which is usually a minus.

The two inner most foreach's do work if they are foreach's and not while's as earlier noted, however they seem to exit after just one loop so the only antenna length that gets calculated is 0.3. Possible lengths are 0.3, 0.6, 0.8, 1.0 and 1.2 for both site A and site B.

Frequency SQL query outputs: 7, 11, 13, 15, 18, 23, 26, 28, 38

Channel Width SQL query outputs: 7, 14, 28, 56

I have put all 4 SQL queries into Perl arrays and referenced them that way, but it has not changed anything, they behave in the same way as with SQL query outputs.

Many thanks to anyone who can shed any light onto the situation.

Kind regards,

Ben

Replies are listed 'Best First'.
Re: Nested While Loops, foreach and next
by ikegami (Patriarch) on Jun 01, 2011 at 17:40 UTC

    Your code is rather unreadable due to poor indenting.

    Your post includes far more code than necessary to demonstrate the problem.

    Your post includes too little to demonstrate the problem. You're saying the problem is that a call to log is being reached when it shouldn't, yet there is no such function call in your code.

    You've made it impossible for us to help you because Perl does indeed behave as you think it should.

    When next is called in

    for (...) { ... &conflict_check; ... log ... } sub conflict_check { ... if (...) { next; } ... }
    Perl does indeed skip the to the next pass of the loop directly (skipping log), although it does issue a warning* because the following would be clearer
    for (...) { ... next if &conflict_exists; ... log ... } sub conflict_exists { ... if (...) { return 1; } ... return 0; }

    * — Please tell me you are using use strict; use warnings;.

      Thanks for the reply.

      I have made the recommended changes to &conflict_check and the innermost foreach but the behaviour is the same. The best way I could describe it is that it is treating the next as a last.

      I always use use strict and warnings - it actually makes it easier because the errors are more helpful for fixing things before they have a confusing knock-on effect for other lines.

      All of the lines I have included are wrapped by PerlMonks - it's much clearer without this. There are several logs in the script, they are part of an equation a couple of thousand lines long with 185 variables. I know this code works because they are working fine as part of a manual process where frequency, antenna length and channel width are selected by the user. The only part that doesn't work properly is this while/foreach nested loop which doesn't cycle through antenna lengths as expected.

      I have included below all lines that I could find with log (in no particular order) and the log subroutine. All of the log lines are either part of &calculate_rsl;, &calculate_oav_h or &calculate_oav_v; - all of which shouldn't be reached if certain antenna lengths match incompatible frequencies as defined in &conflict_check;.

      $FREE_SPACE_PATH_LOSS = 92.45+20*log10($PATH_LENGTH)+20*log10($FREQUEN +CY); $OUTAGE_FACTOR_H = 0.139735174-0.172*log10($FADE_MARGIN/$PATH_ATTENUAT +ION_H); $GEOCLIMATIC_CONVERSION_FACTOR = (10.3-5*log10(1-abs(cos($LATITUDE))** +0.7)-2.8*log10($PATH_LENGTH)+1.8*log10(abs($INCLINATION))); $OUTAGE_FACTOR_V = 0.139735174-0.172*log10($FADE_MARGIN/$PATH_ATTENUAT +ION_V); sub log10 { my $n = shift; return log($n)/log(10); }

      Thanks for any additional help that anybody can offer :-)

      Kind regards,

      Ben

        foreach ( ($SITE_B_ANTENNA_LENGTH) = @SITE_B_ANTENNA_LENGTH_QUERY = $S +ITE_B_ANTENNA_LENGTH_QUERY->fetchrow_array() )
        looks really weird. In fact, I have no idea what you are trying to accomplish. Maybe you want
        foreach my $SITE_B_ANTENNA_LENGTH ( $SITE_B_ANTENNA_LENGTH_QUERY->fetc +hrow_array() )
        or
        my @SITE_B_ANTENNA_LENGTHS = $SITE_B_ANTENNA_LENGTH_QUERY->fetchrow_ar +ray(); foreach my $SITE_B_ANTENNA_LENGTH (@SITE_B_ANTENNA_LENGTHS)
Re: Nested While Loops, foreach and next
by Marshall (Canon) on Jun 02, 2011 at 23:40 UTC
    As ikegami commented, I also found the indenting style to hamper understanding. This is more than line wrap, which is actually fine. It is a matter of what you have lined up with what. Example:
    $FREQUENCY_QUERY->execute( ); while ( ($FREQUENCY_ID) = @FREQUENCY_QUERY = $FREQUENCY_QUERY- +>fetchrow_array() ) { $FREQUENCY_ID = "$FREQUENCY_QUERY[0]"; $FREQUENCY = "$FREQUENCY_QUERY[1]"; $CHANNEL_QUERY->execute( ); ### better ### $FREQUENCY_QUERY->execute( ); while ( ($FREQUENCY_ID) = @FREQUENCY_QUERY = $FREQUENCY_QUERY->fetchro +w_array() ) { $FREQUENCY_ID = "$FREQUENCY_QUERY[0]"; $FREQUENCY = "$FREQUENCY_QUERY[1]"; $CHANNEL_QUERY->execute( ); .... }
    The studies that I've read say that the number of characters of indentation between levels should be either 3 or 4, 2 is too few and 5 or more is too many. In practice, I have found this to be good advice.

    You claim that use strict; and use warnings; are being used. In the case of strictures, one of the purposes is to limit the visibility of variables to just where you need it. If there is some huge block that pre-declares things like $FREQUENCY_ID and gives it scope that makes it visible in all other code, it defeats the whole purpose.

    I would expect to see code like this:

    $FREQUENCY_QUERY->execute( ); while ( my ($FREQUENCY_ID, $FREQUENCY) = $FREQUENCY_QUERY->fetchrow_ar +ray() ) { $CHANNEL_QUERY->execute( ); .... } ####### #note # $FREQUENCY_ID = "$FREQUENCY_QUERY[0]"; #not needed, delete # $FREQUENCY = "$FREQUENCY_QUERY[1]"; #not needed, delete #
    I don't think that your foreach() loops do what you want. If you replace "foreach" with "while", what happens will be very different, so it is not clear at all to me what you are saying works and what doesn't. See if you can replicate the problem with a much more simple example.

      Thanks for your help guys.

      Ben