dmtelf has asked for the wisdom of the Perl Monks concerning the following question:
Problem - code snippet below prints global array OK before subroutine is called. Array is passed to a subroutine and printed.
Only the 1st record gets printed inside the subroutine, but everything gets printed outside the subroutine. Why?
@AllFileDetails[0] = "blah, blah, blah";
@AllFileDetails[1] = "foo, bar, diddledum";
print "Printing array.\n";
print @AllFileDetails;
print "\nCalling subroutine now\n";
&WriteToDisk(@AllFileDetails,files.txt);
exit;
sub WriteToDisk
{
@arraytowrite = shift;
$writefile = shift;
$text = shift;
print "\nInside the subroutine 'WriteToDisk' now.\n";
print "Printing array.\n";
print @arraytowrite;
exit;
open (HANDLE,$writefile) || die "Cannot open open output file $wri
+tefile for writing\n";
print HANDLE $ReportGenerated;
print HANDLE @arraytowrite;
close HANDLE;
}
RE: Weird array printing/passing to subroutine problem (kudra: switch order of args, use strict)
by kudra (Vicar) on Jul 10, 2000 at 16:10 UTC
|
Like ar0n said, if you want to pass an array, you should
do it later, as all args are put in @_ together.
Other notes:
- This won't pass strict. Your variables in the subroutine
should probably be 'my', and if you really want your
arrays global, declare them as such.
Read what
davorg
has to say on using these in example code.
- You're only passing two arguments to your sub,
but you're trying to get three out of @_, but
it doesn't look as if the first arg of @AllFileDetails
is really meant to be $text.
- Put quotes around the string you're sending to the
function if you want it preserved.
Example:
use strict;
use vars qw(@AllFileDetails); # I'm using 5.005_03
$AllFileDetails[0] = "blah, blah, blah";
$AllFileDetails[1] = "foo, bar, diddledum";
print "Printing array.\n";
print @AllFileDetails;
print "\nCalling subroutine now\n";
&WriteToDisk("files.txt", @AllFileDetails);
exit; sub WriteToDisk
{
my ($writefile, @arraytowrite) = @_;
# Thanks to davorg for pointing out that I was
# only getting part of the array with my earlier
# code! *blush*
print "\nInside the subroutine 'WriteToDisk' now.\n";
print "writefile = $writefile\n";
print @arraytowrite;
print "\n";
}
The above was called with perl -w. | [reply] [d/l] |
Re: Weird array printing/passing to subroutine problem
by c-era (Curate) on Jul 10, 2000 at 16:14 UTC
|
You can also pass a referance to an array if you want to keep your params in the same order.
@AllFileDetails[0] = "blah, blah, blah";
@AllFileDetails[1] = "foo, bar, diddledum";
print "Printing array.\n";
print @AllFileDetails;
print "\nCalling subroutine now\n";
&WriteToDisk(\@AllFileDetails,"files.txt");
exit;
sub WriteToDisk
{
($arraytowrite,$writefile,$text) = @_;
print "\nInside the subroutine 'WriteToDisk' now.\n";
print "Printing array.\n";
print @$arraytowrite;
exit;
open (HANDLE,$writefile) || die "Cannot open open output file
+ $writefile for writing\n";
print HANDLE $ReportGenerated;
print HANDLE @$arraytowrite;
close HANDLE;
}
| [reply] [d/l] |
RE: Weird array printing/passing to subroutine problem
by mikfire (Deacon) on Jul 10, 2000 at 17:23 UTC
|
Since we are here to refine our perl skills, I cannot add
anything ot the answers already given, but I can ask why
you are using positional parameters?
It may be me ( my mind is like a steel trap... rusted open )
but I have an awful time remembering arbitrary order like this.
I have found it much easier on myself to use named parameters
instead. This also allows a much nicer syntax to give errors
or default values.
My take on your code would be
@AllFileDetails[0] = "blah, blah, blah";
@AllFileDetails[1] = "foo, bar, diddledum";
print "Printing array.\n";
print @AllFileDetails;
print "\nCalling subroutine now\n";
&WriteToDisk(details => \@AllFileDetails,
filename => 'files.txt');
exit;
sub WriteToDisk
{
my %params = @_;
my @arraytowrite = @{$params{details}} || ();
my $writefile = $params{filename} || '';
my $text = $params{text} || 'no text';
unless ( @arraytowrite && $writefile ) {
print "Usage: WriteToDisk( filename => file to use,
details => reference to an array holding the detai
+ls
";
return 0;
}
print "\nInside the subroutine 'WriteToDisk' now.\n";
print "Printing array.\n";
print @arraytowrite;
exit;
open HANDLE,$writefile or die "Cannot open open output file $write
+file for writing:$!\n";
print HANDLE $ReportGenerated;
print HANDLE @arraytowrite;
close HANDLE;
}
This way, you never need to memorize the call order. Also
notice the slight changes I made to the open HANDLE statement.
You really want to use parens around both the open and the die
arguements combined with ||, or you want to forget all the
parens and use 'or'. Don't mix.
Mik Firestone ( perlus bigotus maximus ) | [reply] [d/l] |
Re: Weird array printing/passing to subroutine problem
by davorg (Chancellor) on Jul 10, 2000 at 16:15 UTC
|
There's a problem with your parameter passing. Within the subroutine, the parameters are recieved in the special array @_. This contains all of the parameters in one flattened list. In your case, this gives you:
$_[0] = 'blah, blah, blah';
$_[1] = 'foo, bar, diddledum';
$_[2] = 'files'txt';
When you then use shift to extract these values into local variables, it only pulls values off @_ one at a time, so you end up with
@arraytowrite = ('blah, blah, blah');
$writefile = 'foo, bar, diddledum';
$text = 'files'txt';
Which will probably explain the problem you see.
What you should be doing is either passing a reference to the array or passing the array as the last parameter and pulling the scalars off the front first.
--
<http://www.dave.org.uk>
European Perl Conference - Sept 22/24 2000, ICA, London
<http://www.yapc.org/Europe/> | [reply] [d/l] [select] |
|
Great point about the flattening of the passed array. That seems to hang a lot of new Perl programmers, myself included. Hangs them until they read perldoc perlsub.
| [reply] [d/l] |
RE: Weird array printing/passing to subroutine problem
by ar0n (Priest) on Jul 10, 2000 at 16:05 UTC
|
When you do a shift on @arraytowrite,
it only gets the first value in the array ($arraytowrite[0])
see the docs on shift.
What you want is:
$writefile = shift;
$text = shift;
@arraytowrite = @_;
You'll have to switch around you params, though.
-- ar0n
| [reply] [d/l] |
RE: Weird array printing/passing to subroutine problem
by Ovid (Cardinal) on Jul 10, 2000 at 20:21 UTC
|
Just a quick comment. I've seen this a couple of times in this thread:
- You'll have to switch around you params (meaning that the filename to print to should be first)
- If you want to pass an array, you should do it later (meaning that the array, or reference to such, should be passed AFTER the filename -- same as above).
- You can also pass a referance to an array if you want to keep your params in the same order. (Passing an array reference is often a good idea, but not for this reason).
So we have two posts telling dmtelf that (s)he has to have the filename first in the argument list (not true). We have one post stating that dmtelf can pass an array reference to keep the arguments in the same order -- this is not why an array reference is passed. The following would allow him to preserve his argument order:
$writefile = pop;
@arraytowrite = @_;
Everyone is suggesting alternatives, but that seems so natural that I'm surprised it wasn't brought up. The only reason I mention this is that some shops have may have a standard that the filename comes last in an argument list or perhaps dmtelf was updating a function that is in a library that is required into many programs. If either of those were the case, we wouldn't necessarily have the luxury of changing the order of arguments.
Also, though several people have corrected this, I don't see that it has been explicitly mentioned. @AllFileDetails[0] should be written as $AllFileDetails[0] with a '$' instead of an '@'. You're referencing a scalar here and Perl prefers that you use the scalar notation. What @AllFileDetails[0] actually does is take a one element array slice, which for all practical purposes is useless (and I suspect that it will have a negative impact on performance).
dmtelf, if you are not used to passing a reference to an array, it is used to speed processing and save memory. What happens is that rather than passing the entire array to the subroutine, Perl (when passing a reference), just passes a value telling the sub where the array can be found. If you have an array with hundreds of elements, passing the reference is much faster. The main danger is that changing array elements in the passed array will now change the elements in the original array also. To actually get at the values in the array that was passed be reference, use the arrow '->' operator. Here's an example:
#!/usr/bin/perl -w
use strict; # Always use this!!!
my @AllFileDetails;
$AllFileDetails[0] = "blah, blah, blah";
$AllFileDetails[1] = "foo, bar, diddledum";
print "Printing array.\n";
print @AllFileDetails;
print "\nCalling subroutine now\n";
&WriteToDisk(\@AllFileDetails, "garbage", "more garbage", "files.txt")
+;
print $AllFileDetails[1]; # This will print the new value
exit;
sub WriteToDisk
{
my $arraytowrite = $_[0];
my $writefile = pop;
print "\nInside the subroutine 'WriteToDisk' now.\n";
print "Printing array.\n";
print $arraytowrite->[0] . " " . $arraytowrite->[1];
$arraytowrite->[1] = "New value\n"; # We've now changed the val
+ue of $AllFileDetails[1]!
print "\n" . $writefile . "\n";
}
Cheers,
Ovid | [reply] [d/l] [select] |
Re: Weird array printing/passing to subroutine problem
by ahunter (Monk) on Jul 10, 2000 at 21:37 UTC
|
I'm surprised no-one has suggested it yet, but you can use
prototyping to force things to happen the way you want:
sub my_sub (\@$)
{
my $array = shift;
my $scalar = shift;
# $array is a reference to the array
# $scalar is the next argument
}
my_sub @array, "scalar";
Perl will automagically change @array into a reference for you.
See perlsub for more information about prototyping.
Andrew. | [reply] [d/l] |
Re: Weird array printing/passing to subroutine problem
by jjhorner (Hermit) on Jul 10, 2000 at 16:45 UTC
|
You are not using the 'strict' pragma. You therefore
receive -- from me. This may just be a short snippet of
code, but new people look at this and try to copy it. Don't
shoot them in the foot.
Most people complain about not knowing why someone votes
down their posts. I'm explaining why.
Moral: USE STRICT and USE WARNINGS!
Update:
kudra, again, points out something to which I'm oblivious.
I do not mean to sound so brutal. I'm just trying to help you
improve your perl code. Using 'strict' and 'warnings' will
save you debugging hours and money, if you do this for a living.
Take what I say in the spirit with which I say it: as a
gentle rebuke to not-so-cool coding. I'm not personally
attacking you. After all, you are a perl coder, that makes
you better than the rest of the world. :)
Special thanks to kudra. Between her and my new wife,
I will be ready for public consumption in no time!
Update #2:
Once again, kudra proves to be wise beyond her years.
here
is a link to the post that started this trend.
J. J. Horner
Linux, Perl, Apache, Stronghold, Unix
jhorner@knoxlug.org http://www.knoxlug.org/
| [reply] |
|
eduardo's thread explorations of consciousness and symbolic references...
might give you new insight into the 'strict' controversy
personally, i'd love to use strict but there are things
that i can't do with it on (i've tried)
i always try to use it for the first few lines of a
proggie but wind up turning it off to get the job done
i'd love to send my code to a perl clinic, so i could learn
how to do those things (and i try that here from time to time ;)
but i worry about treadding to close to the line on my NDA
Update:
after an email discussion with dave (i tried to include jjhorner but to no avail), i've
got to admit that i was wrong. my being limited to non-strict code was a limitation of my
own in how to use global variables
eduardo's node did however make me feel better about not knowing how to do it tho' ;)
thanks to dave fo going through a mighty heafty chunk of code, his responses are about
to spawn another thread
| [reply] |
|
J. J. Horner
Linux, Perl, Apache, Stronghold, Unix
jhorner@knoxlug.org http://www.knoxlug.org/
| [reply] |
|
I'd also like to see examples of things that you can't do with use strict switched on. The only thing I can think of is symbolic references and there are better ways to do most of the things that they are used for.
Even if you must do things that break use strict what's to stop you making most of the code strict-clean and putting no strict around the problem areas - together with a comment explaining what is going on.
--
<http://www.dave.org.uk>
European Perl Conference - Sept 22/24 2000, ICA, London
<http://www.yapc.org/Europe/>
| [reply] |
|
While I agree that your code should be able to pass both strict and warnings, I don't think that is always deserving of a -- vote. If the question being asked is directly caused by their failure to use these tools, and they are a professed newbie, then they need to be shown the light, not the door. On the other hand, if they are posting code in the snipits section, and it can't pass "-wc -Mstrict", then vote --. There is no reason to be unilateral about voting around here. If there was, then voting would be pointless.
-Adam
BTW, I trust that you only mean that the code should be able to compile with the "use strict;" pragma, not that the poster need include that line in their post, (unless it is a complete script).
| [reply] |
|
|