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

This program is to prompt the user for a temperature and use the input to compute potential temperature at each pressure level from 500 to 1000 in increments of 100. I used:
my (@thetas, @press); my ($i); use strict; use warnings; my $outfilename = "pot_temp.txt";# define an output filename open(TFILE,">$outfilename"); print "What is the temperature (K)? \n"; my $T = <STDIN>; chomp ($T); for ($i=500, $i=900 , $i += 100) { my $theta = $T*((1013/$i)**(287/1004)); push (@press, $i); push (@thetas,$theta); } print TFILE "A T of $T results in thetas of @thetas at the pressure le +vels @press \n";
The file gets written fine, but the results are bogus so the problem is in the loop. Specifically, I get three identical values for $theta and $i, and I should get 5 different values.

Thanks to those that offered advice to my last "Improve my Coding" post. I tried to implement some suggestions.

Replies are listed 'Best First'.
Re: For loop trouble
by graff (Chancellor) on Jun 13, 2009 at 17:21 UTC
    Now that others have explained the C-style loop structure that you intended to use, it might help to know that Perl has another kind of for-loop structure: for ( list, of, items ) { ...}

    By using commas in your original code, that is the type of loop you created. Your first attempt also demonstrates how this "for (list...)" structure evaluates (creates) the entire list before doing the first iteration. In effect, the OP code is sort of equivalent to:

    $i = 500; $i = 900; $i += 100; for ( $i, $i, $i ) { .... }
    In this sort of for loop (with no explicit loop variable given, as in for my $x (list...)), at each iteration, the special global variable "$_" is set to the given value in the list, so the body of the loop would have done the same thing as the OP code if written as follows:
    my $theta = $T * (( 1013 / $_ ) ** ( 287/1004 )); push ( @press, $_ ); push ( @thetas, $theta );
    (updated to fix grammar mistake, and to add more info in last paragraph)
Re: For loop trouble
by Utilitarian (Vicar) on Jun 13, 2009 at 16:47 UTC
    for ($i=500;$i<=900;$i+=100)
      I was taught that for structure was like this: (starting value, ending value, increment). But I infer from your response that it must be: (starting value, continue while this is true, increment)?
        It's for (EXPR1; EXPR2; EXPR3) {...}. Note that there are semi-colons between the expressions, not commas.

        The first expression is run before entering the loop. Before each iteration the second expression is run; if false, the loop terminates. After each iteration, the third expression is run. For further details, see man perlsyn.

        Perls three-argument for loop is exactly like in C: initializer, test, and counting. See also http://en.wikipedia.org/wiki/For_loop#Three-expression_for_loops. Other languages may implement for loops differently, notably BASIC and its derivates often have constructs like for i=1 to 100 step 3 (counting up from 1 with increments to 3 until 100 is reached or exceeded) or even for i=100 downto 1 step 3 (counting down in steps of 3, starting at 100, stopping when 1 is reached or exceeded). Other languages have even stranger for loops, see the wikipedia article.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        I was taught that for structure was like this: (starting value, ending value, increment). But I infer from your response that it must be: (starting value, continue while this is true, increment)?

        Won't hurt you to read the docs...

        What's wrong:

        • The first expression has nothing to do with a "starting value". (Starting value of what??? It's not like C-style loops have counters.)

        • The last expression has nothing to do with an "increment". (Incrementing what??? It's not like C-style loops have counters.)

        What it really means

        • The first expression (if specified) is evaluated unconditionally before the loop is started.

        • The second expression is evaluated at the start of every loop pass (incl the first). If an expression is specified and it evaluates to something false, the look exits.

        • The third expression (if specified) is evaluated at the end of every loop pass (even if next is called).

        A common usage:

        for (my $i = 0; $i < $n; ++$i) { ... }

        The above is equivalent to the following, except $i is scoped to the loop.

        my $i = 0; while ($i < $n) { ... } continue { ++$i }
Re: For loop trouble
by Marshall (Canon) on Jun 14, 2009 at 05:16 UTC
    I offer some suggestions below.
    use strict; use warnings; # no need for your comment! it is clear!! my $outfilename = "pot_temp.txt"; # define an output filename # always check the return values of file operations !!! # there a bunch of syntax'es for this, the main thing is to do it! #open(TFILE,">$outfilename") || die ("unable to open $outfilename $!") +; # below I don't use TFILE, this is just a test to stdout #there are shorter ways to do this, but this is staightforward. #Maybe these are pressure increments? And I have wrong name? #whatever, this is one way to do it... my @temp_increments; for (my $i=500; $i<=900 ; $i += 100) { push @temp_increments, $i; } #my @temp_increments = qw( 500, 600, 700, 800, 900); #would be equivalent for example print "What is the temperature (K)?:"; my $T = <STDIN>; chomp ($T); print "T results are:\n"; foreach my $temp (@temp_increments) { my $theta = $T*((1013/$temp)**(287/1004)); print "temp = $temp theta=$theta \n"; } #Example output...... #C:\TEMP>perl temp.pl #What is the temperature (K)?:26 #T results are: #temp = 500 theta=31.8147304436198 #temp = 600 theta=30.1990838700515 #temp = 700 theta=28.8972548674746 #temp = 800 theta=27.8150092740013 #temp = 900 theta=26.8940943585568
    Update: I see that these are pressure increments instead of temperature but no matter. replace @temp_increments with @pressure_increments and my $temp with my $pressure and I think the same numeric result will happen.

    The C style for loop is rare in Perl. Strive to use this in as limited of scope and context as possible.