in reply to Dynamic function chains?
(I used a helper function to build the printf functions for me.)sub printN($) { my $format = shift; sub { sprintf $format, @_ } } my %functions = ( -power => sub { exp( $_[0] )}, -log2 => sub { log( $_[0] )/log(2.0) }, -loge => sub { log( $_[0] ) }, -log10 => sub { log( $_[0] )/log(10.0) }, -round => sub { int($_[0] + ($_[0] <=> 0)*0.5) }, -trunc => sub { int $_[0] }, -f1 => printN "%.1f", -f2 => printN "%.2f", -f3 => printN "%.3f", -f4 => printN "%.4f", -f5 => printN "%.5f", );
Second, I would parse the command line to extract, in order, the operators to be performed:
my @func_pipeline = grep {$_} map {$functions{"$_"}} @ARGV; @ARGV = grep {!$functions{"$_"}} @ARGV;
At this point, @func_pipeline contains the functions corresponding to the operators we want to apply. I would then use function composition to glue the functions together, to result in a single function that performs the entire pipeline:
(The above uses the ever-handy reduce from List::Util.)sub compose($$) { my ($f, $g) = @_; sub { $f->( $g->(@_) ) } } sub id { @_ }; # identity function my $composite_fn = reduce {compose($a,$b)} @func_pipeline, \&id;
Finally, I would apply the composite function to each word of input, line by line:
That's it. Putting it all together into a program, gives this:print join ' ', map $composite_fn->($_), split while <>;
As an example, let's print (with 4-digit precision) the result of "powering" the numbers 1 2 3:#!/usr/bin/perl -l use strict; use List::Util qw( reduce ); sub printN($) { my $format = shift; sub { sprintf $format, @_ } } sub compose($$) { my ($f, $g) = @_; sub { $f->( $g->(@_) ) } } sub id { @_ }; # identity function my %functions = ( -power => sub { exp( $_[0] )}, -log2 => sub { log( $_[0] )/log(2.0) }, -loge => sub { log( $_[0] ) }, -log10 => sub { log( $_[0] )/log(10.0) }, -round => sub { int($_[0] + ($_[0] <=> 0)*0.5) }, -trunc => sub { int $_[0] }, -f1 => printN "%.1f", -f2 => printN "%.2f", -f3 => printN "%.3f", -f4 => printN "%.4f", -f5 => printN "%.5f", ); my @func_pipeline = grep {$_} map {$functions{"$_"}} @ARGV; @ARGV = grep {!$functions{"$_"}} @ARGV; my $composite_fn = reduce {compose($a,$b)} @func_pipeline, \&id; print join ' ', map $composite_fn->($_), split while <>;
There you have it!$ echo 1 2 3 | ./calc-pipeline -f4 -power 2.7183 7.3891 20.0855
Cheers,
Tom
Tom Moertel : Blog / Talks / LectroTest / PXSL / Coffee / Movie Rating Decoder
|
|---|