rovf has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks! I would like to know if someone has some idea how I could design the following in a nicer way:

I would like to build a data structure which is conceptually like a matrix, but where the rows and columns are identified by keys (like a hash). Actually, the columns are identified by various kinds of our platform, and the rows by properties of these platforms. For example, the element in column 'R-Solaris', row 'scriptpath', would be a path of directories containing scripts on platform of type R-Solaris, while the element in column 'R-Linux', row 'confdir', would be the path of some configuration directory used on systems of type R-Linux.

One obvious way to implement it would be to build a nested hash, like this:

my $conf = { scriptpath => { R-Solaris => '....', R-Linux => '....', UNC-Windows => '....', ... }, confdir => { R-Solaris => '....', R-Linux => '....', UNC-Windows => '....', ... }, ... }
This is certainly possible, but I don't like it very much, because it obsures the fact that we have a "matrix" (it's not obvious for the reader that every contained hash must have the same set of keys.

I was thinking of a variation of this solution in that I still use the data structure as outlined above, but don't construct it "statically", but with the help of constructor functions:

my $conf={}; sub conf { $conf{$_[0]}={ R-Solaris => $_[1], R-Linux => $_[2], UNC-Windows => $_[3], ... } } conf('scriptpath','...',...); conf('confdir',........);
This has the advantage that it not only becomes obvious, that the data is structured like a matrix, but that I can even check that the number of parameters is the correct one. Still, this looks to me like "too much code" for such a seamingly simple problem.

Of course those style questions are always are matter of personal taste, but if someone would solve this in a different way, I would like to see some alternative.

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: "Hash Matrix" Design Question
by jethro (Monsignor) on Sep 24, 2008 at 13:22 UTC

    If this data structure is big, you might put it into a config file and use a modul from the CPAN config hierarchy to read that config file. If you don't like a separate file, put it into the __DATA__ section.

    Naturally you also need to check afterwards if every mandatory entry in the matrix is filled in, but that is a good idea anyway

    The main advantage is that the configuration data is separate from the code.

      If this data structure is big, you might put it into a config file

      This is not possible, because it needs to be computed at run time...

      -- 
      Ronald Fischer <ynnor@mm.st>

        Then your conf idea sounds good.

        A different idea might be to have three subs like this:

        my @alloptions=(); my @allOS=(); sub add_OS { # add an operation system to the configuration matrix my $os= shift; push @allOS, $os; foreach my $opt (@alloptions) { calculate_parameter($os,$opt); } } sub add_option { # add a configuration option to the configuration matrix my $option= shift; push @alloptions, $option; foreach my $os (@allOS) { calculate_parameter($os,$option); } } sub calculate_parameter { # calculate the option parameter in a specific OS my ($os, $option)= @_; ...

        Creating and filling the matrix is automatically done by calling add_OS and add_option for every os and option.

Re: "Hash Matrix" Design Question
by pajout (Curate) on Sep 24, 2008 at 16:02 UTC
    In my opinion, it very depends on what "matrix" functionalities do you need to implement. For instance, for some jobs, I do not see anything wrong in the simplest HoH representation - nested hash, as you noticed above, plus methods (not error/warning proof):
    my %matrix = ('R-Linux' => {confdir=>'abc'}, 'R-Solaris'=> {scriptpath=>'def'}); sub item { my ($os,$what,$value) = @_; $matrix{$os}{$what} = $value if 3==@_; return $matrix{$os}{$what}; } sub delete_item { my ($os,$what) = @_; delete $matrix{$os}{$what}; } ...