#!/usr/bin/perl -w # # "Loop Abstraction Via Recursion" # # 060111 by liverpole # # Strict use strict; use warnings; # User-defined my @list = qw( 1 2 3 4 5 ); # Prototypes sub abstract_loop($$$); sub arbitrary_N_loops($;$$); sub show_results($$$); # Globals my $N; # The number of loops (which we want to abstract) my $total; # Total match attempts my $pvals = [ ]; # The list of values my $nmatch = 0; # Total matches # Subroutines sub match(@) { my ($plist) = @_; my $sum = 0; my $prod = 1; map { $prod *= $_; $sum += $_ } @$plist; my $div = ($prod / $sum); ($div == int($div)); } sub show_results($$$) { my ($nloops, $nmatch, $total) = @_; my $pct = ($total)? int(1000 * $nmatch / $total) / 10: 0; my $s = (1 == $nloops)? " ": "s"; printf "[%3d loop$s] ", $nloops; printf "Matches: %8d / %9d ", $nmatch, $total; printf "(%5.1f%%)\n", $pct; } # Main program # One loop $N = 1; $nmatch = $total = 0; foreach (@list) { push @$pvals, $_; # Add a new value to the list match($pvals) and ++$nmatch; # Check if it matches the criteria ++$total; # Add 1 to the total count pop @$pvals; # Remove the value at the end } show_results($N, $nmatch, $total); #### [ 1 loop ] Matches: 5 / 5 (100.0%) #### # Two loops $N = 2; $nmatch = $total = 0; foreach (@list) { push @$pvals, $_; foreach (@list) { push @$pvals, $_; match($pvals) and ++$nmatch; ++$total; pop @$pvals; } pop @$pvals; } show_results($N, $nmatch, $total) #### [ 1 loop ] Matches: 5 / 5 (100.0%) [ 2 loops] Matches: 2 / 25 ( 8.0%) #### # Three loops $N = 3; $nmatch = $total = 0; foreach (@list) { push @$pvals, $_; foreach (@list) { push @$pvals, $_; foreach (@list) { push @$pvals, $_; match($pvals) and ++$nmatch; ++$total; pop @$pvals; } pop @$pvals; } pop @$pvals; } show_results($N, $nmatch, $total); #### # Four loops $N = 4; $nmatch = $total = 0; foreach (@list) { push @$pvals, $_; foreach (@list) { push @$pvals, $_; foreach (@list) { push @$pvals, $_; foreach (@list) { push @$pvals, $_; match($pvals) and ++$nmatch; ++$total; pop @$pvals; } pop @$pvals; } pop @$pvals; } pop @$pvals; } show_results($N, $nmatch, $total); #### [ 1 loop ] Matches: 5 / 5 (100.0%) [ 2 loops] Matches: 2 / 25 ( 8.0%) [ 3 loops] Matches: 28 / 125 ( 22.4%) [ 4 loops] Matches: 110 / 625 ( 17.6%) #### sub abstract_loop($$$) { my ($N, $plevel, $pvals) = @_; foreach (@list) { push @$pvals, $_; arbitrary_N_loops($N, $plevel, $pvals); pop @$pvals; } } sub arbitrary_N_loops($;$$) { my ($N, $plevel, $pvals) = @_; # Check level my $level = (!defined($plevel))? 1: $$plevel + 1; # Initialization (Called once at the beginning of all loops) if (1 == $level) { $pvals = [ ]; $nmatch = $total = 0; } # Call loop N times if ($level <= $N) { &abstract_loop($N, \$level, $pvals); } else { # This block only happens within the innermost loop match($pvals) and ++$nmatch; ++$total; } # Finalization (Called once at the end of all loops) if (0 == --$level) { show_results($N, $nmatch, $total); } } arbitrary_N_loops(1); arbitrary_N_loops(2); arbitrary_N_loops(3); arbitrary_N_loops(4); arbitrary_N_loops(5); arbitrary_N_loops(6); arbitrary_N_loops(7); arbitrary_N_loops(8); #### [ 1 loop ] Matches: 5 / 5 (100.0%) [ 2 loops] Matches: 2 / 25 ( 8.0%) [ 3 loops] Matches: 28 / 125 ( 22.4%) [ 4 loops] Matches: 110 / 625 ( 17.6%) ------------------------------------------------------------------------------- [ 1 loop ] Matches: 5 / 5 (100.0%) [ 2 loops] Matches: 2 / 25 ( 8.0%) [ 3 loops] Matches: 28 / 125 ( 22.4%) [ 4 loops] Matches: 110 / 625 ( 17.6%) [ 5 loops] Matches: 681 / 3125 ( 21.7%) [ 6 loops] Matches: 3615 / 15625 ( 23.1%) [ 7 loops] Matches: 18550 / 78125 ( 23.7%) [ 8 loops] Matches: 92598 / 390625 ( 23.7%)