Perl function: accept hash, array, or scalar as parameter - perl

I have the following sub-routine.
sub Function{
my $ref = \($_[0]);
if(ref($ref) eq 'SCALAR'){
. . .
}
}
I am trying to get it to work such that passing a list, scalar or hash to it, it converts that into a reference and depending upon whether that reference is of type ARRAY, SCALAR or HASH, different actions are performed.
It could be called as
Function(%a)
Function($a)
Function(#a)
How can I make $ref be a reference to whatever is passed to the Function? My current approach isn't working.

Function(%a)
Function($a)
Function(#a)
You can't do what you are asking. There is no way inside Function to determine whether an array or a hash was passed, because, in fact, you can't pass an array or a hash to a sub in the first place. You can only pass a list; the hashes and arrays are converted to lists in the sub calls above.
You should pass a reference to the function in the first place:
Function(\%a)
Function($a)
Function(\#a)
Then you can check what it is easily:
sub Function {
my $param = shift;
if (not ref $param) {
...
} elsif (ref $param eq 'HASH') {
...
} elsif (ref $param eq 'ARRAY') {
...
}

You can use a prototyped function. Although Perl 5 prototypes are a total mess, they can be used quite well in this case. As I understand them the prototype
sub Function (\[$#%]) {...}
should solve your problem, and the reference be in $_[0].
The big disadvantage is that the variable you pass has to start with the $, #, or % character, so you can't call Function with a constant argument directly:
Function(1)
fails, but
my $temp = 1;
Function($temp)
works.
Your approach failes, because #_ is a list, and all elements are scalars.

Why not just check to see if the argument passed in is a reference and act accordingly?
sub gimme_a_ref
{
my $arg=shift;
if(ref $arg ne ref "")
{
return $arg;
}
else
{
return \$arg;
}
}

Related

Perl get parameter datatype

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.

HASH element from an object's return in a single string

I have a module Module.pm with function getHash() which returns, as you guessed, a hash of data)). We all know well how to get an element form that hash, using a standard model:
use Module;
my $m = new Module;
my %hash = $m->getHash();
print $hash{needed_element's_key};
everything is ok.
But what if you need to write two last strings in a single string WITHOUT initializing a new HASH (and occupying a memory for a whole hash, using just one element)?
Is it possible to do so somehow? Like, say, $m->getHash()->{needed_element's_key};
Of course, the given example does not work.
Any suggestions?
Thanks!
It's impossible to return a hash. Subs can only return a list of scalars. You are assigning that list of scalars to an existing hash my %hash and getting an element of that hash.
You could disguise the fact that you are creating a new hash by using an anonymous hash:
my $val = { $m->getKeyValPairs() }->{$key};
Alternatively, a custom filter could be more efficient:
sub find_val {
my $target_key = shift;
while (#_) {
my $key = shift;
my $val = shift;
return $val if $key eq $target_key;
}
return undef;
}
my $val = find_val $key, $m->getKeyValPairs();
But the best option would be to have the method return a reference to a hash instead. Then, you wouldn't have to perform the wasteful activity of creating a hash. You'd simply use the following:
$m->getHashRef()->{$key}
If a sub does return %hash, it actually returns a list of keys and values (alternating). You can put it back in a hash and get a specific key with:
print +{ $m->getHash() }->{needed_element_key};
(The leading + is needed to prevent perl from thinking the { is a block returning a filehandle to print to.)
But you are better off just returning a hash reference.

Properly forward return value in Perl

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.

What are the differences between parameter inputting mechanisms in Perl?

While reading a downloaded Perl module, I found several ways of defining input parameters, which are listed as following. What are the differences between them?
sub new{
my $class = shift;
my $self = {#_};
bless{$self, $class};
}
sub count1{
my ($self, $lab1) = #_;
}
sub new1{
my ($class, $lab1) = #_;
my $self = {};
bless $class, $self;
}
sub setpath{
my $self = shift;
}
When a subroutine is called, the passed parameters are put into a special array #_. One can consume this array by shifting values out my $foo = shift or by direct array assignment my ($foo,$bar)=#_; It is even possible to use the values directly from the array: $_[0]
Why one versus the others? Direct array assignment is the most standard and common. Sometimes the shift way is used when there are optional trailing values. Direct array usage is discouraged except in few small niches: wrapper functions that are calling other functions, especially inside of objects. functions that wrap other functions and and modify the inputs. Also the special form of goto &func which immediately drops the current call stack and calls func on the current value of #_.
# use shift for optional trailing values
use v5.10;
my $foo = shift;
my $bar = shift // 'default bar value';
my $baz = shift // 'default baz value';
#obj method to call related non-object function.
sub bar { my $self = shift; _bar(#_) }
sub longname { shortname(#_) }
sub get { return $_[0]->$_[1]; }
#1 and #3 are examples of associating an object with a class (Object Oriented Perl).
In #2, #_ is the list of parameters passed to the function, so $self and $lab1 get the values of the first 2 passed parameters.
In #4, shift() is a built in Perl subroutine that takes an array as an argument, then returns and deletes the first item in that array. If it has no argument, it is executed implicitly on #_. So $self gets the value of the first passed parameter.

How do I tell what type of value is in a Perl variable?

How do I tell what type of value is in a Perl variable?
$x might be a scalar, a ref to an array or a ref to a hash (or maybe other things).
ref():
Perl provides the ref() function so that you can check the reference type before dereferencing a reference...
By using the ref() function you can protect program code that dereferences variables from producing errors when the wrong type of reference is used...
$x is always a scalar. The hint is the sigil $: any variable (or dereferencing of some other type) starting with $ is a scalar. (See perldoc perldata for more about data types.)
A reference is just a particular type of scalar.
The built-in function ref will tell you what kind of reference it is. On the other hand, if you have a blessed reference, ref will only tell you the package name the reference was blessed into, not the actual core type of the data (blessed references can be hashrefs, arrayrefs or other things). You can use Scalar::Util 's reftype will tell you what type of reference it is:
use Scalar::Util qw(reftype);
my $x = bless {}, 'My::Foo';
my $y = { };
print "type of x: " . ref($x) . "\n";
print "type of y: " . ref($y) . "\n";
print "base type of x: " . reftype($x) . "\n";
print "base type of y: " . reftype($y) . "\n";
...produces the output:
type of x: My::Foo
type of y: HASH
base type of x: HASH
base type of y: HASH
For more information about the other types of references (e.g. coderef, arrayref etc), see this question: How can I get Perl's ref() function to return REF, IO, and LVALUE? and perldoc perlref.
Note: You should not use ref to implement code branches with a blessed object (e.g. $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";) -- if you need to make any decisions based on the type of a variable, use isa (i.e if ($a->isa("My::Foo") { ... or if ($a->can("foo") { ...). Also see polymorphism.
A scalar always holds a single element. Whatever is in a scalar variable is always a scalar. A reference is a scalar value.
If you want to know if it is a reference, you can use ref. If you want to know the reference type,
you can use the reftype routine from Scalar::Util.
If you want to know if it is an object, you can use the blessed routine from Scalar::Util. You should never care what the blessed package is, though. UNIVERSAL has some methods to tell you about an object: if you want to check that it has the method you want to call, use can; if you want to see that it inherits from something, use isa; and if you want to see it the object handles a role, use DOES.
If you want to know if that scalar is actually just acting like a scalar but tied to a class, try tied. If you get an object, continue your checks.
If you want to know if it looks like a number, you can use looks_like_number from Scalar::Util. If it doesn't look like a number and it's not a reference, it's a string. However, all simple values can be strings.
If you need to do something more fancy, you can use a module such as Params::Validate.
I like polymorphism instead of manually checking for something:
use MooseX::Declare;
class Foo {
use MooseX::MultiMethods;
multi method foo (ArrayRef $arg){ say "arg is an array" }
multi method foo (HashRef $arg) { say "arg is a hash" }
multi method foo (Any $arg) { say "arg is something else" }
}
Foo->new->foo([]); # arg is an array
Foo->new->foo(40); # arg is something else
This is much more powerful than manual checking, as you can reuse your "checks" like you would any other type constraint. That means when you want to handle arrays, hashes, and even numbers less than 42, you just write a constraint for "even numbers less than 42" and add a new multimethod for that case. The "calling code" is not affected.
Your type library:
package MyApp::Types;
use MooseX::Types -declare => ['EvenNumberLessThan42'];
use MooseX::Types::Moose qw(Num);
subtype EvenNumberLessThan42, as Num, where { $_ < 42 && $_ % 2 == 0 };
Then make Foo support this (in that class definition):
class Foo {
use MyApp::Types qw(EvenNumberLessThan42);
multi method foo (EvenNumberLessThan42 $arg) { say "arg is an even number less than 42" }
}
Then Foo->new->foo(40) prints arg is an even number less than 42 instead of arg is something else.
Maintainable.
At some point I read a reasonably convincing argument on Perlmonks that testing the type of a scalar with ref or reftype is a bad idea. I don't recall who put the idea forward, or the link. Sorry.
The point was that in Perl there are many mechanisms that make it possible to make a given scalar act like just about anything you want. If you tie a filehandle so that it acts like a hash, the testing with reftype will tell you that you have a filehanle. It won't tell you that you need to use it like a hash.
So, the argument went, it is better to use duck typing to find out what a variable is.
Instead of:
sub foo {
my $var = shift;
my $type = reftype $var;
my $result;
if( $type eq 'HASH' ) {
$result = $var->{foo};
}
elsif( $type eq 'ARRAY' ) {
$result = $var->[3];
}
else {
$result = 'foo';
}
return $result;
}
You should do something like this:
sub foo {
my $var = shift;
my $type = reftype $var;
my $result;
eval {
$result = $var->{foo};
1; # guarantee a true result if code works.
}
or eval {
$result = $var->[3];
1;
}
or do {
$result = 'foo';
}
return $result;
}
For the most part I don't actually do this, but in some cases I have. I'm still making my mind up as to when this approach is appropriate. I thought I'd throw the concept out for further discussion. I'd love to see comments.
Update
I realized I should put forward my thoughts on this approach.
This method has the advantage of handling anything you throw at it.
It has the disadvantage of being cumbersome, and somewhat strange. Stumbling upon this in some code would make me issue a big fat 'WTF'.
I like the idea of testing whether a scalar acts like a hash-ref, rather that whether it is a hash ref.
I don't like this implementation.