[Purdue-pm] Perl 6 hyper and reduction operators, etc.

Mark Senn mark at purdue.edu
Fri Mar 8 17:06:41 PST 2019


Purdue Perl Mongers,

Thought you might be interested in the Perl 6 program below.  See
https://perl6.org/ for more information on Perl 6 (not to be
confused with Perl 5).

It shows how to
    o   defining your own infix command (+ is a normal infix command)
    o   do custom usage messages---automatic usage
        messages are done by default
    o   do grep commands in parallel using .race
    o   use hyper operators
    o   use reduction operations
    o   etc.

I'm working on a one page printed on both sides example of how to
use the free LaTeX typesetting system.  So far the following is
planned (this document will have narrow margins and not much white
space):
    automatically produced bibliographies
    chemical diagrams
    chemical symbols
    computer program listings
    defining LaTeX commands, in particular to make it easy to typeset
      the first two verses of Supercalifragilisticexpialidocious
    doing SI and etc. units properly (meters, seconds, etc.)
    electrical circuits
    embedding video files in PDF files
    embedding you tube videos in PDF files
    Feynman diagrams
    figures
    graphics including 2D and 3D graphs using the handicapped friendly
      Cividis color map
    math (including weird fonts and symbols from many branches of math)
    microtype typograhy improvements
    music
    pictures
    tables
The purpose of this document is to grab people's attention and try
to get them interested in using LaTeX for their thesis or dissertation.
Let me know if there is anything you'd like to see demostrated.

Mark Senn, Senior Software Engineer,
Engineering Computer Network, Purdue University




#
#  factor  2019-03-08  Mark Senn  http://engineering.purdue.edu/~mark
#
#  Factor the positive integer given on the command line.
#

# If the MAIN subroutine signature doesn't match usage print
# this custom error message.
sub USAGE  {
    say "Usage: {$*PROGRAM-NAME} unsigned_composite_number";
}

# Set the MAIN subroutine signature so $N must be a positive
# integer that is is a composite number---not a prime.
sub MAIN (UInt $N where $N > 0 && !($N.is-prime))  {



    # METHOD 1: divide by each prime, one at a time.
    
    # From
    #     http://www.jnthn.net/papers/2018-conc-par-8-ways.pdf
    # starting at page 39 (for example, ^3 is a (0,1,2) list:

    #     Sequential runs in 17.2s
    #     say ^100000.grep(*.is-prime) .elems
    #     Parallel runs in 5.3s
    #     say ^100000.race.grep(*.is-prime) .elems

    # Make a list of primes that are in the range of 2 to floor(sqrt($n)).
    # I LOVE the way Perl 6 code reads well from left to right.
    my @p = (2..$N.sqrt).race.grep(*.is-prime);

    my $n = $N;

    # While the number hasn't been factored.
    while ($n != 1  &&  !($n.is-prime))  {
        # Try dividing it by all computed primes.
        for @p -> $p  {
            # Is the number evenly divisible by the current prime?
            while $n %% $p  {
                say $p;
                $n div= $p;
            }
        }
    }

    # For the number 6, for example, we factor out a 2 above
    # and there is a 3 left.  If there is 1 don't print it,
    # if there is one remaining prime factor then print it.
    ($n == 1)  or  say $n;



    say "-----";



    # METHOD 2: divide by all primes in parallel.  We can
    # use hyper operators and reduction operators to make
    # the notation nicer.
    #
    # From https://docs.perl6.org/language/operators#Hyper_operators:
    #     Hyper operators include « and », with their ASCII variants
    #     << and >>. They apply a given operator enclosed (or preceded
    #     or followed, in the case of unary operators) by « and/or »
    #     to one or two lists, returning the resulting list, with the
    #     pointy part of « or » aimed at the shorter list. Single
    #     elements are turned to a list, so they can be used too.
    #     [...]
    #     Also, methods can be called in an out of order, concurrent
    #     fashion. The resulting list is in order. Note that all hyper
    #     operators are candidates for autothreading and will cause
    #     tears if the methods have side effects. The optimizer has
    #     full reign over hyper operators, which is the reason that
    #     they cannot be defined by the user.

    $n = $N;

    # Define a new operator called mt (make triple).  The $n
    # (nummber) and $p (prime) in the sub's signature are
    # local to this subroutine.  Given $n and $p we return a
    # (multiplier, multiplacand, count) triple.  Equivalent
    # code for expression below is
    #     If ($n mod $p == 0)  {
    #         return ($p, 1, 1);
    #     }  else  {
    #         return (1, 1, 0);
    #     }
    # Offhand I don't know how to turn ordinary subs into hyper
    # operators---that's why I made this an infix operator.
    sub infix:<mt>(UInt $n, UInt $p)  {
        ($n %% $p)  ??  ($p,1,1)  !!  (1,1,0);
    }

    # Initialize the @totalcount array.
    my @totalcount = 0 xx @p;

    # Divide $n by each prime.
    # For $n = 2 x 3 x 5 x 7 = 210, this gave
    #     (1, 1, 1, 1, 0, 0).
    # For $n = 2^2 x 3 x 5 x 7 = 420, this gave
    #     (1, 1, 1, 1, 0, 0, 0, 0)
    # not
    #     (2, 1, 1, 1, 0, 0, 0, 0)
    # To get all the factors we need to divide $n by
    # the product of all multipliers and multiplacands
    # and do another pass.

    while (True)  {

        # One should be able to do
        #     my @triple = $n >>mt<< @p;
        # and/or
        #     my @triple = ($n) >>mt<< @p;
        # because, as stated above, "Single elements are turned to
        # a list, so they can be used too.".  This didn't work using
        # the April 2018 version on Perl 6.  Make a list of $n's the
        # length of @p.
        my @n = $n xx @p;

        my @triple = @n >>mt<< @p;

        # Use some temporary variables to make things more clear.
        my @multiplier   = @triple[*; 0];
        my @multiplicand = @triple[*; 1];
        my @count        = @triple[*; 2];

        # If we don't have any primes exit the loop.
        # ([+] @count == 0)  and  last;
        # Depending on the hardware we're running on, doing a
        # bit-wise or over all the counts may be faster.
        ([+|] @count)  or  last;

        @totalcount >>+=<< @count;

        $n div= [*] (@multiplier >>*<< @multiplicand);

    }

    for (0..^@p) -> $i  {
        for (^@totalcount[$i])  {
            say "{@p[$i]}";
        }
    }
    
    ($n > 1)  and  say $n;



}


More information about the Purdue-pm mailing list