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

I have the following code and the following two situations, the code strips a pair of numbers from a string and adds them together and writes to a report the details of the operation (100 rounds of incremental iteration for $num1, $num2) using formats then it asks if the user wants to run another round by providing a string containing another pair of numbers (I would still have to work on the if conditional at the end for better functionality anyways). The code works fine when run in DOS shell but
Situation1: when I tried to associate with the write function another filehandle t +han STDOUT to make it write the report to a file it tells me the file +handle isn't defined, uncomment the lines 32 and 33 to see that.
Situation2: for every run of the report I wanted to make the program start a new e +pisode on another page (i.e it starts a new episode at page 3 if it s +topped at page 2)
I couldn't recognize where I went wrong for the first situation, and for the other situation I could not figure how to manipulate the $%, $= and $- variables. So I would welcome your suggestions .
#!/usr/local/bin/perl use strict; use warnings; my($title,$num1, $num2, $result); format report_TOP= @||||||||||||||@>>>>>>>> $title , "Page $%" --------------------------------- NUMBER1 || NUMBER2 || ADDITION --------------------------------- . format report= @###@#########@############ $num1, $num2, $result . $~="report"; $^="report_TOP"; $title = "Some Report"; START: print "Please Enter a text containing pairs of numbers\n"; while(<>){ if(/^(exit|quit)$/){exit;} ($num1, $num2) = (/(\d+)/g) for ($_); add($num1, $num2); } sub add{ # open FILE,'>REPORT.txt' or die "could not open it $!\n"; # select FILE; for(my $index=0;$index<100;$index++){ $result = $num1 + $num2; write; $num1++; $num2++; } print "another Pair of numbers? Y N\n"; if(<> =~ /^N$/i){ exit; }else{ print "Please Enter a text containing pairs of numb +ers\n"; "START"; } }
Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind

Replies are listed 'Best First'.
Re: Writing to a file using Formats
by jethro (Monsignor) on Aug 01, 2009 at 18:32 UTC

    The answer can be found in the manpage to select:

    "Second, references to variables related to output will refer to this output channel".

    See also the example directly below that sentence.

    Since you define the format for STDOUT (before the select() call) no format is defined for FILE. Solution: Move the format definition after the select()

    UPDATE: Since no one else seemed willing to risk brain damage to answer your second question I had to do it myself: Setting $- to 0 will force a new page on the next write. This is described in write

      Thanks, in situation 2 it worked fine, I could start a new page by setting $- to 0 after a certain run so no overlapping sort of thing is involved. unfortunately for the first case, this doesn't seem to be the answer, I have tried it over and over, it gives me blank command shell when I did move the format definition after the select(), and the file created therefore would still be blank, I seem to not be able to associate to select the filehandle in a proper manner, another error I get is
      undefined format "FILE" called at Program.pl ...
      when I explicitly associate to write() the filehandle "FILE", there is something else which I am not noticing apparently...so still this situation is open for the experiences of Perl Monks to tackle it. Thanks for everyone in advance.
      Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind

        Well, I tried it out and it worked, so I'm pretty sure it was the right answer. Note the error message you are getting specifically tells you that the format(!!!) is undefined.

        You could post your updated code if you don't find the mistake yourself

Re: Writing to a file using Formats
by jwkrahn (Abbot) on Aug 01, 2009 at 23:49 UTC

    format report_TOP=

    You don't define the report filehandle yet.   You should define it first.


    $~="report";
    $^="report_TOP";
    $title = "Some Report";

    You define these variables too late to use them.


            ($num1, $num2) = (/(\d+)/g) for ($_);

    That should simply be:

            ( $num1, $num2 ) = /\d+/g;


    sub add{

    Your subroutine is called "add" but it also opens a file, increments two variables and queries the user for a yes/no answer and a pair of numbers.   Either make the name more descriptive or reduce the functionality to match the name.


                       "START";

    You have a string in void context which should generate a warning message.   But if you are trying to jump to the START label you shouldn't try to do that from inside a subroutine.

      While the comments here are great, I've personally found using the Perl6::Form module to be easier to use, less finicky, and has made my code more readable and maintainable. It's written by Damian Conway; he recommends it in his book _Perl Best Practices_.

      An example from the CPAN page:

      use Perl6::Form; $text = form " =================================== ", "| NAME | AGE | ID NUMBER |", "|----------+------------+-----------|", "| {<<<<<<} | {||||||||} | {>>>>>>>} |", $name, $age, $ID, "|===================================|", "| COMMENTS |", "|-----------------------------------|", "| {[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[} |", $comments, " =================================== ";

      -- Burvil

Re: Writing to a file using Formats
by Marshall (Canon) on Aug 02, 2009 at 12:41 UTC
    This format thing is great in the right situation and I have used it. But often it is not the best way. It is "sort of an in-between approach". I think that better is: a) use printf or b) use a very zoomy Perl module.

    There are some problems with your command loop. Normally this question of "are you finished or not?" is a bad idea. Instead of "another Pair of numbers? Y/N?", just prompt for the next pair of numbers like at the first prompt. If I don't have another set of numbers, then I quit, exit, CTL-C or whatever.

    I may have goofed up something below, but I did run this code on my machine and it should give you some ideas about the command loop and how to use printf() for this problem.

    #!usr/bin/perl -w use strict; my $title = "SOME REPORT TITLE"; my $LINES_PER_PAGE =10; my $page =1; my $line =9999; sub get_header_string { "\n$title , Page $page\n". " ---------------------------------\n". " NUMBER1 || NUMBER2 || ADDITION\n". " ---------------------------------\n"; } while ( (print "Enter a pair of numbers (space between) or quit: "), (my $line =<STDIN>) !~ /^\s*q(uit)?\s*$/i ) { next if $line =~ m/^\s*$/; #re-prompt for blank lines my ($num1, $num2, $kruft) = split(/\s+/,$line); if ( $kruft #user input validation is || ($num1 =~ m/\D/) #important, I like this style || ($num2 =~ m/\D/) #of formatting, others like ) #something different { print_command_error(); next; } foreach (1..100) { output ($num1, $num2, $num1+$num2); $num1++; $num2++; } } sub print_command_error { print "Illegal command args....\n"; print "some other message..probably usage message\n"; } sub output { my ($num1, $num2, $sum) = @_; if ($line++ >= $LINES_PER_PAGE) { if ($page >1) {print "\f"} print get_header_string; $line = 1; $page++; } printf (" %-9d %-10d %-10d\n", $num1, $num2, $sum); } __END__ C:\TEMP>useprintf.pl Enter a pair of numbers (space between) or quit: 1 2 SOME REPORT TITLE , Page 1 --------------------------------- NUMBER1 || NUMBER2 || ADDITION --------------------------------- 1 2 3 2 3 5 3 4 7 4 5 9 5 6 11 6 7 13 7 8 15 8 9 17 9 10 19 10 11 21 \f SOME REPORT TITLE , Page 2 --------------------------------- NUMBER1 || NUMBER2 || ADDITION --------------------------------- 11 12 23 12 13 25 13 14 27 14 15 29 15 16 31 16 17 33 17 18 35 18 19 37 19 20 39 20 21 41 \f SOME REPORT TITLE , Page 3 ...blah....
    Update: I fiddled with this some more to get error handling right, etc. Not any big deal, but appended...There are some fancy Perl modules for tabular output. But this app only appears to need less than a dozen lines of real formatting code (the output() routine below).
      I will use your code to train on error handling, I heard of existence of tabular modules, the thing is, I don't want to go through shortcuts, I want to learn Perl and what all there could be to it before advancing to these shortcuts employment, like when you know how something works base up is better than landing at its peak and not knowing how to climb down if you needed sometimes.. I will refer to your code for this basic error handling idea too... Thanks for your generosity
      Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind
        Yes, I would say the use of "warnings" and "strict" is THE most important thing here.

        I hope that you also are able to use the standard command line loop I gave you to great effect during the coming years. This is a standard pattern that you will use over and over again.

        When you write a "while" loop, you want the main termination condition to be right there in the while()! Not buried further on down in the guts. That's not to say that other exit conditions can't exist in the "body", just that the "normal" situation should be apparent.

        The technique that I used is called the "comma operator". I guess crudely put, this is way to cram 2 or more statements into the place where normally just one would go. The truth of the while() is determined by the last comma statement, in this case whether this is a "quit" command or not.

        Now, some folks go "crazy" with this comma thing. I mean you could pretty much cram 10 statements in there. That is a bad idea!

        Yes, there are many fancy Perl modules for formatting text. I think that once you really understand printf() you will use either it or one of those modules and this format statement will fall by the wayside - or that's what happened in my learning experience. The format thing just didn't work out to give me as much control as I often needed.