What does #data actually contain in PDF::Report::Table $table_write->addTable(#data);? - perl

I think I've got the gist of creating a table using Perl's PDF::Report and PDF::Report::Table, but am having difficulty seeing what the 2-dimensional array #data would look like.
The documentation says it's a 2-dimensional array, but the example on CPAN just shows an array of arrays test1, test2, and so on, rather than the example showing data and formatting like $padding $bgcolor_odd, and so on.
Here's what I've done so far:
$main_rpt_path = "/home/ics/work/rpts/interim/mtr_prebill.rpt";
$main_rpt_pdf =
new PDF::Report('PageSize' => 'letter', 'PageOrientation' => 'Landscape',);
$main_rpt_tbl_wrt =
PDF::Report::Table->new($main_rpt_pdf);
Obviously, I can't pass a one dimensional array, but I have searched for examples and can only find the one in CPAN search.
Edit:
Here is how I am trying to call addTable:
$main_rpt_tbl_wrt->addTable(build_table_writer_array($pt_column_headers_ref, undef));
.
.
.
sub build_table_writer_array
# $data -- an array ref of data
# $format -- an array ref of formatting
#
# returns an array ref of a 2d array.
#
{
my ($data, $format) = #_;
my $out_data_table = undef;
my #format_array = (10, 10, 0xFFFFFF, 0xFFFFCC);
$out_data_table = [[#$data],];
return $out_data_table;
}
and here is the error I'm getting.
Use of uninitialized value in subtraction (-) at /usr/local/share/perl5/PDF/Report/Table.pm line 88.
at /usr/local/share/perl5/PDF/Report/Table.pm line 88
I cannot figure out what addTable wants for data. That is I am wondering where the formatting is supposed to go.
Edit:
It appears the addData call should look like
$main_rpt_tbl_wrt->addTable(build_table_writer_array($pt_column_headers_ref), 10,10,xFFFFFF, 0xFFFFCC);
not the way I've indicated.

This looks like a bug in the module. I tried running the example code in the SYNOPSIS, and I got the same error you get. The module has no real tests, so it is no surprise that there would be bugs. You can report it on CPAN.
The POD has bugs, too.
You increase your chances of getting it fixed if you look at the source code and fix it yourself with a patch.

Related

perl nested while loop - half working

I was hoping that someone might be able to assist me. I'm new to Perl and generally getting some good results from some small scripts I've written, however, I'm stuck on a nested while loop of a new script I'm working on.
The script I've put together performs two mysql select statements, and then places the results into to separate arrays. I then want to check from the first element in the first array against all of the results in the second array. Then move to the second element in the first array and check for against all results in the seconds array and so on.
The goal of the script is to find an IP address in the first array and see which subnets it fits into in the second...
What I find is happening is that the script runs through on only the first element on the first array and all elements on the second array, then stops.
Here is the extract of the perl script below - if anyone could point me int the right direction I would really appreciate it.
my #ip_core_wan_field;
while ( #ip_core_wan_field = $wan_core_collection->fetchrow_array() ) {
my $coreipAddr = #ip_core_wan_field[1];
my #ip_wan_field;
while ( #ip_wan_field = $wan_collection->fetchrow_array() ) {
my $ipAddr = #ip_wan_field[1];
my $network = NetAddr::IP->new( #ip_wan_field[4], #ip_wan_field[5] );
my $ip = NetAddr::IP->new($coreipAddr);
if ( $ip->within($network) && $ip ne $ipAddr ) {
print "$ip IS IN THE SAME subnet as $network \n";
}
else {
print "$coreipAddr is outside the subnet for $network\n\n";
}
}
}
Your sql queries are single pass operations. If you want to loop over the second collection more than once, you need to either cache the values and interate over the cache, or rerun the query.
I would of course advise that you go with the first option using fetchall_arrayref
my $wan_arrayref = $wan_collection->fetchall_arrayref;
while ( my #ip_core_wan_field = $wan_core_collection->fetchrow_array() ) {
my $coreipAddr = #ip_core_wan_field[1];
for my $ip_wan_field_ref (#$wan_arrayref) {
my #ip_wan_field = #$ip_wan_field_ref;
There are of course other ways to make this operation more efficient, but that's the crux of your current problem.

What does this mean in Perl 1..$#something?

I have a loop for example :
for my $something ( #place[1..$#thing] ) {
}
I don't get this statement 1..$#thing
I know that # is for comments but my IDE doesn't color #thing as comment. Or is it really just a comment for someone to know that what is in "$" is "thing" ? And if it's a comment why was the rest of the line not commented out like ] ) { ?
If it has other meanings, i will like to know. Sorry if my question sounds odd, i am just new to perl and perplexed by such an expression.
The $# is the syntax for getting the highest index of the array in question, so $#thing is the highest index of the array #thing. This is documented in perldoc perldata
.. is the range operator, and 1 .. $#thing means a list of numbers, from 1 to whatever the highest index of #thing is.
Using this list inside array brackets with the # sigill denotes that this is an array slice, which is to say, a selected number of elements in the #place array.
So assuming the following:
my #thing = qw(foo bar baz);
my #place = qw(home work restaurant gym);
then #place[1 .. $#thing] (or 1 .. 2) would expand into the list work, restaurant.
It is correct that # is used for comments, but not in this case.
it's how you define a range. From starting value to some other value.
for my $something ( #place[1..3] ) {
# Takes the first three elements
}
Binary ".." is the range operator, which is really two different
operators depending on the context. In list context, it returns a list
of values counting (up by ones) from the left value to the right
value. If the left value is greater than the right value then it
returns the empty list. The range operator is useful for writing
foreach (1..10) loops and for doing slice operations on arrays. In the
current implementation, no temporary array is created when the range
operator is used as the expression in foreach loops, but older
versions of Perl might burn a lot of memory when you write something
like this:
http://perldoc.perl.org/perlop.html#Range-Operators

Printing a 2500 x 2500 dimensional matrix using Perl

I am very new to Perl. Recently I wrote a code to calculate the coefficient of correlation between the atoms between two structures. This is a brief summary of my program.
for($i=1;$i<=2500;$i++)
{
for($j=1;$j<=2500;$j++)
{
calculate the correlation (Cij);
print $Cij;
}
}
This program prints all the correlations serially in a single column. But I need to print the correlations in the form of a matrix, something like..
Atom1 Atom2 Atom3 Atom4
Atom1 0.5 -0.1 0.6 0.8
Atom2 0.1 0.2 0.3 -0.5
Atom3 -0.8 0.9 1.0 0.0
Atom4 0.3 1.0 0.8 -0.8
I don't know, how it can be done. Please help me with a solution or suggest me how to do it !
Simple issue you're having. You need to print a NL after you finish printing a row. However, while i have your attention, I'll prattle on.
You should store your data in a matrix using references. This way, the way you store your data matches the concept of your data:
my #atoms; # Storing the data in here
my $i = 300;
my $j = 400;
my $value = ...; # Calculating what the value should be at column 300, row 400.
# Any one of these will work. Pick one:
my $atoms[$i][$j] = $value; # Looks just like a matrix!
my $atoms[$i]->[$j] = $value; # Reminds you this isn't really a matrix.
my ${$atoms[$1]}[$j] = $value; # Now this just looks ridiculous, but is technically correct.
My preference is the second way. It's just a light reminder that this isn't actually a matrix. Instead it's an array of my rows, and each row points to another array that holds the column data for that particular row. The syntax is still pretty clean although not quite as clean as the first way.
Now, let's get back to your problem:
my #atoms; # I'll store the calculated values here
....
my $atoms[$i]->[$j] = ... # calculated value for row $i column $j
....
# And not to print out my matrix
for my $i (0..$#atoms) {
for my $j (0..$#{ $atoms[$i] } ) {
printf "%4.2f ", $atoms[$i]->[$j]; # Notice no "\n".
}
print "\n"; # Print the NL once you finish a row
}
Notice I use for my $i (0..$#atoms). This syntax is cleaner than the C style three part for which is being discouraged. (Python doesn't have it, and I don't know it will be supported in Perl 6). This is very easy to understand: I'm incrementing through my array. I also use $#atom which is the length of my #atoms array -- or the number of rows in my Matrix. This way, as my matrix size changes, I don't have to edit my program.
The columns [$j] is a bit tricker. $atom[$i] is a reference to an array that contains my column data for row $i, and doesn't really represent a row of data directly. (This is why I like $atoms[$i]->[$j] instead of $atoms[$i][$j]. It gives me this subtle reminder.) To get the actual array that contains my column data for row $i, I need to dereference it. Thus, the actual column values are stored in row $i in the array array #{$atoms[$i]}.
To get the last entry in an array, you replace the # sigil with $#, so the last index in my
array is $#{ $atoms[$i] }.
Oh, another thing because this isn't a true matrix: Each row could have a different numbers of entries. You can't have that with a real matrix. This makes using an Array of Arrays in Perl a bit more powerful, and a bit more dangerous. If you need a consistent number of columns, you have to manually check for that. A true matrix would automatically create the required columns based upon the largest $j value.
Disclaimer: Pseudo Code, you might have to take care of special cases and especially the headers yourself.
for($i=1;$i<=2500;$i++)
{
print "\n"; # linebreak here.
for($j=1;$j<=2500;$j++)
{
calculate the correlation (Cij);
printf "\t%4f",$Cij; # print a tab followed by your float giving it 4
# spaces of room. But no linebreak here.
}
}
This is of course a very crude and quick and dirty solution. But if you save the output into a .csv file, most csv-able spreadsheet programs (OpenOfice) should easily be able to read it into a proper table. If the spreadsheet viewer of your choice can not understand tabs as delimeter, you could easily add ; or / or whatever it can use into the printf string.

How do you concatenate strings in a Puppet .pp file?

Here is my naive approach:
# puppet/init.pp
$x = 'hello ' +
'goodbye'
This does not work. How does one concatenate strings in Puppet?
Keyword variable interpolation:
$value = "${one}${two}"
Source: http://docs.puppetlabs.com/puppet/4.3/reference/lang_variables.html#interpolation
Note that although it might work without the curly braces, you should always use them.
I use the construct where I put the values into an array an then 'join' them.
In this example my input is an array and after those have been joined with the ':2181,' the resulting value is again put into an array that is joined with an empty string as separator.
$zookeeperservers = [ 'node1.example.com', 'node2.example.com', 'node3.example.com' ]
$mesosZK = join([ "zk://" , join($zookeeperservers,':2181,') ,":2181/mesos" ],'')
resulting value of $mesosZK
zk://node1.example.com:2181,node2.example.com:2181,node3.example.com:2181/mesos
Another option not mentioned in other answers is using Puppet's sprintf() function, which functions identically to the Ruby function behind it. An example:
$x = sprintf('hello user %s', 'CoolUser')
Verified to work perfectly with puppet. As mentioned by chutz, this approach can also help you concatenate the output of functions.
The following worked for me.
puppet apply -e ' $y = "Hello" $z = "world" $x = "$y $z" notify { "$x": } '
notice: Hello world
notice: /Stage[main]//Notify[Hello world]/message: defined 'message' as 'Hello world'
notice: Finished catalog run in 0.04 seconds
The following works as well:
$abc = "def"
file { "/tmp/$abc":
You could use the join() function from puppetlabs-stdlib. I was thinking there should be a string concat function there, but I don't see it. It'd be easy to write one.
As stated in docs, you can just use ${varname} interpolation. And that works with function calls as well:
$mesosZK = "zk://${join($zookeeperservers,':2181,')}:2181/mesos"
$x = "${dirname($file)}/anotherfile"
Could not use {} with function arguments though: got Syntax error at '}'.

XML::Simple removes root element

Hi I have a xml data which i get from array of hashes and when I do a Dumper on it the output is as follows:
$var1=
'<Data>
<Data1>ABC</Data1>
<Data2>ABCD</Data2>
</Data>';
This I have in a variable call $var1. Now I am using XML::Simple on it.. it is somewhat like: {Data1=>'ABC',Data2=>'ABCd'};
The first tag Data is gone. What is wrong?
Seems to be well-documented:
KeepRoot => 1:
In its attempt to return a data structure free of superfluous detail
and unnecessary levels of indirection, XMLin() normally discards the
root element name. Setting the KeepRoot option to 1 will cause the
root element name to be retained. So after executing this code:
$config = XMLin('<config tempdir="/tmp" />', KeepRoot => 1)
You'll be able to reference the tempdir as
"$config->{config}->{tempdir}" instead of the default
"$config->{tempdir}".