I try to use a (class) method in an Object for sorting object instances.
package Something;
use strict;
use warnings;
use Data::Dumper;
sub new {
my ($class, $date) = #_;
my $self = bless{}, $class;
$self->{date} = $date;
return $self;
}
sub _sort($$) {
print STDERR Dumper($_[0], $_[1]);
$_[0]->{date} cmp $_[1]->{date};
}
package SomethingTest;
use base 'Test::Class';
use Test::More;
__PACKAGE__->runtests() unless caller;
sub sorting : Test {
my $jan = Something->new("2016-01-01");
my $feb = Something->new("2016-02-01");
my $mar = Something->new("2016-03-01");
is_deeply(
sort Something::_sort [$feb, $mar, $jan],
[$jan, $feb, $mar]);
}
I've seen this snippet in perldoc -f sort, hence the prototype for _sort.
# using a prototype allows you to use any comparison subroutine
# as a sort subroutine (including other package's subroutines)
package other;
sub backwards ($$) { $_[1] cmp $_[0]; } # $a and $b are
# not set here
package main;
#new = sort other::backwards #old;
However, the dumped arguments look odd:
$VAR1 = [
bless( {
'date' => '2016-02-01'
}, 'Something' ),
bless( {
'date' => '2016-03-01'
}, 'Something' ),
bless( {
'date' => '2016-01-01'
}, 'Something' )
];
$VAR2 = [
$VAR1->[2],
$VAR1->[0],
$VAR1->[1]
];
and the test fails with
# Failed test 'sorting died (Not a HASH reference at sort.t line 16.)'
# at sort.t line 25.
Is this just my test setup or can't I have the same objects in these arrays?
What else am I missing?
Your problem isn't with the subroutine you pass to sort(), but in the arguments you pass to is_deeply(). The way you have written it parses like this, if we add some parentheses:
is_deeply(
sort(Something::_sort [$feb, $mar, $jan], [$jan, $feb, $mar] )
);
That is, you're telling sort() to act on a list consisting of two anonymous array references, and then is_deeply() to run with the single argument returned from sort (except it crashes before is_deeply() can try to run and complain that you gave it too few arguments to work with).
This is probably closer to what you intended:
is_deeply(
[sort(Something::_sort ($feb, $mar, $jan))],
[$jan, $feb, $mar]);
That is, tell is_deeply() to compare two anonymous arrays, the first of which is made from telling sort() to apply your sorting routine to the list ($feb, $mar, $jan).
Related
sub funcA{
my ($A) = #_; <--require 1
}
sub funcB{
my ($A, $B) = #_; <--require 2
}
sub funcC{
my ($A, $B, $C) = #_; <--require 3
}
sub funcRun{
my ($param) = #_;
my $other1, $other2, $other3; <--will be assigned at ',,,,,'
,,,,,
if($param eq 'a'){
funcA, $other1;
return;
}elsif($param eq = 'b'){
funcB, $other1, $other2;
return;
}elsif($param eq = 'c'){
funcC, $other1, $other2, $other3;
return;
}
}
I want to change this to next code
sub funcA{
my ($A) = #_; #<--require 1
}
sub funcB{
my ($A, $B) = #_; #<--require 2
}
sub funcC{
my ($A, $B, $C) = #_; #<--require 3
}
my $hash_ref = {
'a' => \&funcA,
'b' => \&funcB,
'c' => \&funcC
}
sub funcRun{
my ($param) = #_;
my $other1, $other2, $other3; #<--will be assigned at ',,,,,'
,,,,,
$hash_ref->{$param}(ARGUMENTS); #<--my problem
}
But I can't think how to make ARGUMENTS section including variable number of arguments.
I considered each function to be defined in the funcRun code, but then I don't know the difference with if/else code. And I heard that passing 3 arguments values and accepting parameters in order from first, is not good from someone
Updated following a clarification. The very last code segment may be exactly what is asked for.
The design where a parameter decides what argument set to use from a list generated at runtime is what is giving you trouble; it's complicated. Not knowing about the actual problem I don't know what else to offer though (other than guesses). Perhaps clarify the use of this?
Having said that, one way to complete what you want is to store a specification of what arguments go with a function along with the function name in the hashref; another would be to have a separate structure with argument sets for each parameter.
For example
use warnings;
use strict;
use feature 'say';
my $dispatch = {
'a' => { name => \&funcA, lastidx => 0 },
'b' => { name => \&funcB, lastidx => 1 },
'c' => { name => \&funcC, lastidx => 2 }
};
sub funcRun {
my ($param) = #_;
my #args = qw(one two three);
my $func = $dispatch->{$param}{name};
my $lastidx = $dispatch->{$param}{lastidx};
$func->( #args[0..$lastidx] );
}
sub funcA { say "#_" }
sub funcB { say "#_" }
sub funcC { say "#_" }
funcRun($_) for qw(a b c);
Prints
one
one two
one two three
If you really need to pick arguments positionally from a list then use an array.
However, I suggest that you clarify what this is for so that we can offer a simpler design.
Following an explanation in a comment, a property I thought was accidental may in fact help.
If the function funcA indeed takes only the first argument, funcB the first two and funcC all three (from a list built at runtime), then you can nicely pass all to all of them
$func->( #args );
sub funcA {
my ($A) = #_; # arguments other than the first are discarded
...
}
Each function takes what it needs and the rest of the arguments are discarded.
Further, if functions in any way know which of a given list of arguments to take then again you can simply pass all of them. Then they can pick their arguments either positionally
sub funcB {
my ($A, undef, $B) = #_; # if it needs first and third
...
}
or by a named key
# Work out what arguments are for which function
my $args = { for_A => ..., for_B => ..., for_C => ... };
...
$func->( $args );
sub funcA {
my ($args) = #_
my $A = $args->{for_A};
...
}
where now arguments need be stored in a hash.
Finally and best, this can all be worked out ahead of the call
my $dispatch = { a => \&funcA, b => \&funcB, c => \&funcC };
sub funcRun {
my ($param) = #_;
# Work out arguments for functions
my $args = { a => ..., b => ..., c => ... };
$dispatch->{$param}->( $args->{$param} );
}
# subs funcA, funcB, funcC are the same
funcRun($_) for qw(a b c);
what requires minimal changes to your code (just store arguments in a hash).
Here neither the functions nor the dispatch table need knowledge of the possible argument lists, what is all resolved in funcRun. This avoids entangling functions with outside code.
Your problem stems from the fact that you're passing in a selection of values from arbitrary, unrelated variables. The solution, therefore, is to put all the data you might want to pass to you subroutines in a single data structure and define a mechanism for extracting the correct data for each call. You already have a solution which uses an array for this, but I think it's slightly easier to understand in a hash.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
sub funcA{
my ($A) = #_;
say $A;
}
sub funcB{
my ($A, $B) = #_;
say "$A / $B";
}
sub funcC{
my ($A, $B, $C) = #_;
say "$A / $B / $C";
}
my $hash_ref = {
'a' => { func => \&funcA, args => [ qw[ other1 ] ] },
'b' => { func => \&funcB, args => [ qw[ other1 other2 ] ] },
'c' => { func => \&funcC, args => [ qw[ other1 other2 other3 ] ] },
};
sub funcRun{
my ($param) = #_;
my %args = (
other1 => 'foo',
other2 => 'bar',
other3 => 'baz',
);
# $hash_ref->{$param}{args} is an array reference containing
# the names of the arguments you need.
# #{ ... } turns that array reference into an array.
# #args{ ... } uses that array to look up those keys in the
# %args hash (this is called a hash slice)
$hash_ref->{$param}{func}(#args{ #{ $hash_ref->{$param}{args} } });
}
funcRun($_) for qw[a b c];
But, to be honest, having stored your data in a hash, it's only a small step to passing the whole hash into every subroutine and letting them determine which data they want to use. Or even turning your hash into an object.
I have a hash with a few values that are not scalar data but rather anonymous subroutines that return scalar data. I want to make this completely transparent to the part of the code that looks up values in the hash, so that it doesn't have to be aware that some of the hash values may be anonymous subroutines that return scalar data rather than just plain old scalar data.
To that effect, is there any way to have the anonymous subroutines executed when their keys are accessed, without using any special syntax? Here's a simplified example that illustrates the goal and the problem:
#!/usr/bin/perl
my %hash = (
key1 => "value1",
key2 => sub {
return "value2"; # In the real code, this value can differ
},
);
foreach my $key (sort keys %hash) {
print $hash{$key} . "\n";
}
The output I would like is:
perl ./test.pl
value1
value2
Instead, this is what I get:
perl ./test.pl
value1
CODE(0x7fb30282cfe0)
As noted by Oleg, it's possible to do this using various more or less arcane tricks like tie, overloading or magic variables. However, this would be both needlessly complicated and pointlessly obfuscated. As cool as such tricks are, using them in real code would be a mistake at least 99% of the time.
In practice, the simplest and cleanest solution is probably to write a helper subroutine that takes a scalar and, if it's a code reference, executes it and returns the result:
sub evaluate {
my $val = shift;
return $val->() if ref($val) eq 'CODE';
return $val; # otherwise
}
and use it like this:
foreach my $key (sort keys %hash) {
print evaluate($hash{$key}) . "\n";
}
I don't believe that the words that others have written in disapproval of the tie mechanism are warranted. None of the authors seem to properly understand how it works and what core library backup is available
Here's a tie example based on Tie::StdHash
If you tie a hash to the Tie::StdHash class then it works exactly as a normal hash. That means there's nothing left to write except for methods that you may want to override
In this case I've overridden TIEHASH so that I could specify the initialisation list in the same statement as the tie command, and FETCH, which calls the superclass's FETCH and then makes a call to it if it happens to be a subroutine reference
Your tied hash will work as normal except for the change that you have asked for. I hope it is obvious that there is no longer a direct way to retrieve a subroutine reference if you have stored it as a hash value. Such a value will always be replaced by the result of calling it without any parameters
SpecialHash.pm
package SpecialHash;
use Tie::Hash;
use base 'Tie::StdHash';
sub TIEHASH {
my $class = shift;
bless { #_ }, $class;
}
sub FETCH {
my $self = shift;
my $val = $self->SUPER::FETCH(#_);
ref $val eq 'CODE' ? $val->() : $val;
}
1;
main.pl
use strict;
use warnings 'all';
use SpecialHash;
tie my %hash, SpecialHash => (
key1 => "value1",
key2 => sub {
return "value2"; # In the real code, this value can differ
},
);
print "$hash{$_}\n" for sort keys %hash;
output
value1
value2
Update
It sounds like your real situation is with an existing hash that looks something like this
my %hash = (
a => {
key_a1 => 'value_a1',
key_a2 => sub { 'value_a2' },
},
b => {
key_b1 => sub { 'value_b1' },
key_b2 => 'value_b2',
},
);
Using tie on already-populated variables isn't so neat as tying then at the point of declaration and then inserting the values as the data must be copied to the tied object. However the way I have written the TIEHASH method in the SpecialHash class makes this simple to do in the tie statement
If possible, it would be much better to tie each hash before you put data into it and add it to the primary hash
This program ties every value of %hash that happens to be a hash reference. The core of this is the statement
tie %$val, SpecialHash => ( %$val )
which functions identically to
tie my %hash, SpecialHash => ( ... )
in the previous code but dereferences $val to make the syntax valid, and also uses the current contents of the hash as the initialisation data for the tied hash. That is how the data gets copied
After that there is just a couple of nested loops that dump the whole of %hash to verify that the ties are working
use strict;
use warnings 'all';
use SpecialHash;
my %hash = (
a => {
key_a1 => 'value_a1',
key_a2 => sub { 'value_a2' },
},
b => {
key_b1 => sub { 'value_b1' },
key_b2 => 'value_b2',
},
);
# Tie all the secondary hashes that are hash references
#
for my $val ( values %hash ) {
tie %$val, SpecialHash => ( %$val ) if ref $val eq 'HASH';
}
# Dump all the elements of the second-level hashes
#
for my $k ( sort keys %hash ) {
my $v = $hash{$k};
next unless ref $v eq 'HASH';
print "$k =>\n";
for my $kk ( sort keys %$v ) {
my $vv = $v->{$kk};
print " $kk => $v->{$kk}\n"
}
}
output
a =>
key_a1 => value_a1
key_a2 => value_a2
b =>
key_b1 => value_b1
key_b2 => value_b2
There's a feature called "magic" that allows code to be called when variables are accessed.
Adding magic to a variable greatly slows down access to that variable, but some are more expensive than others.
There's no need to make access to every element of the hash magical, just some values.
tie is an more expensive form of magic, and it's not needed here.
As such, the most efficient solution is the following:
use Time::HiRes qw( time );
use Variable::Magic qw( cast wizard );
{
my $wiz = wizard(
data => sub { my $code = $_[1]; $code },
get => sub { ${ $_[0] } = $_[1]->(); },
);
sub make_evaluator { cast($_[0], $wiz, $_[1]) }
}
my %hash;
$hash{key1} = 'value1';
make_evaluator($hash{key2}, sub { 'value2#'.time });
print("$hash{$_}\n") for qw( key1 key2 key2 );
Output:
value1
value2#1462548850.76715
value2#1462548850.76721
Other examples:
my %hash; make_evaluator($hash{key}, sub { ... });
my $hash; make_evaluator($hash->{$key}, sub { ... });
my $x; make_evaluator($x, sub { ... });
make_evaluator(my $x, sub { ... });
make_evaluator(..., sub { ... });
make_evaluator(..., \&some_sub);
You can also "fix up" an existing hash. In your hash-of-hashes scenario,
my $hoh = {
{
key1 => 'value1',
key2 => sub { ... },
...
},
...
);
for my $h (values(%$hoh)) {
for my $v (values(%$h)) {
if (ref($v) eq 'CODE') {
make_evaluator($v, $v);
}
}
}
Yes you can. You can either tie hash to implementation that will resolve coderefs to their return values or you can use blessed scalars as values with overloaded mehods for stringification, numification and whatever else context you want to resolve automatically.
One of perl's special features for just such a use case is tie. This allows you to attach object oriented style methods, to a scalar or hash.
It should be used with caution, because it can mean that your code is doing really strange things, in unexpected ways.
But as an example:
#!/usr/bin/env perl
package RandomScalar;
my $random_range = 10;
sub TIESCALAR {
my ( $class, $range ) = #_;
my $value = 0;
bless \$value, $class;
}
sub FETCH {
my ($self) = #_;
return rand($random_range);
}
sub STORE {
my ( $self, $range ) = #_;
$random_range = $range;
}
package main;
use strict;
use warnings;
tie my $random_var, 'RandomScalar', 5;
for ( 1 .. 10 ) {
print $random_var, "\n";
}
$random_var = 100;
for ( 1 .. 10 ) {
print $random_var, "\n";
}
As you can see - this lets you take an 'ordinary' scalar, and do fruity things with it. You can use a very similar mechanism with a hash - an example might be to do database lookups.
However, you also need to be quite cautious - because you're creating action at a distance by doing so. Future maintenance programmers might well not expect your $random_var to actually change each time you run it, and a value assignment to not actually 'set'.
It can be really useful for e.g. testing though, which is why I give an example.
In your example - you could potentially 'tie' the hash:
#!/usr/bin/env perl
package MagicHash;
sub TIEHASH {
my ($class) = #_;
my $self = {};
return bless $self, $class;
}
sub FETCH {
my ( $self, $key ) = #_;
if ( ref( $self->{$key} ) eq 'CODE' ) {
return $self->{$key}->();
}
else {
return $self->{$key};
}
}
sub STORE {
my ( $self, $key, $value ) = #_;
$self->{$key} = $value;
}
sub CLEAR {
my ($self) = #_;
$self = {};
}
sub FIRSTKEY {
my ($self) = #_;
my $null = keys %$self; #reset iterator
return each %$self;
}
sub NEXTKEY {
my ($self) = #_;
return each %$self;
}
package main;
use strict;
use warnings;
use Data::Dumper;
tie my %magic_hash, 'MagicHash';
%magic_hash = (
key1 => 2,
key2 => sub { return "beefcake" },
);
$magic_hash{random} = sub { return rand 10 };
foreach my $key ( keys %magic_hash ) {
print "$key => $magic_hash{$key}\n";
}
foreach my $key ( keys %magic_hash ) {
print "$key => $magic_hash{$key}\n";
}
foreach my $key ( keys %magic_hash ) {
print "$key => $magic_hash{$key}\n";
}
This is slightly less evil, because future maintenance programmers can use your 'hash' normally. But dynamic eval can shoot the unwary in the foot, so still - caution is advised.
And alternative is to do it 'proper' object oriented - create a 'storage object' that's ... basically like the above - only it creates an object, rather than using tie. This should be much clearer for long term usage, because you won't get unexpected behaviour. (It's an object doing magic, which is normal, not a hash that 'works funny').
You need to identify when a code ref is present, then execute it as an actual call:
foreach my $key (sort keys %hash) {
if (ref $hash{$key} eq 'CODE'){
print $hash{$key}->() . "\n";
}
else {
print "$hash{$key}\n";
}
}
Note that you may consider making all of the hash values subs (a true dispatch table) instead of having some that return non-coderefs and some that return refs.
However, if you define the hash as such, you don't have to do any special trickery when it comes time to use the hash. It calls the sub and returns the value directly when the key is looked up.
key2 => sub {
return "value2";
}->(),
No, not without some ancillary code. You are asking for a simple scalar value and a code reference to behave in the same way. The code that would do that is far from simple and also injects complexity between your hash and its use. You might find the following approach simpler and cleaner.
You can make all values code references, making the hash a dispatch table, for uniform invocation
my %hash = (
key1 => sub { return "value1" },
key2 => sub {
# carry on some processing ...
return "value2"; # In the real code, this value can differ
},
);
print $hash{$_}->() . "\n" for sort keys %hash;
But of course there is a minimal overhead to this approach.
In this answer I found a recommendation for a simple TO_JSON method, which is needed for serializing blessed objects to JSON.
sub TO_JSON { return { %{ shift() } }; }
Could anybody please explain in detail how it works?
I changed it to:
sub TO_JSON {
my $self = shift; # the object itself – blessed ref
print STDERR Dumper $self;
my %h = %{ $self }; # Somehow unblesses $self. WHY???
print STDERR Dumper \%h; # same as $self, only unblessed
return { %h }; # Returns a hashref that contains a hash.
#return \%h; # Why not this? Works too…
}
Many questions… :( Simply, I’m unable to understand 3-liner Perl code. ;(
I need the TO_JSON but it will filter out:
unwanted attributes and
unset attributes too (e.g. for those the has_${attr} predicate returns false)
This is my code – it works but I really don't understand why the unblessing works…
use 5.010;
use warnings;
use Data::Dumper;
package Some;
use Moo;
has $_ => ( is => 'rw', predicate => 1,) for (qw(a1 a2 nn xx));
sub TO_JSON {
my $self = shift;
my $href;
$href->{$_} = $self->$_ for( grep {!/xx/} keys %$self );
# Same mysterious unblessing. The `keys` automagically filters out
# “unset” attributes without the need of call of the has_${attr}
# predicate… WHY?
return $href;
}
package main;
use JSON;
use Data::Dumper;
my #objs = map { Some->new(a1 => "a1-$_", a2 => "a2-$_", xx=>"xx-$_") } (1..2);
my $data = {arr => \#objs};
#say Dumper $data;
say JSON->new->allow_blessed->convert_blessed->utf8->pretty->encode($data);
EDIT: To clarify the questions:
The %{ $hRef } derefences the $hRef (getting the hash pointed to by the reference), but why get a plain hash from a blessed object reference $self?
In other words, why the $self is a hashref?
I tried to make a hash slice like #{$self}{ grep {!/xx/} keys %$self} but it didn't work. Therefore I created that horrible TO_JSON.
If the $self is a hashref, why the keys %$self returns only attributes having a value, and not all declared attributes (e.g. the nn too – see the has)?
sub TO_JSON { return { %{ shift() } }; }
| | |
| | L_ 1. pull first parameter from `#_`
| | (hashref/blessed or not)
| |
| L____ 2. dereference hash (returns key/value list)
|
L______ 3. return hashref assembled out of list
In your TO_JSON() function { %h } returns a shallow hash copy, while \%h returns a reference to %h (no copying).
Perl implemented object orientation by simply making it possible for a reference to know which package it came from (with bless). Knowing that a reference came from the Foo package means that methods are really functions defined in that package.
Perl allows any kind of reference to get blessed; not just hash references. It's very common to bless hash references; a lot of documentation shows doing exactly that; and Moose does it; but, it's possible to bless an array reference, or a subroutine reference, or a filehandle, or a reference to a scalar. The syntax %{$self} only works on hash references (blessed or not). It takes the hash reference, and dereferences it as a hash. The fact that the original reference may have been blessed is lost.
I need the TO_JSON but what will filter out:
unwanted attributes
and unset attributes too (e.g. for those the has_${attr} predicate returns false.
Pre-5.20, hash slices only give you the values and not the keys from the original hash. You want both keys and values.
Assuming you have a hash, and want to filter out undef values and keys not on a whitelist, there are a few options. Here's what I have, using the JSON module:
use strict; # well, I used "use v5.18", but I don't know which version of Perl you're using
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_, 1 } qw{foo bar};
my %bar = map { $_ => $foo->{$_} }
grep { defined $foo->{$_} && exists $whitelist{$_} }
keys %$foo;
print to_json(\%bar) . "\n";
# well, I used say() instead of print(), but I don't know which version of Perl you're using
The maps and greps aren't necessarily pretty, but it's the simplest way I could think of to filter out keys not on the whitelist and elements without an undef value.
You could use an array slice:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my #whitelist = qw{foo bar};
my %filtered_on_keys;
#filtered_on_keys{#whitelist} = #$foo{#whitelist};
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";
Or if you like loops:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_ => 1 } qw{foo bar};
my %bar;
while (my ($key, $value) = each %$foo) {
if (defined $value && exists $whitelist{$key}) {
$bar{$key} = $value;
}
}
print to_json(\%bar) . "\n";
It seems like a good time to bring up Larry wall's quote, "Perl is designed to give you several ways to do anything, so consider picking the most readable one."
However, I made a big point that not all objects are hashes. The appropriate way to get data from an object is through its getter functions:
use strict;
use warnings;
use JSON;
my $foo = Foo->new({ foo => undef, bar => 'baz', quux => 5 }); # as an example
my %filtered_on_keys;
#filtered_on_keys{qw{foo bar}} = ($foo->get_foo(), $foo->get_bar());
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";
I am facing compilation error while declaring array type argument in perl subroutine defination.
My complete code is below:
use Data::Dumper;
use Win32;
use Win32::Service;
use strict;
use warnings;
my #Services = qw(NNMAction RpcEptMapper smstsmgr SNMPTRAP);
my $server = 'nnmi.hclt.corp.hcl.in';
ServiceStatus($server , #Services);
sub ServiceStatus ($serverName,#serverServices)
{ my %statcodeHash = ( '1' => 'stopped',
'2' => 'start pending',
'3' => 'stop pending',
'4' => 'running',
'5' => 'continue pending',
'6' => 'pause pending',
'7' => 'paused' );
foreach my $serv (#serverServices)
{ my %status;
my $ret = Win32::Service::GetStatus($serverName , $serv , \%status);
if ($ret)
{ print "success \t$statcodeHash{$status{CurrentState}} \t$serv\n";
}
else
{ print Win32::FormatMessage(Win32::GetLastError()), "\n";
}
}
}
COMPILATION ERROR
>perl -w perl_RemoteServiceStatus.pl
Prototype after '#' for main::ServiceStatus : $serverName,#serverServices at per
l_RemoteServiceStatus.pl line 21.
Illegal character in prototype for main::ServiceStatus : $serverName,#serverServ
ices at perl_RemoteServiceStatus.pl line 21.
main::ServiceStatus() called too early to check prototype at perl_RemoteServiceS
tatus.pl line 16.
Global symbol "#serverServices" requires explicit package name at perl_RemoteSer
viceStatus.pl line 31.
Global symbol "$serverName" requires explicit package name at perl_RemoteService
Status.pl line 33.
Execution of perl_RemoteServiceStatus.pl aborted due to compilation errors.
Kindly help me debugging ths code. I am sure it would be a piece of cake for some.
It is really simple: Do not use prototypes if you do not know how they work. To get your code running, change the subroutine declaration from:
sub ServiceStatus ($serverName,#serverServices)
{ #...
to:
sub ServiceStatus {
my ($serverName, #serverServices) = #_;
edit: If you need to pass more than one array/hash to a subroutine, or an array/hash should be passed-in before some other value(s), you have to pass it by reference:
sub complex_params {
my ($array1, $scalar, $hash, $array2) = #_;
# dereference
my #a1 = #$array1;
my #a2 = #$array2;
my %h = %$hash;
#...
}
# reference
complex_params(\#some_array, $some_scalar, \%some_hash, \#other_array);
Perl prototypes are not for naming parameters, or even for giving types to them, they are for creating evaluation contexts. You need to modify the subroutines like this:
sub ServiceStatus ($#){
my ($serverName,#serverServices) = #_;
# ...
}
or totally get rid of the prototype:
sub ServiceStatus {
my ($serverName,#serverServices) = #_;
# ...
}
sub ServiceStatus
{
my ($serverName,#serverServices) = #_; # Declare variables and populate from #_, the parameter list.
...
}
what are you doing?
First! Don't try to use prototypes:
sub ServiceStatus($#){
}
Let's see, what you want:
Passing array or hash to function is a very old trick:
sub ServiceStatus{
my ($firstArgument, $refToSecondArgumentWhichIsArray) = #_;
#return undef unless defined($firstArgument&&$refToSecondArgumentWhichIsArray);
...
}
How to use this?
ServiceStatus($serverName, \#serverServices);
And what to do with refs?
$refToArray->[0]; <- first element of array you pass
#{$refToArray} <- array you pass to function
Need help figuring out how to do this. My code:
my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};
&printInfo(%hash);
sub printInfo{
my (%hash) = %_;
foreach my $key (keys %_{
my $a = $_{$key}{'Make'};
my $b = $_{$key}{'Color'};
print "$a $b\n";
}
}
The easy way, which may lead to problems when the code evolves, is simply by assigning the default array #_ (which contains all key-value-pairs as an even list) to the %hash which then rebuilds accordingliy. So your code would look like this:
sub printInfo {
my %hash = #_;
...
}
The better way would be to pass the hash as reference to the subroutine. This way you could still pass more parameters to your subroutine.
printInfo(\%hash);
sub PrintInfo {
my %hash = %{$_[0]};
...
}
An introduction to using references in Perl can be found in the perlreftut
You're so very, very close. There is no %_ for passing hashes, it must be passed in #_. Luckily, Hashes are assigned using a list context, so
sub printInfo {
my %hash = #_;
...
}
will make it work!
Also note, using the & in front of the subroutine call has been, in most cases, unnecessary since at least Perl 5.000. You can call Perl subroutines just like in other languages these days, with just the name and arguments. (As #mob points out in the comments, there are some instances where this is still necessary; see perlsub to understand this more, if interested.)
The best way to pass hashes and arrays is by reference. A reference is simply a way to talk about a complex data structure as a single data point -- something that can be stored in a scalar variable (like $foo).
Read up on references, so you understand how to create a reference and dereference a reference in order to get your original data back.
The very basics: You precede your data structure with a backslash to get the reference to that structure.
my $hash_ref = \%hash;
my $array_ref = \#array;
my $scalar_ref = \$scalar; #Legal, but doesn't do much for you...
A reference is a memory location of the original structure (plus a clue about the structure):
print "$hash_ref\n";
Will print something like:
HASH(0x7f9b0a843708)
To get the reference back into a useable format, you simply put the reference into the correct sigil in front:
my %new_hash = %{ $hash_ref };
You should learn about using references since this is the way you can create extremely complex data structures in Perl, and how Object Oriented Perl works.
Let's say you want to pass three hashes to your subroutine. Here are the three hashes:
my %hash1 = ( this => 1, that => 2, the => 3, other => 4 );
my %hash2 = ( tom => 10, dick => 20, harry => 30 );
my %hash3 = ( no => 100, man => 200, is => 300, an => 400, island => 500 );
I'll create the references for them
my $hash_ref1 = \%hash1;
my $hash_ref2 = \%hash2;
my $hash_ref3 = \%hash3;
And now just pass the references:
mysub ( $hash_ref1, $hash_ref2, $hash_ref3 );
The references are scalar data, so there's no problem passing them to my subroutine:
sub mysub {
my $sub_hash_ref1 = shift;
my $sub_hash_ref2 = shift;
my $sub_hash_ref3 = shift;
Now, I just dereference them, and my subroutine can use them.
my %sub_hash1 = %{ $sub_hash_ref1 };
my %sub_hash2 = %{ $sub_hash_ref2 };
my %sub_hash3 = %{ $sub_hash_ref3 };
You can see what a reference is a reference to by using the ref command:
my $ref_type = ref $sub_hash_ref; # $ref_type is now equal to "HASH"
This is useful if you want to make sure you're being passed the correct type of data structure.
sub mysub {
my $hash_ref = shift;
if ( ref $hash_ref ne "HASH" ) {
croak qq(You need to pass in a hash reference);
}
Also note that these are memory references, so modifying the reference will modify the original hash:
my %hash = (this => 1, is => 2, a => 3 test => 4);
print "$hash{test}\n"; # Printing "4" as expected
sub mysub ( \%hash ); # Passing the reference
print "$hash{test}\n"; # This is printing "foo". See subroutine:
sub mysub {
my $hash_ref = shift;
$hash_ref->{test} = "foo"; This is modifying the original hash!
}
This can be good -- it allows you to modify data passed to the subroutine, or bad -- it allows you to unintentionally modify data passed to the original subroutine.
I believe you want
my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};
printInfo(%hash);
sub printInfo{
my %hash = #_;
foreach my $key (keys %hash){
my $a = $hash{$key}{'Make'};
my $b = $hash{$key}{'Color'};
print "$a $b\n";
}
}
In the line printInfo(%hash) the %hash is expanded to a list with the alternating key-value pairs.
In printInfo, the #_ is this list that, and assigned to %hash it creates again the keys with their corresponding value from the alternating elements in the list.
You can pass them as
The argument list do_hash_thing( %hash )
A reference to the hash in the argument list
`do_hash_thing( #args_before, \%hash, #args_after )
As a reference by prototype, working like keys and other hash operators.
The list works like so:
sub do_hash_thing {
my %hash = #_;
...
}
do_hash_thing( %hash );
This also allows you to "stream" hash arguments as well:
do_hash_thing( %hash_1, %hash_2, parameter => 'green', other => 'pair' );
By reference works like this:
sub do_hash_thing {
my $hash_ref = shift;
...
}
do_hash_thing( \%hash, #other_args );
Here by prototype (\%#). The prototype makes perl look for a hash in the first argument and pass it by reference.
sub do_hash_thing (\%#) {
my $hash_ref = shift;
...
}
do_hash_thing( %hash => qw(other args) );
# OR
do_hash_thing %hash => qw(other args);
Caveat: prototypes don't work on methods.