in reply to Dotted hash access

You seem to be doing a lot of extra work.

$cost = $h->{$location}{$building};

If that doesn't work for you, create a little class to make the hash an object and give it some accessors.

As for your new method of doing things, it's been done before and never really caught on. It's not easy to do either. In your example,

$cost = $h->{"Locations.$location.Buildings.$b.cost"};

You need to restrict the values of $location and $b so they don't contain the separator character. Not only do you have to program it, but you have to document it and tell users they can't use it. Or, let users escape that character, but then your parsing is more difficult. There's a lot of ways for this to go wrong. There is a reason Perl doesn't do this anymore. Your way is the Perl4 way of creating multi-dimensional hashes: see the documentation of $; in perlvar (it tells you not to do this).

The way to make the conventional syntax more comfortable is to use it more.

--
brian d foy <bdfoy@cpan.org>

Replies are listed 'Best First'.
Re^2: Dotted hash access
by sfink (Deacon) on Nov 25, 2004 at 09:40 UTC
    I can buy your arguments if we're talking about completely general nested data structure access, but that's not what I'm after. I really do have a deeply nested structure, which I am completely specifying -- so I have no worries about $location or $b containing the separator, because they're field names. My values could very well have weird characters in them, but I'll never have anything unexpected in the "path" of a value. (True, a field name like $location could easily be coming from an external source, but I have to scrub them for sanity well before reaching this code anyway.)

    $cost = $h->{$location}{$building} wouldn't work for me. The structure contains many more things than just Locations, and a location has much more data associated with it than a set of Buildings. So I don't want to accidentally get the wrong value back if I have a location name that happens to coincide with some other field name. (Perhaps I should give a more detailed dump of a data set?)

    My approach differs from Perl4's because mine can be used hierarchically, unlike $;. So I am free to do

    $locinfo = $h->{"Locations.$location"}; count_totals($locinfo->{"Resources"}); compute_upkeep($locinfo->{"Buildings"});
    or whatever.
      If that's the case, why don't have have a class that represents and provides access to the data structure? You can make the interface very simple without creating a bunch of new syntax that you will have to explain to everyone who looks at the code. Try a class setup: Thingy HAS Buildings HAS Locations: You leave everything in your big hash, but when you need something, you ask for it through a method which gives you a reference to that branch of the data structure.
      $location = $thingy->get_location( $l );
      That method looks at the big data structure, pulls out the reference which is the value for the key $l (which has no restrictions on its characters), blesses it as the mini-class Location, and returns it. It's the same old reference, there is no copy, but you can call methods on it. Not only that, it's easy to see what it's doing because there isn't a lot of things happening at that level.
      $building = $location->get_building( $b );
      That's the same thing, called on the previous mini-object. All the data is still in the big data structure, but you have this hook into it because the reference $building is blessed into the Buidling class. Again, it's the same old reference that's in the data strucutre, but it now knows how to respond to method calls. Or, you can do this in one step.
      $building = $thingy->get_location( $l )->get_building( $b ); #or $building = $thingy->get_building_by_location( $l, $b );
      Or
      $cost = $thingy->get_location( $l )->get_building( $b )->cost; $totals = $location->get_totals; $upkeep = $building->get_upkeep;
      If you want to iterate through everything:
      foreach my $l ( $thingy->all_locations ) { foreacn my $b ( $b->all_buildings ) { $b->set_cost( $b->cost + 1 ); } }
      You can define all sorts of other iterators, visitors, and cool things. You don't have to know anything about the data structure. You have a lot of flexibility with this approach, and it uses vanilla Perl syntax that people can read about it in books and in the documentation. You don't have to create any new way to do thing, which means you don't have to create new logic or new bugs. If you want iterators, you can create those yourself inside the class. I talk about all sorts of these things in The Perl Review 0.5.
      while( my $location = $thingy->next_location ) { ... }
      So don't create a new dialect, which just adds to the complexity of your code. You should code not so you understand it, but so other people will understand. :)
      --
      brian d foy <bdfoy@cpan.org>