rsiedl has asked for the wisdom of the Perl Monks concerning the following question:
hey monks,
a quick one, can anyone suggest a cleaner way to do the following?
my (@vals) = split(/\;/, $memory_data);
foreach my $mem (@vals) {
my ($mem_name,$mem_value) = split(/\=/, $mem);
$data{$mem_name} = $mem_value;
} # end-foreach
Cheers
Reagen
Re: clean code
by Zaxo (Archbishop) on Apr 20, 2006 at 12:52 UTC
|
I think I'd make it a straight initialization from a list, using a split on either separator:
my %data = split /[=;]/, $memory_data;
That's a little reliant on the data being what you expect (anything but one "=" in a record is unsupported). It reduces the temporary variables and looping quite a bit.
| [reply] [d/l] |
Re: clean code
by gellyfish (Monsignor) on Apr 20, 2006 at 12:50 UTC
|
Dunno about 'cleaner' (everyone has their own understanding of that term) but certainly more succinct:
my $memory_data = 'foo=bar;zub=baz;yar=grr';
+
my %data;
+
$data{$1} = $2 while $memory_data =~ /([^=;]+)=([^;]+)/g;
/J\ | [reply] [d/l] |
Re: clean code
by Samy_rio (Vicar) on Apr 20, 2006 at 12:50 UTC
|
use strict;
use warnings;
my $memory_data = "foo=bar;perl=japh;";
my %data;
%data = map{split/\=/,$_} split(/\;/, $memory_data);
print "$_\t$data{$_}\n"for keys(%data);
__END__
perl japh
foo bar
Regards, Velusamy R. eval"print uc\"\\c$_\""for split'','j)@,/6%@0%2,`e@3!-9v2)/@|6%,53!-9@2~j';
| [reply] [d/l] [select] |
Re: clean code
by jhourcle (Prior) on Apr 20, 2006 at 13:14 UTC
|
use CGI;
my %data = CGI->new( $memory_data )->Vars();
Of course, that assumes there's no key defined twice, or it won't give the same results. (multiple keys will results in a null (\0) seperated list of all possible values, rather than just the last value for the given key). It'll also do URI decoding, so % and + will get converted. | [reply] [d/l] |
Re: clean code
by dragonchild (Archbishop) on Apr 20, 2006 at 13:16 UTC
|
use CGI::Simple;
my $data = CGI::Simple->new( $memory_data );
# At this point, you just do $data->param( 'foo' ) to retrieve the val
+ue
# associated with 'foo'. Instead of what you're doing which is $data{
+'foo' };
My criteria for good software:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] [d/l] |
Re: clean code
by blazar (Canon) on Apr 20, 2006 at 13:53 UTC
|
Pleonastic quoting and parentheses decrease the ease with which I can read code. But a map is well suited for this task:
my %data=map +(split /=/)[0,1], split /;/, $memory_data;
The [0,1] thing is there just in case split /=/ would return more than two entries, which may be possible. Depending on the reliability of your input you may either leave it out altogether or on the contrary add more strict checks.
Also, this assumes you want to fill %data all at once. If you're just adding (and possibly replacing) key-value pairs to it instead, then just:
%data=(%data, map +(split /=/)[0,1], split /;/, $memory_data);
| [reply] [d/l] [select] |
Re: clean code
by creamygoodness (Curate) on Apr 20, 2006 at 19:45 UTC
|
I pretty much like what you have already! It's easy to understand and robust. The only thing I think you really ought to change are the backslashes in the split regexes, which shouldn't be there.
All the other changes I'd make are superficial. Structurally, the temporary variable @vals can be eliminated by putting it within the loop initializer. Stylistically, I think you ought to leave off the comment marking the end of the loop, as you're already practicing proper indentation, and that's sufficient. Here's some code I might have written:
# extract name-value pairs from serialized database row
for my $pair ( split( /;/, $serialized_row ) ) {
my ( $name, $value ) = split( /=/, $pair );
$record{$name} = $value;
}
| [reply] [d/l] [select] |
|
Make that split(/=/, $pair, 2) and your values will be allowed to have equals signs.
For most casual applications, I usually use split(/\s*=\s*/, $pair, 2). I like spaces. I'd do the same for the semicolon split. But considering the comment (this is from a serialized db row), I doubt there's much point.
To be more robust (specifically: to allow arbitrary characters in the names and values), I agree with the suggestions to use URI escaping. Since you're programmatically generating the data you're parsing, there's no reason not to do the job right -- your first concern should be correctness, second should be basic human readability (so you can tell what's going on), and a very distant third should be human writability (for debugging, casual testing, off-the-cuff model manipulation, etc.)
| [reply] [d/l] [select] |
|
sfink++ on the additional argument to split().
As for the comment, the original post did not make clear the purpose of the code block. I always code in commented paragraphs (as per Perl Best Practices), and such comments should be as specific as possible. However, I didn't want to make a big deal about that personal stylistic choice it because it wasn't germane.
Several other posters made what I think was a reasonable assumption that the purpose of the block was CGI processing, presumably based on the structure of the data and the name of the $memory_data variable. Processing of CGI query strings raises a host of issues I didn't want to get into, so I made a different assumption about the nature of the data. :)
| [reply] [d/l] |
Re: clean code
by Gilimanjaro (Hermit) on Apr 21, 2006 at 10:58 UTC
|
%data = $memory_data =~ /([^=]+)=([^;]*);?/g;
at least, depending on your definition on 'clean'...
update: I now noticed that my regex is almost identical to gellyfish's, but I assign directly to the hash... | [reply] [d/l] |
Re: clean code
by bart (Canon) on Apr 23, 2006 at 11:16 UTC
|
You've got a bit of superfluous characters. And your variables, which are lexically scoped to the block (= invisible outside that block), can have much simpler names.
foreach my $mem (split /;/, $memory_data) {
my($name,$value) = split /=/, $mem, 2;
$data{$name} = $value;
}
I don't think this is that bad.
I've applied sfink's advice, which is my recommendation too. | [reply] [d/l] |
|
|