I'm passing some arguments to a function, one of which might be undefined.
$a = ($config->function(param('a'),'int'));
my module contains a function which looks like this:
sub function{
my $self = $_[0];
my $t = $_[1];
my $type = $_[2];
print "$self,$t,$type<br/>";
}
I've tried with shift instead of the #_ syntax, but there's no change.
The problem is that if $config->function is called with an undefined param('a') it prints like this:
MY_FUNC=HASH(0x206e9e0),name,
it seems that $t is being set to the value of what $type should be and the undef is being ignored completely.
undef is perfectly valid in a parameter list. I suspect that the problem here is that the param function is returning an empty list, rather than undef.
sub function {
no warnings 'uninitialized';
print join "/", #_;
}
function(undef,"foo"); # outputs "/foo"
function((), "foo"); # outputs "foo"
In the argument list to function, the param function is evaluated in list context.
sub param1 {
return; # undef in scalar context, but empty list in list context
}
sub param2 {
return undef; # scalar ctx: undef, list ctx: list with one undef elem
}
function(param1(), "foo"); # param1() -> () ... outputs "foo"
function(param2(), "foo"); # param2() -> (undef) ... outputs "/foo"
A workaround is to make sure that your param function is evaluated in scalar context.
function(scalar param1(), "foo"); # now outputs "/foo"
Note that actually saying return undef in your Perl subroutines is considered by some to be a code smell.
The CGI param method is correctly returning an empty list in the case of an error. This is standard behaviour for Perl subroutines and methods, which return a false value in the case of an erorr.
As #mob says, an explicit return undef is wrong precisely because it returns a one-element list in list context. This is a true value and implies success, which would break a lot of code if it was changed
All you need to do is change your code to extract the parameter separately
my $param = param('a');
$config->function($param, 'int'));
or if you're really keen to put the call in the parameter list then you can write
$config->function(scalar param('a'), 'int'));
or
$config->function(param('a') // undef, 'int'));
[I started this as a comment but it got too long].
Inside an argument list, arguments are evaluated in list context. Two answers have already noted that, but I think you missed it for all of the other things they were talking about.
Beyond that, param has to handle at least three cases, and the interface Lincoln chose, although sometimes annoying, is a good enough way to handle them.
The query parameter a does not appear at all:
b=1
The query parameter a appears, but with no value:
a=&b=1
The query parameter a appears with a value:
a=1&b=1
Related
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;
}
I want to create a hash of optional query parameters that are sometimes passed to my subroutine. Sometimes a query parameter called welcome is passed in, and the value is either 1 or 0.
If that variable exists, I want to add it to a hash.
I've created a configuration value called OPTIONAL_URL_PARAMS which is a list of expected parameter names that can be passed in:
use constant OPTIONAL_URL_PARAMS => ("welcome")
So far I have:
my $tempParams = {};
if ( $optionalParam ) {
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
if ($optionalParam eq $self->{$param}) {
$tempParams->{$param} = $optionalParam;
$tempParams->{$param} =~ s/\s+//g; # strip whitespace
}
}
}
But this tries to use the value of $self->{$param} instead of its name. i.e. I want welcome to match welcome, but it's trying to match 1 instead.
I know when it comes to keys in a hash you can use keys %hash, but is there a way you can do this with regular variables?
Edit
My subroutine is being called indirectly:
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
If $optionalParam's variable name is 'welcome', then I want to try and map it to the constant welcome.
This is not an answer any more, but I cannot remove it yet as there is still a discussion going on to clarify the question.
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
# ...
}
The return value of OPTIONAL_URL_PARAMS (you already got an error here and that's why you have the &, that should have told you something...) is simply a list, not an array ref. Actually at this point it should throw an error because you cannot use 1 as an array reference.
Edit
In Perl, when you pass arguments to a subroutine, all the values are flattened into a single list (reference). Specifically, if you are passing parameters to a sub, the sub doesn't know the names of the variables you originally used. It only knows their values. Therefore, if you want names as well as values, you have to pass them separately. An easy way is using a hash. E.g., in new():
my $class = shift;
my $param = shift; # The first, required parameter
my %therest = (#_); # Optional parameters, if any
Then you can say URL::Handler->new($param, 'welcome' => 1), and $therest{welcome} will have the value 1. You can also say
URL::Handler->new($param, 'welcome'=>1, 'another'=>42);
and %therest will have the two keys welcome and another.
See also some further discussion of passing whole hashes as parameters
Original
This also probably doesn't answer the question!
Some thoughts on the code from your comment.
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift; # OK; refers to URL::Handler
my $args = #_; # Problematic: now $args is the _number_ of args passed (list #_ evaluated in scalar context).
my $self = {
# There is no %args hash, so the next two lines are not doing what you expect.
# I actually don't know enough perl to know what they do! :)
param => $args{param},
optionalParams => $args{optionalParam}
};
}
Some thoughts:
use strict; and use warnings; at the top of your source file, if you haven't yet.
I can think of no languages other than Algol 60 that support this idea. It goes against the idea of encapsulation, and prevents you from using an array or hash element, a function call, a constant, or an expression as the actual parameter to a call
Variable names are purely for the convenience of the programmer and have no part in the functionality of any running program. If you wished so, you could write your code using a single array #memory and have "variables" $memory[0], $memory[1] etc. But you would be bypassing the most useful part of compiler technology that allows us to relate readable text to memory locations. It is best to consider those names to be lost once the program is running
The called code should be interested only in the values passed, and it would be a nightmare if the name of a variable passed as an actual parameter were significant within the subroutine
If you were able to access the name of a variable passed as a parameter, what do you suppose would be provided to subroutine stats if the call looked like this
stats( ( $base[$i] + 0.6 + sum(#offset{qw/ x y /}) + sum(#aa) ) / #aa )
In summary, it cannot be done in general. If you need to associate a value with a name then you should probably be looking at hashes
Your code
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
has a number of problems
You correctly shift the name of the class from parameter array #_, but then set my $args = #_, which sets $args to the number of elements remaining in #_. But the value of $args is irrelevant because you never use it again
You then set $self to a new anonymous hash, which is created with two elements, using the values from hash %args. But %args doesn't exist, so the value of both elements will be undef. Had you put use strict and use warnings 'all' in place you would have been alerted to this
The keys that you're using to access this non-existent hash are param and optionalParam, and I think it's more than a coincidence that they match the names of the actual parameters of the call to new
While Perl is unusual in that it allows programmatic access to its symbol tables, it is an arcane and unrecommended method. Those names are essentially hidden from the program and the programmer and while modules like Exporter must manipulate symbol tables to do their job, any such behaviour inside base-level software is very much to be avoided
Finally, you never use $self again after defining it. You should be blessing it into a class according to the $class variable (which contains the string URL::Handler) and returning it from the constructor
I hope this helps
Am trying to make a subroutine that replaces data depending on datatype: the problem is i can't get the datatype of the parameter, i used this:
sub replace {
my ($search, $replacement, $subject) = #_;
if (ref($search) eq "HASH") {
print "r is a reference to a HASH.\n";
}
elsif (ref($search) eq "SCALAR") {
print "r is a reference to a SCALAR.\n";
}
elsif (ref($search) eq "ARRAY") {
print "r is a reference to a ARRAY.\n";
}
}
my $str = "Foo";
my #arr = ("Foo");
replace($str);
replace(#arr);
But none works. am really new to perl
ref() takes a reference to something, not the something itself. Here:
replace($str);
replace(#arr);
...you are sending in the something directly. Send in a reference to the something instead, by putting a \ in front of it (which says, "take a reference to this something"):
replace(\$str);
replace(\#arr);
Output:
r is a reference to a SCALAR.
r is a reference to a ARRAY.
Note also that in your replace() function, in this line:
my ($search, $replacement, $subject) = #_;
You are effectively asking for a scalar value as the thing to search, so passing in a list (array, hash etc) will clobber $replacement and $subject if the passed in list has more than one element, so you may want to do something like this to ensure you're getting the proper params, and nothing is clobbered unexpectedly:
sub replace {
my ($search, $replacement, $subject) = #_;
die "first arg must be a ref\n" if ! ref $search;
Of course, you can do further argument checking, but this'll ensure that the first parameter can only be a reference to something. Instead of die(), you can also just return so the program doesn't crash, or print or warn and then return.
It is not stated what you want to do with it, but here's what is wrong with what you show.
The ref function shows the datatype of the reference subtmitted to it, or it returns an empty string if its argument isn't a reference at all.
So to get the expected behavior you should do
replace(\$str);
replace(\#arr);
Also, you need to add the test to your function
else (not ref $search)
for when a submitted string is not a reference.
For completeness, I should also point out an issue, explained in the answer by stevieb. When you pass an array to a function, it receives it as a flat list of arguments. With your function you clearly do not want replace(#arr). They are assigned to your list of scalar variables in order, one element to each. (As soon as there is an array variable it all goes into it.) See, for example, this post.
I'm pretty new to Perl and have a hard time wrapping my head around its very implicit type-system. What I like to achieve is a simple wrapper function that has the same signature as the function it wraps so it can be used in its place.
So let's say I have an existing function orig that I want to wrap. I takes a number of input arguments and has different return types based on those arguments. As long as I write the wrapper in the following way the return types are the same as the original function and it all works nicely:
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
return orig(#_);
}
However, if I want to execute some code in the wrapper after the execution of orig I don't know how I preserve the types. From my understanding, the same way the input to a perl function is always an array of scalars so is the output. So the solution then should be this:
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
my #result = orig(#_);
print "after";
return #result;
}
But that does not seem to work as expected. What am I missing? How to write such wrapper function that it works properly for arbitrary return types?
From my understanding, the same way the input to a perl function is
always an array of scalars so is the output.
No, not quite.
Perl functions can be called in list context, scalar context, or void context.
some_function(#args); # void
my $result = some_function(#args); # scalar
my #results = some_function(#args); # list
Many of Perl's built-in functions act differently according to which context they are called. For example grep returns a list of results in list context, and a count of results in scalar context.
If you are writing your own function and wish to behave differently in different contexts, the function can use the wantarray keyword to detect what context it's been called in. wantarray returns true for list context, false for scalar context, and undef for void context.
Even if you are not consciously wanting to write a function which behaves differently according to context, you may end up doing so accidentally by returning a context-sensitive expression such as grep, or map, or an array (arrays in scalar context return their length).
The correct way to wrap a function without breaking context is this. And yes, I realise it ain't pretty.
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
my #result =
wantarray ? orig(#_) :
defined wantarray ? scalar orig(#_) :
do { orig(#_); () };
print "after";
wantarray ? #result : $result[0];
}
Now, if your wrapper doesn't need to alter #_, and doesn't need to alter the return values, then Class::Method::Modifiers can make this a bit easier:
use Class::Method::Modifiers;
sub wrapper { orig(#_) } # do nothing in the wrapper itself
before wrapper => sub {
my ($first) = #_;
print "before. first argument: $first\n";
};
after wrapper => sub {
print "after";
};
I suspect there's a better way to do what you want in Perl, but since you specify only the mechanism I can tell you only how to make it work.
The return value of a Perl subroutine depends on the context of the call. Within the subroutine you can use the built-in operator wantarray to detect the context -- it will return a true value if the call was in list context, and false otherwise.
So to pass on the value that another subroutine would return in the same context, you would have to write wrapper like this
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
if (wantarray) {
my #result = orig(#_);
print "after";
return #result;
}
else {
my $result = orig(#_);
print "after";
return $result;
}
}
But bear in mind that a given subroutine is probably meant to be called in either scalar or list context. Writing something that behaves differently according to context is rare, so you would normally want only one of these two conditional branches depending on the behaviour of orig.
I am looking for the proper perl-ism for this issue. I can work around it but just have to ask...
I am using HTML::TreeBuilder and am using the look_down method. This returns an array or scalar depending on context and returns undef if no matching tag is found. Cool.
I want to do the following:
foreach my $tag ( #{ $head->look_down('_tag', 'link') } ) {
...
}
but if there is no link tag the function returns undef and generates the error Can't use an undefined value as an ARRAY reference at myCGI.cgi line ###. So I try this modification:
foreach my $tag ( #{ $head->look_down('_tag', 'link') || [] } ) {
...
}
My thought was that if the method returns undef then it will get changed into an empty array. This works when there are no link tags. But, if there is at least one expected tag then there is an error: Not an ARRAY reference at myCGI.cgi line ###.
Do I need to just bite the bullet and break the method call out of the loop and check for undef before entering the loop?
"returns an array" is sometimes mentioned in documentation but is incorrect; perl subroutines always return lists (though in scalar context it will be a list with only one element).
It does not return undef in list context, it returns an empty list (return with no arguments returns an empty list in list context and undef in scalar context). You can just loop over the returned values with no #{ } required:
foreach my $tag ( $head->look_down('_tag', 'link') ) {
...
}