robinbowes has asked for the wisdom of the Perl Monks concerning the following question:
Hi,
I am successfully using the following data structures:
# List all packet types that this script knows about
my %Idents = (
Rotation_Marker => 0x90,
Format_7 => 0x87,
);
# List packet lengths of each of the packet types (Ident char to check
+sum, inclusive)
my %Packet_Length = (
$Idents{ Rotation_Marker } => 30,
$Idents{ Format_7 } => 28,
);
# unpack templates to use for each packet type (not including the pack
+et Ident)
my %packet_template = (
$Idents{ Rotation_Marker } => "C3 n n n n n n n n n n n n C3",
$Idents{ Format_7 } => "C B16 n2 A4 A8 A2 A A A3 C2",
);
So far so good. I can read an ident from a file and then use construct like $packet_template{ $ident }
I am also using the following data structures:
my @packet_fields = qw( ident
flags
x
y
mode3
callsign
route
spare1
spare2
flight_level
terminator
chksum
);
my @rotation_packet_fields = qw(ident
RDP_Ident
Radar_Site_Ident
site_x
site_y
Useful_Vol_Upper
Useful_Vol_Lower
Useful_Vol_North
Useful_Vol_South
Useful_Vol_East
Useful_Vol_West
Rotation
Rotation_x
Rotation_y
Reserved
QNH_Datum
terminator
chksum
);
I use these as follows:
if ($ident == $Idents{ Rotation_Marker }) {
@packet{ @rotation_packet_fields } = unpack (
$packet_template{ $ident }, $buffer);
...
if ($ident == $Idents{ Format_7 }) {
@packet{ @packet_fields } = unpack (
$packet_template{ $ident }, $buffer);
Now, what I'd like to do is to create a hash of these arrays and refer to them in a similar fashion to the way I refer to the %packet_template hash. However, I can't work out how to do it. I've come up with the following declaration to create the hash, but I don't know how to re-reference it:
my %packet_fields = (
$Idents{ Format_7 } => qw( ident
flags
x
y
mode3
callsign
route
spare1
spare2
flight_level
terminator
chksum
),
$Idents{ Format_7 } => qw(ident
RDP_Ident
Radar_Site_Ident
site_x
site_y
Useful_Vol_Upper
Useful_Vol_Lower
Useful_Vol_North
Useful_Vol_South
Useful_Vol_East
Useful_Vol_West
Rotation
Rotation_x
Rotation_y
Reserved
QNH_Datum
terminator
chksum
),
);
Is this correct?
How do I use it in my code, e.g. this doesn't work:
@packet{$%packet_fields{ $ident} } = unpack (
$packet_template{ $ident }, $buffer);
What should I use?
Thanks,
R.
--
Robin Bowes | http://robinbowes.com
Re: Help with hash/array data structure
by princepawn (Parson) on Nov 11, 2003 at 17:24 UTC
|
This
my %packet_fields = (
$Idents{ Format_7 } => qw( ident
flags
x
...
Should be:
my %packet_fields = (
$Idents{ Format_7 } => [qw( ident
flags
x
...]
Likewise, this:
$Idents{ Format_7 } => qw(ident
RDP_Ident
Radar_Site_Ident
site_x
Should be this:
$Idents{ Format_7 } => [qw(ident
RDP_Ident
Radar_Site_Ident
site_x
... ]
The value of a key must be a scalar or *reference* to an array or hash, not an actual array or hash, as you had in your code.
Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality
| [reply] [d/l] [select] |
Re: Help with hash/array data structure
by injunjoel (Priest) on Nov 11, 2003 at 18:39 UTC
|
Greetings all,
Nested data structures will most definitely make you life easier... I suggest getting the book Advanced Perl Programming and reading the chapter on complex data structures. Also have a look at this node for a nutshell version of nested data types.
All that being said the one helpful (hopefully) piece of advice I would give you in working with this area of perl is, Always check your data. Personally I use Dumpvalue others around here tout Data::Dumper, the choice is yours.
Here is how I check my stuff.
use strict; #Always
use Dumpvalue;
###just an example of nested data
my @example = (
{'firstname'=>'Justin', 'lastname'=>'Tyme', 'Age'=>'21'},
{'firstname'=>'Ima', 'lastname'=>'Geek','Age'=>'21'}
);
dump_ref(\@example);###you need to pass a reference to your data since
+ the dumpValues method only takes references.
sub dump_ref
{
my $ref = shift;
my $dumper = new Dumpvalue;
$dumper->dumpValues($ref);
}
___OUTPUT___
0 ARRAY(0x80628f0)
0 HASH(0x804c8d4)
'Age' => 21
'firstname' => 'Justin'
'lastname' => 'Tyme'
1 HASH(0x80628b4)
'Age' => 21
'firstname' => 'Ima'
'lastname' => 'Geek'
So that gets you some of the way there as far as creating data and seeing what it is actually doing.
Now getting it back out! This is where I got the most tripped up.
Just keep in mind that you must reference (or dereference in this case) the data in the form you will eventually be useing it.
So, if you want to get the firstname of the person in the second element of the array above it would be:
$example[1]->{firstname};
or if you wanted to get all the keys for the hash in the first element of the array you would use:
my @first_elm_keys = keys %{$example[0]};
Of course like with anything in perl there is more than one way to do it. I personally like the arrow dereferencing scheme. It just makes more sense to me, looks like its pointing to the data.
Hope that helps
-injunjoel | [reply] [d/l] [select] |
Re: Help with hash/array data structure
by shockme (Chaplain) on Nov 11, 2003 at 17:23 UTC
|
Unless I've misread the above, you're wanting to store the array as a hash element. If so, the following should illustrate one way to do it:
my @array = qw (ident
RDP_Ident
Radar_Site_Ident
site_x
site_y
Useful_Vol_Upper
Useful_Vol_Lower
Useful_Vol_North
Useful_Vol_South
Useful_Vol_East
Useful_Vol_West
Rotation
Rotation_x
Rotation_y
Reserved
QNH_Datum
terminator
chksum
);
my %Idents;
$Idents{ Format_7 } = \@array;
foreach my $element (@{$Idents{ Format_7 }})
{
print "$element\n";
}
If things get any worse, I'll have to ask you to stop helping me.
| [reply] [d/l] |
Re: Help with hash/array data structure
by duff (Parson) on Nov 11, 2003 at 18:17 UTC
|
Here's some syntax that might start you off:
$hash{$key} = [ qw(foo bar baz) ]; # The [ ] make an anonymous array
+ reference
print "@{$hash{$key}}\n"; # output the whole array
print $hash{$key}->[0], "\n"; # output an individual element of
+ an array
See perlref, perldsc, and perllol for more info.
| [reply] [d/l] |
Re: Help with hash/array data structure
by Roy Johnson (Monsignor) on Nov 11, 2003 at 17:45 UTC
|
You've muddled your thinking about references and dereferencing.
my %packet_fields = (
$Idents{ Format_7 } => qw( ident
flags
Here, you're trying to use an array (qw(...)) where you can only put a scalar. What you want is an array reference:[qw(...)]
I'm not entirely clear on what you're hoping for here:
$%packet_fields{ $ident}
, but you can bet that $% is not going to be a useful combination of sigils. It's probably parsed as a special variable followed by a bareword and panic.
When in doubt, use the general form of dereferencing, which is the sigil you would use if you had an ordinary variable, then a curly-wrapped expression returning a rereference, and then any indexing you would use on an ordinary variable of the referenced type. You can nest these as deeply as you need to.
Check out perldoc perlreftut again.
| [reply] [d/l] [select] |
Re: Help with hash/array data structure
by Art_XIV (Hermit) on Nov 11, 2003 at 18:48 UTC
|
References are a big spicy meatball! And, of course, there's more than one way to do it.
The following snippet might help:
use strict;
my %packet_fields = (
'Format_X' => ['ident', 'flags', 'x', 'y', 'mode3'],
'Format_Y' => ['ident', 'RDP_Ident', 'Radar_Site_Ident', 'site_x']
);
foreach my $format (keys %packet_fields) {
foreach my $field (@{$packet_fields{$format}}) { #line 9
print "$format -> $field\n";
}
}
foreach my $format (keys %packet_fields) {
print "$format's 2nd field = ", $packet_fields{$format}->[1], "\n";
+#line 15
print "$format's 3rd field = ", $packet_fields{$format}[2], "\n"; #l
+ine 16
}
In this sample, %packet_fields is a regular old hash. The keys for this hash are garden-variety scalars. The value associated w/ each key is a reference to an anonymous (unnamed) array. These two anon. arrays are the only references that we have to deal with, and the only things that require any dereferencing syntax.
The @{} in line 9 is using a form of dereferencing notation to give me an array.
There's another form of dereferencing that I can use, too, as seen in lines 15 and 16.
Hanlon's Razor - "Never attribute to malice that which can be adequately explained by stupidity"
| [reply] [d/l] |
|
Hi,
Thanks for all the replies - most informative!
I've now got working code, something like this:
# Declaration of the data structure
my %packet_fields = (
$Idents{ Format_7 } => [qw( ident
x
y
flight_level
chksum
)],
$Idents{ Rotation_Marker } => [qw( ident
site_x
site_y
chksum
)],
);
# This structure listed for completeness of this example!
my %packet_template = (
$Idents{ Rotation_Marker } => "C3 n n n n n n n n n n n n C3",
$Idents{ Format_7 } => "C B16 n2 A4 A8 A2 A A A3 C2
+",
);
# Referencing the data structure
# code here to read $buffer from STDIN
# $ident is extracted from $buffer
@packet{ @{$packet_fields{ $ident }} } =
unpack ( $packet_template{ $ident }, $buffer);
I've got one further question about creating and accessing a hash of hashes.
I'm currently using the following structures:
my %ARP_GLA = (
x => 247866.9,
y => 666999.7,
);
my %ARP_EDI = (
x => 314382.0,
y => 673841.9,
I'd like to combine these into one data structure which I guess is a hash of hashes.
I believe I know enough to create the hash. This should do the trick:
my %ARP = (
GLA => {
x => 247866.9,
y => 666999.7
},
EDI => {
x => 314382.0,
y => 673841.9
}
);
How do I now access the elements in this structure?
Is it something like:
my $edi_arp = $ARP{EDI}->{x};
Thanks.
R.
--
Robin Bowes | http://robinbowes.com
| [reply] [d/l] [select] |
|
|