Re: How to process named parameters that have the same key name?
by saintmike (Vicar) on Oct 17, 2005 at 23:46 UTC
|
Pass them as an array and transform it into a hash internally:
sub simple_call {
my ($self, @params) = @_;
my %params;
while(my($key, $value) = splice @params, 0, 2) {
push @{ $params{$key} }, $value;
}
}
Afterwards, every value in %params is a ref to an array, containing one or more elements. | [reply] [d/l] |
|
|
saintmike,
I have been meaning to reply to this node but I kept getting distracted.
Argument handling is one of those deceptively difficult things to do right. If you assume that your well documented API is being followed, you can probably get away with a few lines of code as you demonstrate here. The difficulty arises when you decide to handle input you don't expect. Imagine how your code would break if an uneven number of elements were passed. It can be easily remedied.
croak "Incorrect number of parameters" if @_ % 2;
Of course this is only one kind of issue that may need to be handled. There are a myriad of potential smoking guns.
- required, optional, and positional arguments
- arguments with a constraint on possible values
- argument a or b, but not both a & b can be present
- if argument a is present, argument b must also be present
- argument a's possible values depends on some other argument
- arguments that expect the value to be a specific data type
This is by far not an exhaustive list. As you can see, this can be a daunting task. That is one reason why there are a plethora of modules on CPAN. Another reason is there are many schools of thought on how to handle the issues.
There is nothing wrong with the solution you presented but it ends up producing the same underlying data structure that the Anonymous Monk indicates is undesired. I find it suspicious that there is no reason given why the obvious solution key => [$val1, $val2, $val3] is unwanted.
In any account, I wanted to take this opportunity to shed some light on the complexity of this seemingly simple problem.
| [reply] [d/l] [select] |
Re: How to process named parameters that have the same key name?
by pg (Canon) on Oct 18, 2005 at 00:17 UTC
|
"P.S. I don't want to do: insect => ['ant', 'spider']"
Depends on the specification. If duplication is not allowed, in which case, if there is duplication, we can just take the last one. Just like you said, the following code would be fine:
use Data::Dumper;
use strict;
use warnings;
foo(
animal => 'monkey',
fish => 'tuna',
insect => 'ant',
insect => 'spider'
);
sub foo {
my %params = @_;
print Dumper(\%params);
}
Which gives:
$VAR1 = {
'insect' => 'spider',
'animal' => 'monkey',
'fish' => 'tuna'
};
If duplication is allowed, then you are forced to use insect => ['ant', 'spider'], or something similar to represent this. Logically you cannot avoid this. saintmike has the code already. Whether you create this structure inside or outside the function/method, really doesn't matter, all depend on which way is easier for you, and probably make the code more reusable. | [reply] [d/l] [select] |
Re: How to process named parameters that have the same key name?
by NetWallah (Canon) on Oct 18, 2005 at 02:33 UTC
|
Based on saintmike's code - here is one way to move the "HoA when necessary" complexity to the CALLED subroutine:
use Data::Dumper;
use strict;
use warnings;
simple_call(
animal => 'monkey',
fish => 'tuna',
insect => 'ant',
insect => 'spider'
);
##########################
sub simple_call {
my %params;
while(my($key, $value) = splice @_, 0, 2) {
if (exists $params{$key}){
if (ref $params{$key} eq "ARRAY"){
push @{$params{$key}},$value;
}else{
$params{$key} = [$params{$key},$value];
}
}else{
$params{$key} = $value;
}
}
print Dumper \%params;
}
prints
$VAR1 = {
'insect' => [
'ant',
'spider'
],
'animal' => 'monkey',
'fish' => 'tuna'
};
Update:Corrected attribution to saintmike, and deleted unnecessary @params declaration.
"Man cannot live by bread alone...
He'd better have some goat cheese and wine to go with it!"
| [reply] [d/l] [select] |
|
|
$VAR1 = {
'insect' => [
'ant',
'spider'
],
'animal' => 'monkey',
'fish' => 'tuna'
};
If duplication is allowed, then it would be better to construct HoA, where values are always arrays to simplify further walking through such structure.
$VAR1 = {
'insect' => [
'ant',
'spider'
],
'animal' => ['monkey'],
'fish' => ['tuna']
};
| [reply] [d/l] [select] |
Re: How to process named parameters that have the same key name?
by Nkuvu (Priest) on Oct 18, 2005 at 00:31 UTC
|
Hashes can't have duplicate keys. If you assign to a pre-existing hash key, the old value is simply overwritten. Consider the following simplistic example:
my %foo = (
animal => 'monkey',
fish => 'tuna',
insect => 'ant',
insect => 'spider'
);
while (my ($key, $value) = each %foo) {
print "KEY: $key\n VALUE: $value\n";
}
This prints out the expected:
KEY: insect
VALUE: spider
KEY: animal
VALUE: monkey
KEY: fish
VALUE: tuna
Note that there is no ant in there. So I'm missing something obvious, or you'll be stomping on a value. | [reply] [d/l] [select] |
Re: How to process named parameters that have the same key name?
by ickyb0d (Monk) on Oct 18, 2005 at 01:06 UTC
|
what about if you just did something like this if you don't want to use arrays...
my $example = test::blah->new();
$example->simple_call(
'animal' => {
'rhino' => 1,
'hippo' => 1},
'fish' => {
'tuna' => 1},
'insect' => {
'ant' => 1,
'spider' => 1}
};
then just use they keys of 'animal' 'fish' and 'insect' to get the data out. isn't a spider an arachnid anyways? :) | [reply] [d/l] |
Re: How to process named parameters that have the same key name?
by jdporter (Paladin) on Oct 18, 2005 at 12:42 UTC
|
As AWTDI, I offer the following.
use Getopt::Long;
sub simple_call
{
my @params = qw( animal fish insect );
my $self = shift;
local @ARGV;
push @ARGV, '--'.shift, shift while @_;
GetOptions( \(my %opt), map $_.'=s@', @params );
# now use %opt. each value is an arrayref.
}
We're building the house of the future together.
| [reply] [d/l] |
Re: How to process named parameters that have the same key name?
by Moron (Curate) on Oct 18, 2005 at 15:09 UTC
|
It seems to me that in order to preserve the unique values of the duplicate keys, storing an array of such values inside the hash against its key should be a candidate solution: #!/usr/bin/perl
use Data::Dumper;
simple_call( uniq1 => 1, uniq2 => 2, dup1 => 3, dup1 => 4 );
sub simple_call {
my %hash = ();
for ( my ($key, $value); $key = shift; ) {
$value = shift;
if ( dupAllow( $key ) ) {
unless ( $hash{ $key } ) {
my @tmp = ();
$hash{ $key } = \@tmp;
}
my $aref = $hash{ $key };
push @$aref, $value;
}
else {
$hash{ $key } = $value;
}
}
print Dumper( \%hash );
# continue subroutine processing
}
sub dupAllow {
my $key = shift;
( $key eq 'dup1' ) and return 1;
( $key eq 'dup2' ) and return 1;
return 0;
}
Output:$VAR1 = {
'dup1' => [
3,
4
],
'uniq1' => 1,
'uniq2' => 2
};
| [reply] [d/l] [select] |
Re: How to process named parameters that have the same key name?
by cowboy (Friar) on Oct 18, 2005 at 21:48 UTC
|
| [reply] |