in reply to Refactoring Redux...

Starting with your code:

my $y = 0; my @list; print "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n +"; print "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"; print " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10. +dtd\" >\n"; print "<svg height=\"200\" width=\"200\">\n"; print " <line x1=\"0\" y1=\"0\" x2=\"80\" y2=\"0\"/>\n"; print " <polyline\n"; print " points=\""; foreach (@scores) { if ($_) { push(@list,$y++); push(@list,$_ * -1.0); } } print join(",",@list); print "\"\n"; print " style=\"stroke: black; width: 1px; fill: none;\"/>\n"; print "</svg>\n";

My first inclination would be to clean up some of the escaped double quotes with heredocs:

my $y = 0; my @list; print <<PAGE_HEADER; <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> PAGEHEADER print <<SVG_HEADER; <svg height="200" width="200"> <line x1="0" y1="0" x2="80" y2="0" /> SVG_HEADER print " <polyline\n"; print " points=\""; foreach (@scores) { if ($_) { push(@list,$y++); push(@list,$_ * -1.0); } } print join(",",@list); print "\"\n"; print " style=\"stroke: black; width: 1px; fill: none;\"/>\n"; print <<SVG_FOOTER; </svg> SVG_FOOTER

Next I would move the variable declarations down to where they are used -- otherwise it looks too much like C code. I put all of the print statements together since they are all working to priduce this one complicated line. I also explicitly test for non-zero-ness.

print <<PAGE_HEADER; <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> PAGEHEADER print <<SVG_HEADER; <svg height="200" width="200"> <line x1="0" y1="0" x2="80" y2="0" /> SVG_HEADER my $y = 0; my @list; foreach (@scores) { if ($_ != 0) { push(@list,$y++); push(@list,$_ * -1.0); } } print " <polyline\n"; print " points=\""; print join(",",@list); print "\"\n"; print " style=\"stroke: black; width: 1px; fill: none;\"/>\n"; print <<SVG_FOOTER; </svg> SVG_FOOTER

Then I clean up the polyline piece and use another heredoc:

print <<PAGE_HEADER; <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> PAGEHEADER print <<SVG_HEADER; <svg height="200" width="200"> <line x1="0" y1="0" x2="80" y2="0" /> SVG_HEADER my $y = 0; my @list; foreach (@scores) { if ($_ != 0) { push(@list,$y++); push(@list,$_ * -1.0); } } my $list = join(",",@list); print <<SVG_BODY; <polyline points="$list" style="stroke: black; width: 1px; fill: none;"/> SVG_BODY print <<SVG_FOOTER; </svg> SVG_FOOTER

Now that I can see clearly, I can collapse the last two heredocs into one.

print <<PAGE_HEADER; <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> PAGEHEADER print <<SVG_HEADER; <svg height="200" width="200"> <line x1="0" y1="0" x2="80" y2="0" /> SVG_HEADER my $y = 0; my @list; foreach (@scores) { if ($_ != 0) { push(@list,$y++); push(@list,$_ * -1.0); } } my $list = join(",",@list); print <<SVG_BODY; <polyline points="$list" style="stroke: black; width: 1px; fill: none;"/> </svg> SVG_BODY

That's it .. I haven't added any subroutines (perhaps more code would have suggested this would have been a good route to take). Now you can see what's going on without having to fight your way through a maze of escaped double quotes.

--t. alex
but my friends call me T.

Update: Jeepers, and I forgot to collapse the two push statements into one .. well, that's left as an exercise for the reader. :)

Replies are listed 'Best First'.
Re^2: Refactoring Redux...
by Aristotle (Chancellor) on Dec 20, 2002 at 08:59 UTC
    Unquoted heredoc terminators in the << are not nice. And you can add another step:
    my $y = 0; my @list; foreach (@scores) { if ($_ != 0) { push(@list, $y++, @list, $_ * -1.0); } } my $list = join(",",@list); print << "SVG"; <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg height="200" width="200"> <line x1="0" y1="0" x2="80" y2="0"/> <polyline points="$list" style="stroke: black; width: 1px; fill: non +e;"/> </svg> SVG
    And now we're moving in the right direction - separate the application logic from the output logic. Let's go another step towards it:
    my $y = 0; my @list; foreach (@scores) { if ($_ != 0) { push(@list, $y++, @list, $_ * -1.0); } } print << "SVG"; <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg height="200" width="200"> <line x1="0" y1="0" x2="80" y2="0"/> <polyline points="@{[ join(",", @list) ]}" style="stroke: black; width: 1px; fill: none;"/> </svg> SVG
    Finally we wrap up by collapsing the application logic:
    my @list = map { $scores[$_] != 0 ? ($_, $scores[$_] * -1.0) : () } 0 .. $#scores;

    No extra temporary variables, no scattered output, application logic minimized. The next step, assuming this is part of a larger app, would be Template Toolkit.

    Actually, thinking about it, it's debatable whether the != 0 distinction is part of the application logic or part of the display logic. One might just as well do

    my @l_list = map [ $_, $scores[$_] * -1.0 ], 0 .. $#scores; print << "SVG"; ... @{[ join ",", map @$_, grep $_->[0] != 0, @l_list; ]} ...

    Which leads me to question my term "application logic" - one might just as well consider the pairing of data points and coordinates display logic and shift it all into the template. That depends on the specific situation at hand.

    At any rate, this is a very important distinction to make, and to make consciously. I see failure to do so all the time even though it's just as important everywhere else as in web related programming. Maintainability and clarity is best achieved if you do exactly one cleanly defined thing at a time in every piece of your code. Be careful not to mix up activities. Of course the entire concept hinges on the idea of clean data structure design and in a way, that means clean interfaces between parts of your code. All of these matters are interrelated, and mastering them is what makes one a great software engineer.

    Makeshifts last the longest.