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

Fellow monks,

Although Jeffa was kind enough to try and help me with my problem, I'm still stuck.

I've got the following code:

#!/usr/bin/perl -w use strict 'vars'; my %list; my ($key, $value, $cust, $total); format STDOUT_TOP = +-----------------------------------------------------------------+ | Monthly Recap | |----------------------------+----------+-----------+-------------+ |Account | Prior | Current | Difference | |----------------------------+----------+-----------+-------------+ . format STDOUT = |@<<<<<<<<<<<<<<<<<<<<<<<<<<<|@#####.## | @#####.## | @#####.## | $cust,$list{$cust}->{prior}, $list{$cust}->{new}, $total |----------------------------+----------+-----------+-------------+ . &readfile('sfoct01.txt', 'prior'); # &readfile('sfnov01.txt', 'new'); &printdata; sub readfile{ my ($file, $money) = @_; my ($line, $amount); die unless open FH, $file; while ($line = <FH>){ chomp($line); ($cust, $amount) = split('",',($line)); $cust =~ s/"//g; if (exists $list{$cust}){ $list{$cust}->{$money} += $amount; } else{ $list{$cust}->{$money} = $amount; } $list{$cust}->{new} = 10; } close FH; } sub printdata{ foreach $cust (sort keys %list){ $total = $list{$cust}->{new} - $list{$cust}->{pr +ior}; write; } }

Which is all very well and simple and good, but it isn't working!

Well it is working from the standpoint that my data structure is being built correctly. However my data output is the last item in the hash, for every item in the hash. The only data that is correct is the calculated value $total.

What I can't seem to get a handle on is if it is possible to use the HoH variables in the format statement or do I have to use temporary variables?

There is no emoticon for what I'm feeling now.

Replies are listed 'Best First'.
Re: Using HoH data in formats
by jarich (Curate) on Dec 20, 2002 at 01:36 UTC
    What I can't seem to get a handle on is if it is possible to use the HoH variables in the format statement or do I have to use temporary variables?

    The problem isn't with using your HoH variables in the format statement, as you'll notice that your prior and new fields are getting a value. The problem is with your $cust variable. As far as I can tell, the format watches each time that the $cust variable is set explictly and maintains that value somehow. Since assignment ($cust = 1) is different to aliasing (foreach $cust (@list)) in some way, the last assigned value to $cust (ie the last customer read in) is the value that the format keeps.

    I think that there might be a special variable that tells the format to reset all its notions of what things are but you don't really need to use it here.

    This code will do what you want it to:

    my %list; my ($customer, $total); # use $custome +r instead of $cust format STDOUT_TOP = +-----------------------------------------------------------------+ | Monthly Recap | |----------------------------+----------+-----------+-------------+ |Account | Prior | Current | Difference | |----------------------------+----------+-----------+-------------+ . format STDOUT = |@<<<<<<<<<<<<<<<<<<<<<<<<<<<|@#####.## | @#####.## | @#####.## | $customer, $list{$customer}->{prior}, $list{$customer}->new, $total |----------------------------+----------+-----------+-------------+ . &readfile('sfoct01.txt', 'prior'); # &readfile('sfnov01.txt', 'new'); &printdata; sub readfile{ my ($file, $money) = @_; my ($line, $amount); die unless open FH, $file; while ($line = <FH>){ chomp($line); my ($cust, $amount) = split('",',($line)); # localise thi +s $cust $cust =~ s/"//g; if (exists $list{$cust}){ $list{$cust}->{$money} += $amount; } else{ $list{$cust}->{$money} = $amount; } $list{$cust}->{new} = 10; } close FH; } sub printdata{ foreach my $cust (sort keys %list){ # localised +$cust $customer = $cust; # assign to +$customer $total = $list{$cust}->{new} - $list{$cust}->{pri +or}; write; } }
    Hope it helps.

    jarich

    Update: Thought it might help to make the differences more explicit.

Re: Using HoH data in formats
by BrowserUk (Patriarch) on Dec 20, 2002 at 01:46 UTC

    Assigning the value of $_ to $cust fixes the problem...

    sub printdata{ foreach (sort keys %list){ #! CHANGED $cust = $_; #! ADDED $total = $list{$cust}->{new} - $list{$cust}->{pr +ior}; write; } } __DATA__ "fred", 10 "barney", 30 "Bam bam", 5 "Wilma", 200 "Betty", 300 # Output +-----------------------------------------------------------------+ | Monthly Recap | |----------------------------+----------+-----------+-------------+ |Account | Prior | Current | Difference | |----------------------------+----------+-----------+-------------+ |Bam bam | 5.00 | 10.00 | 5.00 | |----------------------------+----------+-----------+-------------+ |Betty | 300.00 | 10.00 | -290.00 | |----------------------------+----------+-----------+-------------+ |Wilma | 200.00 | 10.00 | -190.00 | |----------------------------+----------+-----------+-------------+ |barney | 30.00 | 10.00 | -20.00 | |----------------------------+----------+-----------+-------------+ |fred | 10.00 | 10.00 | 0.00 | |----------------------------+----------+-----------+-------------+

    My understanding (which is possibly wrong) is that when the format is compiled, a pointer is taken. As the iterator in a foreach loop gets aliased to the things in the list, this effectively bypasses that pointer. By doing the assignment to $cust in the body of the loop, the pointer (reference, whatever) taken when the format is compiled remains valid. That's my best speculation. With luck diotalevi will do his magic with the devel::* modules and get the real skinny.


    Examine what is said, not who speaks.

(jeffa) Re: Using HoH data in formats
by jeffa (Bishop) on Dec 20, 2002 at 04:34 UTC
    Hiya Popcorn Dave. :)

    The more and more i think about your problem (and i still don't know exactly what you are trying to do), the more and more i feel you should be using DBD::CSV. Why? Because your data files are CSV and you are reporting on them, and when it comes to reporting, SQL is quite nice.

    The following code was based off of BrowserUk's data and output.
    use strict; use warnings; use DBI; use File::Basename; my @stuff; my $file = 'sfoct01.txt'; my $table = (fileparse($file,'.txt'))[0]; my $dbh = DBI->connect( "DBI:CSV:f_dir=.;csv_eol=\n;csv_sep_char=,;", {RaiseError=>1}, ); $dbh->{csv_tables}->{$table} = { file => $file, col_names => [qw(account amount)], }; my $sth = $dbh->prepare(" SELECT account,amount FROM $table ORDER BY account "); $sth->execute; while (my ($account,$amount) = $sth->fetchrow_array()) { @stuff = ($account, $amount, 10, 10 - $amount); write; } format STDOUT_TOP = +-----------------------------------------------------------------+ | Monthly Recap | |----------------------------+----------+-----------+-------------+ |Account | Prior | Current | Difference | |----------------------------+----------+-----------+-------------+ . format STDOUT = |@<<<<<<<<<<<<<<<<<<<<<<<<<<<|@#####.## | @#####.## | @#####.## | @stuff |----------------------------+----------+-----------+-------------+ . __END__ yields: +-----------------------------------------------------------------+ | Monthly Recap | |----------------------------+----------+-----------+-------------+ |Account | Prior | Current | Difference | |----------------------------+----------+-----------+-------------+ |Bam bam | 5.00 | 10.00 | 5.00 | |----------------------------+----------+-----------+-------------+ |Betty | 300.00 | 10.00 | -290.00 | |----------------------------+----------+-----------+-------------+ |Wilma | 200.00 | 10.00 | -190.00 | |----------------------------+----------+-----------+-------------+ |barney | 30.00 | 10.00 | -20.00 | |----------------------------+----------+-----------+-------------+ |fred | 10.00 | 10.00 | 0.00 | |----------------------------+----------+-----------+-------------+

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Using HoH data in formats
by ibanix (Hermit) on Dec 20, 2002 at 01:31 UTC
    format STDOUT_TOP = ... ...
    Interesting, I didn't know you could do this with perl. This is all called when write is called?

    The two other things that jumped out at me:

    Why not $list{$cust}{$money} += $amount instead of $list{$cust}->{$money} += $amount? The -> isn't really necessary.

    Where you you compute $list{$cust}->{prior}? I don't see it anywhere else in the code, save the place where you try to print it.

    Update: Oh, I'm an idiot. $money is set to prior by the function call. /me slaps forehead and slinks away....

    Cheers,
    ibanix

    $ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
      Yep. You can set all that stuff to be printed in that 'format' when the write command is called. From what the docs say, it's sort of like nroff for Perl. Never having used nroff I can't say, but for my application currently, I do need it.

      Mutters something unintelligeble about having to output to a dot matrix printer...

      There is no emoticon for what I'm feeling now.