"Too many arguments" when passing an array to Perl sub? - perl

I have a function below in perl
sub create_hash()
{
my #files = #_;
foreach(#files){
if(/.text/)
{
open($files_list{$_},">>$_") || die("This file will not open!");
}
}
}
I am calling this function by passing an array argument like below:
create_hash( #files2);
The array has got around 38 values in it.
But i am getting compilation errors:
Too many arguments for main::create_hash at ....
what is the wrong that i am doing here?
my perl version is :
This is perl, v5.8.4 built for i86pc-solaris-64int
(with 36 registered patches, see perl -V for more detail)

Your problem is right here:
sub create_hash()
{
The () is a prototype. In this case, it indicates that create_hash takes no parameters. When you try to pass it some, Perl complains.
It should look like
sub create_hash
{
In general, you should not use prototypes with Perl functions. They aren't like prototypes in most other languages. They do have uses, but that's a fairly advanced topic in Perl.

May use array reference as:
sub create_hash {
my ($files) = #_;
foreach(#{$files)){
...
}
}
create_hash(\#files2);

Related

Perl sub returns a subroutine

I haven't used Perl for around 20 years, and this is confusing me. I've g******d for it, but I obviously haven't used a suitable search string because I haven't found anything relating to this...
Why would I want to do the following? I understand what it's doing, but the "why" escapes me. Why not just return 0 or 1 to begin with?
I'm working on some code where a sub uses "return sub"; here's a very truncated example e.g.
sub test1 {
$a = shift #_;
if ($a eq "cat") {
return sub {
print("cat test OK\n");
return 0;
}
}
# default if "cat" wasn't the argument
return sub {
print("test for cat did not work\n");
return 1;
}
}
$c = test1("cat");
print ("received: $c\n");
print ("value is: ",&$c,"\n");
$c = test1("bat");
print ("received: $c\n");
print ("value is: ",&$c,"\n");
In your code there is no reason to return a sub. However, with a little tweak
sub test1 {
my $animal = shift #_;
if ($animal eq "cat" || $animal eq "dog") {
return sub {
print("$animal test OK\n");
return 0;
};
}
# default if "cat" or "dog" wasn't the argument
return sub {
print("test for cat or dog did not work\n");
return 1;
};
}
We now have a closure around $animal this saves memory as the test for cat and dog share the same code. Note that this only works with my variables. Also note that $a and $b are slightly special to Perl, they are used in the block of code that you can pass to the sort function and bypass some of the checks on visibility so it's best to avoid them for anything except sort.
You probably want to search "perl closures".
There are many reasons that you'd want to return a code reference, but it's not something I can shortly answer in a StackOverflow question. Mark Jason Dominus's Higher Order Perl is a good way to expand your mind, and we cover a little of that in Intermediate Perl.
I wrote File::Find::Closures as a way to demonstrate this is class. Each subroutine in that module returns two code references—one for the callback to File::Find and the other as a way to access the results. The two share a common variable which nothing else can access.
Notice in your case, you aren't merely calling a subroutine to "get a zero". It's doing other things. Even in your simple example there's some output. Some behavior is then deferred until you actually use the result for something.
Having said that, we have no chance of understanding why the programmer who wrote your particular code did it. One plausible guess was that the system was set up for more complex situations and you're looking at a trivial example that fits into that. Another plausible guess was that the programmer just learned that feature and fell in love with it then used it everywhere for a year. There's always a reason, but that doesn't mean there's always a good reason.

Perl:Issue passing self

I'm having an issue with a recent code review. I've been advised to change the following function call:
storeShmcoreservJobsLogs(
$self->{'shmJobDetails'},
$self->{'nhcJobDetails'},
$self->{'cppDetails'},
$self->{'siteId'},
$neTypesIdMap,
$dbh
);
To only use two arguments, being $self and $dbh. So I have coded as follows
storeShmcoreservJobsLogs($self, $dbh);
And the function signature as follows:
sub storeShmcoreservJobsLogs($$) {
my($self, $dbh) = #_;
if ( $#{$self->$shmJobDetails} > -1 ) {
The if statement unfortunately throws an error with the value of $shmJobDetails when I test the change
Global symbol "$shmJobDetails" requires explicit package name at /data/ddp/current/analysis/TOR/elasticsearch/handlers/misc/Shm.pm line 148.
So I must have misinterpreted the instruction. Is anything obvious wrong?
There's no variable $shmJobDetails so you get the compilation error. Do the same thing that you were doing before:
sub storeShmcoreservJobsLogs {
my($self,$dbh)=#_;
if ( $#{ $self->{'shmJobDetails'} } > -1 ) {
Now you're passing the complete object and the subroutine can use any part of the object it needs.
You might want to make some object methods to answer the questions you'll ask it instead of accessing its internals. That method can do all the work to figure out true or false:
sub storeShmcoreservJobsLogs {
my($self,$dbh)=#_;
if ( $self->has_jobs ) {
The use of a lexical variable called $self implies that you're using object-oriented methods, but your code is far from being object-oriented
Are you sure that you understand the points being made in the code review? It's looking like you're writing a method, and the fields should be extracted from the hash within the method
The method definition should be more like this
sub store_shmcoreserv_jobs_logs {
my $self = shift;
my ($id_map, $dbh) = #_;
my #fields = #{$self}{qw/ shmJobDetails nhcJobDetails cppDetails siteId /};
...
while the call should look like this
$self->storeShmcoreservJobsLogs($neTypesIdMap, $dbh)
All of this is essential to Perl object-oriented programming. You should study perlobj together with the rest of the Perl language reference

Not enough arguments when redefining a subroutine

When I redefine my own subroutine (and not a Perl built-in function), as below :
perl -ce 'sub a($$$){} sub b {a(#_)}'
I get this error :
Not enough arguments for main::a at -e line 1, near "#_)"
I'm wondering why.
Edit :
The word "redefine" is maybe not well chosen. But in my case (and I probably should have explained what I was trying to do originally), I want to redefine (and here "redefine" makes sense) the Test::More::is function by printing first Date and Time before the test result.
Here's what I've done :
Test::More.pm :
sub is ($$;$) {
my $tb = Test::More->builder;
return $tb->is_eq(#_);
}
MyModule.pm :
sub is ($$;$) {
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is(#_);
}
The prototype that you have given your subroutine (copied from Test::More::is) says that your subroutine requires two mandatory parameters and one optional one. Passing in a single array will not satisfy that prototype - it is seen as a single parameter which will be evaluated in scalar context.
The fix is to retrieve the two (or three) parameters passed to your subroutine and to pass them, individually, to Test::More::is.
sub is ($$;$) {
my ($got, $expected, $test_name) = #_;
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is($got, $expected, $test_name);
}
The problem has nothing to do with your use of a prototype or the fact that you are redefining a subroutine (which, strictly, you aren't as the two subroutines are in different packages) but it's because Test::More::is() has a prototype.
You are not redefining anything here.
You've set a prototype for your sub a by saying sub a($$$). The dollar signs in the function definition tell Perl that this sub has exactly three scalar parameters. When you call it with a(#_), Perl doesn't know how many elements will be in that list, thus it doesn't know how many arguments the call will have, and fails at compile time.
Don't mess with prototypes. You probably don't need them.
Instead, if you know your sub will need three arguments, explicitly grab them where you call it.
sub a($$$) {
...
}
sub b {
my ($one, $two, $three) = #_;
a($one, $two, $three);
}
Or better, don't use the prototype at all.
Also, a and b are terrible names. Don't use them.
In Perl, prototypes don't validate arguments so much as alter parsing rules. $$;$ means the sub expects the caller to match is(EXPR, EXPR) or is(EXPR, EXPR, EXPR).
In this case, bypassing the prototype is ideal.
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is(#_);
}
Since you don't care if Test::More::is modifies yours #_, the following is a simple optimization:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is;
}
If Test::More::is uses caller, you'll find the following useful:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
goto &Test::More::is;
}

'Goto undefined subroutine &main::1' writing a simple Perl debugger

I'm trying to write a simple Perl debugger and I'm running into the following problem.
I'm running the following code as a debugger:
{
package DB;
sub DB { }
sub sub
{
&$sub;
# this is what produces the problem
$i = 1*1;
}
}
1;
I'm loading this in by setting the PERL5DB environment variable - e.g.:
export PERL5DB="BEGIN { require './debugger/tracer.pl'; }
Given this simple little Perl script:
#!/usr/bin/env perl
use Getopt::Long;
print "hello world";
I'm running the script as:
perl -d test.pl
When run, it generates the following error:
$ perl -d test.pl
Goto undefined subroutine &main::1 at /home/vagrant/perl5/perlbrew/perls/perl-5.16.0/lib/site_perl/5.16.0/Exporter.pm line 25.
BEGIN failed--compilation aborted at test.pl line 6.
I've isolated the problem to anything that is run after the &$sub; call in sub in the debugger. This problem is happening with certain packages being included in the base Perl script - in this case, Getopt::Long, though I've also found the same result with IO::File.
My Perl is pretty rusty, particularly with respect to advanced topics like the debugger.
Can anyone help me understand how I can get code executing after the &$sub; call in sub in the debugger to place nicely with the packages that I'm importing?
Thanks!
When you leave a Perl subroutine without using an explicit return statement, Perl will return the value of the last statement in the subroutine.
In particular, this means that if you have a subroutine that calls another subroutine as its last statement, like this:
package DB {
sub sub {
warn "Hello from DB::sub, about to call $sub\n";
&$sub;
}
}
then the return value of the other subroutine called via &$sub will be passed to the original caller, just as if you'd done an explicit return &$sub.
However, if the &$sub call is not the last thing in your DB::sub subroutine, then Perl will just throw away its return value and instead return the value of you actual last statement — in this case $i = 1*1, which evaluates to the number 1.
Now, when you define a custom debugger like that, Perl will wrap every ordinary subroutine call with a call to your DB::sub subroutine. Thus, your code causes every subroutine call to return the number 1! It's hardly a surprise that this will break a lot of things very badly.
Specifically, based on your error message, it looks like something in the Exporter module (which is used by many other modules to export symbols to the caller's namespace) is calling a subroutine that should return a reference to another subroutine. But since, because of your debugger, it's actually returning 1, the following attempt to call the returned subroutine ends up trying to call a subroutine named 1 (which gets mapped to the main:: package because numeric symbol names are superglobal), which then fails.
But what if you really need to do something in your DB::sub after calling &$sub? Well, the workaround is to save the return value, like this:
package DB {
sub DB { }
sub sub {
warn "Hello from DB::sub, about to call $sub...\n";
# call &sub, save the return value in #rv
my #rv = (wantarray ? &$sub : scalar &$sub);
warn "Hello again from DB::sub, just called $sub and got #rv!\n";
# ...and return the saved return value
return (wantarray ? #rv : $rv[0]);
}
}
1;
(The code is slightly complicated by the fact that our DB::sub might be called in either list or scalar context, and we need to pass the appropriate context on to &$sub. The wantarray should take care of that, though.)
Adding on to the answer from Ilmari Karonen.
DB::sub can also be called in a no value (void) context, therefore the return handling needs to take this into account. Refer to the documentation in wantarray for more details.
The following code handles all three cases.
package DB {
sub DB { }
sub sub {
# call &sub, save the return value in #rv
my #rv;
if(defined(wantarray)) {
#rv = (wantarray ? &$sub : scalar &$sub);
}
else {
# wantarray is undef
&$sub;
}
# after invoking &$sub
# return #rv
if(defined(wantarray)) {
return (wantarray ? #rv : $rv[0]);
}
else {
return undef
}
}
}
1;

Why does this Perl produce "Not a CODE reference?"

I need to remove a method from the Perl symbol table at runtime. I attempted to do this using undef &Square::area, which does delete the function but leaves some traces behind. Specifically, when $square->area() is called, Perl complains that it is "Not a CODE reference" instead of "Undefined subroutine &Square::area called" which is what I expect.
You might ask, "Why does it matter? You deleted the function, why would you call it?" The answer is that I'm not calling it, Perl is. Square inherits from Rectangle, and I want the inheritance chain to pass $square->area through to &Rectangle::area, but instead of skipping Square where the method doesn't exist and then falling through to Rectangle's area(), the method call dies with "Not a CODE reference."
Oddly, this appears to only happen when &Square::area was defined by typeglob assignment (e.g. *area = sub {...}). If the function is defined using the standard sub area {} approach, the code works as expected.
Also interesting, undefining the whole glob works as expected. Just not undefining the subroutine itself.
Here's a short example that illustrates the symptom, and contrasts with correct behavior:
#!/usr/bin/env perl
use strict;
use warnings;
# This generates "Not a CODE reference". Why?
sub howdy; *howdy = sub { "Howdy!\n" };
undef &howdy;
eval { howdy };
print $#;
# Undefined subroutine &main::hi called (as expected)
sub hi { "Hi!\n" }
undef &hi;
eval { hi };
print $#;
# Undefined subroutine &main::hello called (as expected)
sub hello; *hello = sub { "Hello!\n" };
undef *hello;
eval { hello };
print $#;
Update: I have since solved this problem using Package::Stash (thanks #Ether), but I'm still confused by why it's happening in the first place. perldoc perlmod says:
package main;
sub Some_package::foo { ... } # &foo defined in Some_package
This is just a shorthand for a typeglob assignment at compile time:
BEGIN { *Some_package::foo = sub { ... } }
But it appears that it isn't just shorthand, because the two cause different behavior after undefining the function. I'd appreciate if someone could tell me whether this is a case of (1) incorrect docs, (2) bug in perl, or (3) PEBCAK.
Manipulating symbol table references yourself is bound to get you into trouble, as there are lots of little fiddly things that are hard to get right. Fortunately there is a module that does all the heavy lifting for you, Package::Stash -- so just call its methods add_package_symbol and remove_package_symbol as needed.
Another good method installer that you may want to check out is Sub::Install -- especially nice if you want to generate lots of similar functions.
As to why your approach is not correct, let's take a look at the symbol table after deleting the code reference:
sub foo { "foo!\n"}
sub howdy; *howdy = sub { "Howdy!\n" };
undef &howdy;
eval { howdy };
print $#;
use Data::Dumper;
no strict 'refs';
print Dumper(\%{"main::"});
prints (abridged):
$VAR1 = {
'howdy' => *::howdy,
'foo' => *::foo,
};
As you can see, the 'howdy' slot is still present - undefining &howdy doesn't actually do anything enough. You need to explicitly remove the glob slot, *howdy.
The reason it happens is precisely because you assigned a typeglob.
When you delete the CODE symbol, the rest of typeglob is still lingering, so when you try to execute howdy it will point to the non-CODE piece of typeglob.