Suppose you need a simple filter function, e.g. to detaint variables that you've already checked to be good. I've come up with something similar:
Writing such filter functions, I often wonder whether I should provide an in-place modification version; for this particular example, it would be useful to have deparsing happening in-place, because the variable is actually trustable (assumed that due checks have been done), so I come up with the following:sub detaint_1 { $[0] =~ /^(.*)$/; return $1; }
This has the added advantage to avoid variable copy, which could be expensive if they're big chunks of texts. In-place modification gives the user the ability to choose: if she wants a copy, just copy and call the function on the copy; otherwise, just apply the function to the original variable.sub detaint_1_inp { $[0] =~ /^(.*)$/; $[0] = $1; }
But what if I'll need to detaint more variables at once, for example after having performed cross-checks on them (think of two variables, one holding a directory name and another a file name in the directory)? I can easily extend the approaches:
Obviously, these results could be obtained with the one-argument version as well (I swear that I read the reply from BrowserUK to my snipped here):sub detaint_n { map { /^(.*)$/; $1 } @_; } sub detaint_n_inp { foreach (@_) { /^(.*)$/; $_ = $1} }
but I wonder: wouldn't it be useful to provide all these behaviours at once with a unique interface? So, I finally came up with the following monster, taking advantage of the three different context I can call a function, i.e. list/array, scalar and void:my @a; # ... set @a ... my @b = map { detaint_1 } @a; # Copy detaint_1_inp($_) foreach (@a); # In-place
which can be easily refactored to get a general filter applier for more functions:sub detaint { my $subref = sub { $_[0] =~ /^(.*)$/; $1 }; return map { &$subref($_) } @_ if (wantarray); # Copy for arrays return &$subref($_[0]) if (defined(wantarray)); # Copy for scalar $_ = &$subref($_) foreach (@_); # In-place return; }
sub apply_filter { my $subref = shift; return map { &$subref($_) } @_ if (wantarray); # Copy for arrays return &$subref($_[0]) if (defined(wantarray)); # Copy for scalar $_ = &$subref($_) foreach (@_); # In-place return; } sub detaint { return apply_filter(sub { $_[0] =~ /^(.*)$/; $1 }, @_); } # Refactored code leads to reuse! sub left_pad { my $padding = shift; my $minlength = length($minlength); return apply_filter(sub { my $v = shift; if (length($v) < $minlength) { ($padding . $v) =~ /(.{$minlength})$/; $v = $1; } $v; }, @_); }
Flavio (perl -e "print(scalar(reverse('ti.xittelop@oivalf')))")
Don't fool yourself.In reply to Writing general code: real world example - and doubts! by polettix
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |