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

Hi All (again),
  Sorry for all the messages today. I've got a deadline that has made me put in some stupid hours and my brain has stopped functioning properly :s
I've got a variable update form that is generated based on a hash reference called $Config. The submitted data comes in similar to what is shown below:-
my %inputdata = ( 'Config->{blah}->{hat}' => "splat", 'Config->{cat}->[0]' => "doh", 'Config->{cat}->[1]' => "ray", );
Now if I want to load this data it's easy with eval:-
my $evaltext; foreach my $key (keys %inputdata) { $evaltext .= "\$$key = qq~$inputdata{$key}~;\n"; }#foreach eval $evaltext;

But I don't like eval's overhead, so I usually try to avoid it. The problem is in doing so I've ended up with some really stupid code.. that works, but surely isn't the best way to do it:-
foreach my $key (keys %inputdata) { my @keys = split(/->/, $key); # if ($#keys == 0) { # ${$keys[0]} = $inputdata{$key}; # }#if if ($#keys == 1) { if ($keys[1] =~ /\{/) { $keys[1] =~ s/(\{|\})//g; ${$keys[0]}->{$keys[1]} = $inputdata{$key}; }#if if ($keys[1] =~ /\[/) { $keys[1] =~ s/(\[|\])//g; ${$keys[0]}->[$keys[1]] = $inputdata{$key}; }#if }#if if ($#keys == 2) { if ($keys[1] =~ /\{/) { $keys[1] =~ s/(\{|\})//g; if ($keys[2] =~ /\{/) { $keys[2] =~ s/(\{|\})//g; ${$keys[0]}->{$keys[1]}->{$keys[2]} = $inputdata{$key} +; }#if if ($keys[2] =~ /\[/) { $keys[2] =~ s/(\[|\])//g; ${$keys[0]}->{$keys[1]}->[$keys[2]] = $inputdata{$key} +; }#if }#if if ($keys[1] =~ /\[/) { $keys[1] =~ s/(\[|\])//g; if ($keys[2] =~ /\{/) { $keys[2] =~ s/(\{|\})//g; ${$keys[0]}->[$keys[1]]->{$keys[2]} = $inputdata{$key} +; }#if if ($keys[2] =~ /\[/) { $keys[2] =~ s/(\[|\])//g; ${$keys[0]}->[$keys[1]]->[$keys[2]] = $inputdata{$key} +; }#if }#if }#if }#foreach
It would also quickly break and need to be updated if the data structure became deeper. Feels like brain it's up my a** at the moment, could someone give me a clout and point me onto the right way to do it?

Thanks

Lyle

Replies are listed 'Best First'.
Re: Creating data tree from input without eval
by Corion (Patriarch) on Apr 05, 2008 at 16:51 UTC

    What makes you think that your code, written in Perl, is faster than the Perl code for eval, written in C?

    I would really recommend you move to a saner way of storing your configuration information, like a slash-delimited path to the target or a dot delimited path, like this:

    blah.hat=splat cat=doh,ray

    Then, assuming your data wouldn't contain commas, you could conveniently create your configuration information by using one of the Config modules. Or you could write your own parser for that (untested, ad-hoc code, see Data::Dumper for a better diving implementation):

    ... my $Config = { some => 'default values', }; while (<CONFIG>) { chomp; my ($key, $value) = split /=/,2; my @path = split /\./, $key; my $loc = $Config; my $last = pop @path; for (@path) { $loc->{$_} ||= {}; $loc = $loc->{$_}; }; if ($value =~ /,/) { # Store an array $loc->{$last} = [ split /,/, $value ]; } else { $loc->{$last} = $value; }; };

    Also see Data::Diver for far more convenient diving into data structures.

      I would really recommend you move to a saner way of storing your configuration information, like a slash-delimited path to the target or a dot delimited path, ...
      Or even YAML, which is about as close to having the full complex structures of Perl available, without ever invoking an eval. Heck, it's even cross platform, so you can read the same file into Python or Ruby.
        Looks like I'll use this in the future. At the moment I'm to far in to doing it this way.
      What makes you think that your code, written in Perl, is faster than the Perl code for eval, written in C?
      Benchmark.pm
        Could you please post this benchmarking code?
Re: Creating data tree from input without eval
by dragonchild (Archbishop) on Apr 05, 2008 at 17:33 UTC
    merlyn's suggestion of YAML is good. I like JSON because it's eval'able into a JS data structure and is a nice proper subset of YAML.

    But, eval's overhead doesn't mean anything in a run-once config load. You're worrying about the stupid stuff. Don't.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      You're worrying about the stupid stuff. Don't.
      Your right. Thanks
Re: Creating data tree from input without eval
by planetscape (Chancellor) on Apr 06, 2008 at 05:18 UTC

    Looks to me like (a) you're looking for a serializer, and (b) you might be having some trouble dealing with arbitrarily complex data structures.

    How can I visualize my complex data structure? may help with both (a) and (b).

    Re: Loading an external hash may give you some ideas on where to look should you decide you want to work with .INI files (which is what I immediately thought of when you said "$Config").

    As for your brain being up your arse... don't worry. I've had many such moments, and I can assure you... It'll reappear eventually. ;-)

    HTH,

    planetscape
Re: Creating data tree from input without eval
by roboticus (Chancellor) on Apr 06, 2008 at 10:41 UTC
    cosmicperl:

    Premature optimization is the root of all evil.
    I don't recall who said that--but it's largely true. Don't worry about the overhead of eval until you actually find it noticeable (objectionable?) in your application. I must periodically repeat that mantra to myself. (A side effect of learning to program on a machine with 3284 bytes of storage available for user programs and a clock rate of nearly 2 MHz.)

    Some years ago (20+?) I read an article that said, in essence: Code everything as simple as possible so you can get it done and easily maintain it. After you're done, run it. If it's fast enough--stop! Only if it's not fast enough should you perform the next step: profile the application and find out which part is the part that's too slow--don't guess. Once you find the slow bit, fix it (repair code, replace algorithm, etc.).

    As an experiment, I did a couple of experiments that way, and was pleased with the results. Need a sort? Just code up a bubble-sort. It's amazing how many known bad but simple algorithms are *perfectly* suitable for the task at hand. (NOTE: If you're writing an operating system or device drivers, please ignore this and the preceding paragraph!)

    Parting thought: If you find something you need to optimize, then set your performance goal first. If a routine needs to take less than 5O ms, then once you beat that, STOP. Otherwise, you might get stuck in an obsessive optimization loop (guilty!) and keep tuning microseconds out of code that's already quite fast enough. (I won't confess to how much time I've spent tuning assembly code to squeeze clocks out of code that immediately precedes a blocking user-input request....)

    </Dvorak-mode>

    ...roboticus