Colin Crain › Perl Weekly Review #163

Monday, Jun 20, 2022| Tags: perl

( …continues from previous week. )

Welcome to the Perl review pages for Week 163 of The Weekly Challenge! Here we will take the time to discuss the submissions offered up by the team, factor out some common methodologies that came up in those solutions, and highlight some of the unique approaches and unusual code created.


●︎ Why do we do these challenges?

I suppose any reasonable answer to that question would be from a field as wide ranging and varied as the people who choose to join the team. One thing, though, is clear: it’s not a competition, and there are no judges, even if there is a “prize” of sorts. About that – I think of it more as an honorarium periodically awarded to acknowledge the efforts we make towards this strange goal. So there’s no determination to find the fastest, or the shortest, or even, in some abstract way, the best way to go about things, although I’m certain the participants have their own aspirations and personal drives. As Perl is such a wonderfully expressive language, this provides quite a bit of fodder to the core idea of TMTOWTDI, producing a gamut of varied techniques and solutions.

Even the tasks themselves are often open to a certain amount of discretionary interpretation. What we end up with is a situation where each participant is producing something in the manner they find the most interesting or satisfying. Some team members focus on carefully crafted complete applications, thoroughly vetting input data and handling every use case they can think up. Others choose to apply themselves to the logic of the underlying puzzle and making it work in the most elegant way they can. Some eschew modules they would ordinarily reach for, others embrace them, bringing to light wheels perhaps invented years ago that happen to exactly solve the problem in front of them today.

I’ve been considering this question for some time and have found one binding commonality between all of us out solving these challenges each week, in that however we normally live our lives, the task in front of us more than likely has nothing to do with any of that. And I think this has great value. We all do what we do, in the real world, and hopefully we do it well. The Weekly Challenge provides us with an opportunity to do something germane to that life yet distinctly different; if we only do the things we already know how to do then we will only do the same things over and over. This is where the “challenge” aspect comes into play.

So we can consider The Weekly Challenge as providing a problem space outside of our comfort zone, as far out from that comfort as we wish to take things. From those reaches we can gather and learn things, pick and choose and bring what we want back into our lives. Personally, I think that’s what this whole thing is about. YMMV.


Every week there is an enormous global collective effort made by the team, analyzing and creatively coding the submissions, and that effort deserves credit due.

And that, my friends, is why I’m here, to try and figure out ways to do just that.

So here we are then.

I’m ready — let’s get to it and see what we can find.


For Additional Context…

Before we begin, you may wish to revisit either the pages for the original tasks or the summary recap of the challenge. But don’t worry about it, the challenge text will be repeated and presented as we progress from task to task.

Oh, and one more thing before we finally get started:

Getting in Touch with Us

Email › Please feel free to email me (Colin) with any feedback, notes, clarifications or whatnot about this review.

GitHub › Submit a pull request to us for any issues you may find with this page.

Twitter › Join the discussion on Twitter!

I’m always curious as to what the people think of these efforts. Everyone here at the PWC would like to hear any feedback you’d like to give.

...Enough? Fine. So without even further ado...

•       Task 1       •       Task 2       •       BLOGS       •


TASK 1

Sum Bitwise Operator

Submitted by: Mohammad S Anwar

You are given list positive numbers, @n.

Write script to calculate the sum of bitwise & operator for all unique pairs.

Example 1

    Input: @n = (1, 2, 3)
    Output: 3

Since (1 & 2) + (2 & 3) + (1 & 3) => 0 + 2 + 1 => 3.

Example 2

    Input: @n = (2, 3, 4)
    Output: 2

Since (2 & 3) + (2 & 4) + (3 & 4) => 2 + 0 + 0 => 2.

about the solutions

Adam Russell, Alexander Pankoff, Ali Moradi, Arne Sommer, Athanasius, Cheok-Yin Fung, Colin Crain, Dave Jacoby, Duncan C. White, E. Choroba, Flavio Poletti, Jaldhar H. Vyas, James Smith, Jorg Sommrey, Julien Fiegehenn, Laurent Rosenfeld, Lubos Kolouch, Luiz Felipe, Matthew Neleigh, Mohammad S Anwar, Niels van Dijke, Pete Houston, Peter Campbell Smith, PokGoPun, Roger Bell_West, Ryan Thompson, Ulrich Rieke, and W. Luis Mochan

On perusing our member-generated literature on this first task, it seems that beyond the usual nit-picking about exactly what is being requested here, and how to interpret various alternate readings of the description text, no one can quite figure out why we would ever wish to perform this particular action.

It remains a mystery.

But then again that’s no mind. We don’t need a story to do the job. We have an assignment and simply need to assemble an algorithm to perform it.

There was the usual confusion as to the details, in this case exactly what was meant by “unique pairs”. Does this refer to indices or actual values? Obviously a duplicated index will contain the same value, but with the second option a duplicated value in the list will also trigger the exception case. As we are left in the dark about why we’re performing this analysis we can’t definitively decide without context.

There were 28 submissions for the first task this past week.

a little BIT of THIS, and a little BIT of THAT

Roger Bell_West, Mohammad S Anwar, Ali Moradi, Matthew Neleigh, Alexander Pankoff, Luiz Felipe, Ryan Thompson, Pete Houston, James Smith, E. Choroba, and Niels van Dijke

The unique index interpretation of the “unique pairs” directive was quite popular, but we did see ideas about unique values also implemented. Several in fact. Or even unique pairs, meaning duplicate values were ok, but only once. Hmmm. Some people explored several alternate options.

Due to this ambiguity I took any reasonable interpretation to be valid. Myself, I read pairs as pairings of indices. YMMV.

Roger Bell_West

  additional languages: Javascript, Kotlin, Lua, Postscript, Python, Raku, Ruby, Rust

  blog writeup: RogerBW’s Blog: The Weekly Challenge 163: Sums and Sums

We’ll start with Roger, with an example of interpreting the directive as unique index pairings. Viewed this way, the matching up becomes a problem of combinatorics: how many selections of 2 unique index positions can be gathered from the input?

The bitwise AND operation is commutative, as A & B = B & A, so we are looking for combinations, which are unordered, versus permutations, which are. Assessing combinations, the pairings (1,2) and (2,1) are equivalent.

A simple way to perform this basic action, known as “n choose 2” is to import a function to perform it, such as combinations() from Algorithm::Combinatorics. In common usage, this delivers an iterator function, that when next is called on it returns the anonymous array pairings of the various combinations. Reading through these, we can access the elements and apply the bitwise operations, accumulating a sum of the results along the way.

    use Algorithm::Combinatorics qw(combinations);

    sub sumbitwise {
      my $ls = shift;
      my $iter = combinations($ls,2);
      my $s = 0;
      while (my $c = $iter->next) {
        $s += $c->[0] & $c->[1];
      }
      return $s;
    }

Mohammad S Anwar

Here’s a very similar demonstration from Mohammad. With Algorithm::Combinatorics, if we call combinations in a list context the complete list of combinations is returned as an array of arrays. Done this way we can create a loop using a statement modifier, putting the summation in a single line.

    sub bitwise_and {
        my ($numbers) = @_;

        return unless @$numbers;

        my $sum = 0;
        $sum += $_->[0] & $_->[1] for (combinations($numbers, 2));

        return $sum;
    }

Ali Moradi

  additional languages: C, C++, Lua, Pascal

Then again, the algorithm to match up combinations from a list is quite simple: take the first element, then match it to each element following to make one combination per selection. Then advance to the next element and repeat the process. Only matching to those elements further up the array removes self-pairings from the mix along with any duplicates, such as (2,1) in addition to the already-gathered (1,2).

There really isn’t a need for a module to construct the pairings, as nested loops will do the job quite well.

Here Ali uses two for loops.

    sub sum_bitwise_operator{
      my ($arr) = @_;
      my $sum = 0;
      for(my $i = 0; $i < @$arr; ++$i){
        for(my $j = $i+1; $j < @$arr; ++$j){
          $sum += $arr->[$i] & $arr->[$j];
        }
      }
      $sum;
    }

Matthew Neleigh

Matthew spices things up a bit with a detailed report mirroring the descriptive text from the examples. Once he has passed the input array through a pair of nested loops to create a list of individual pairings, this list is then processed to create both the output value and a report string constructed from the steps used to arrive there.

I find the string assembly to be the more interesting part of the solution. Most of the submissions follow either one or the other of the two patterns we have seen so far to construct the pairings.

You may notice that we are still treating “unique” to mean unique index pairings. This was by far the most common approach.

    Input: @n = (3, 4, 3, 3)
    Output: 9

    Since (3 & 4) + (3 & 3) + (3 & 3) + (4 & 3) + (4 & 3) + (3 & 3)  =>  0 + 3 + 3 + 0 + 0 + 3  =>  9
    foreach $set (@sets){
        my @pairs = unique_pairs_from_list(@{$set});
        my $sum = 0;
        my $shown_work;

        # Do the math and document it; this will be

        # shared later...

        $shown_work = sprintf(
            "Since %s  =>  %s  =>  %d",
            join(" + ", map("(".$_->[0]." & ".$_->[1].")", @pairs)),
            join(
                " + ",
                map(
                    # Where the actual math happens... and also

                    # some output generation

                    { my $a = ($_->[0] & $_->[1]); $sum += $a; $a; }
                    @pairs
                )
            ),
            $sum
        );

        printf("Input: \@n = (%s)\n", join(", ", @{$set}));
        printf("Output: %d\n\n", $sum);
        print("$shown_work\n\n\n");
    }

    sub unique_pairs_from_list{

        return(undef) unless(scalar(@ARG) > 1);

        my @pairs = ();

        for my $i (0 .. ($#ARG - 1)){
            for my $j (($i + 1) .. $#ARG){
                push(@pairs, [$ARG[$i], $ARG[$j]]);
            }
        }

        return(@pairs);
    }

Alexander Pankoff

  additional languages: Haskell

Alexander breaks up his process along a very functional programming paradigm, with a collection of discrete processing units that each perform one specific task. We end up with a familiar process best understood if read backwards from the tail, or in this script fragment, upwards from the bottom. The general purpose combinations function takes a list and a group size and returns an array of arrays. This is called by unique_pairs() as a wrapper, supplying the request for 2-element sets. In turn this is called from sum_bitwise_operator() which processes the array-of-arrays returned first with a map, applying the bitwise operator to each of the paired elements in an individual combination to return a single value. Then finally the list of result values is itself summed.

say sum_bitwise_operator(@n);

sub sum_bitwise_operator(@n) {
    sum0( map { $_->[0] & $_->[1] } unique_pairs(@n) );
}

sub unique_pairs(@xs) {
    return combinations( 2, @xs );
}

sub combinations ( $size, @xs ) {
    return if !@xs;
    return map { [$_] } @xs if $size == 1;

    my @out;
    for my $index ( 0 .. $#xs ) {
        my @rest = @xs[ $index + 1 .. $#xs ];
        for my $comb ( combinations( $size - 1, @rest ) ) {
            push @out, [ $xs[$index], @$comb ];
        }
    }

    return @out;
}

Luiz Felipe

Luiz arrives at his combination pairings again using two loops, this time using a for loop nested in a while. The first items in the list is successively paired with the remaining individual elements, and afterwards shift is employed to discard the first element. The loop then restarts with the shortened list.

    sub sum_bitwise {
        my (@numbers, $sum);

        @numbers = @_;

        $sum = 0;
        while (scalar @numbers != 0) {
        	my ($first);

        	$first = $numbers[0];
        	for my $i (1..$#numbers) {
        		$sum += $first & $numbers[$i];
        	}

        	shift @numbers;
        }

        return $sum;
    }

Ryan Thompson

  additional languages: Raku

  blog writeup: PWC 163 › A tail of two sums - Ryan J Thompson

The esteemed Mr. Thompson has figured out a way of using two nested map functions, applying the bitwise operator across arrays of indices, and referencing the array directly from the argument stack. This makes for a very tight, compact solution.

    say sum_bitwise(@ARGV);

    sub sum_bitwise {
        sum map {
            my $i = $_;
            map {
                $_[$i] & $_[$_]
            } $i+1..$#_
        } 0..$#_
    }

Pete Houston

All of the examples we’ve seen so far have taken the interpretation of “unique pairs” as unique indices within the array — each element is paired with every other element exactly once. One the other hand, this directive could also be taken to mean unique values, and on re-inspection there is no specific restriction on the values other than they should be positive. They don’t even need to be integers.

Here Pete filters his input list using the uniqint() function from the core module List::Util. This removes duplicate values from the list, whist coercing them to integer form at the same time. This last part isn’t strictly necessary as this will happen anyway — bitwise operations don’t really make sense with floats. There is no retriction that we use integers, however although the operator will accept a real number it will quietly truncate it before processing.

Given the list (3,3,3), the filter will only pass the single value (3). As there is no second value to pair it to the result of the sum remains 0.

    use List::Util 1.55 'uniqint';
    use Math::Combinatorics;

    my $combo = Math::Combinatorics->new (
        count => 2,
        data  => [uniqint @ARGV]
    );

    my $tot = 0;
    while (my @c = $combo->next_combination) {
        $tot += $c[0] & $c[1];
    }

E. Choroba

Choroba, on the gripping hand, gives a third reading of unique: that any pair of values is allowed, but only once. Thus in evaluating the list (3,3,3), the result will be a single counting of the expression (3 & 3), resulting in binary 11, or decimal 3.

    sub sum_bitwise_operator {
        my $sum = 0;
        my %seen;
        for my $i (1 .. $#_) {
            for my $j (0 .. $i - 1) {
                my @pair = sort { $a <=> $b } @_[$i, $j];
                $sum += $pair[0] & $pair[1] unless $seen{"@pair"}++;
            }
        }
        return $sum
    }

Niels van Dijke

And finally, as though we don’t already have enough, Niels brings yet another ambiguous reading to the table in the first of his two competing interpretations, which themselves give differing results in certain cases. For the two original task examples the results, however, are the same.

So what has Niels done?

Here he presents (1,3) and (3,1) as completely different combinations. That’s novel. With a different operation, say division, these would indeed produce different outcomes. One could I suppose argue that the fact that the bitwise AND just happens to be commutative could be viewed as circumstantial and irrelevant. It’s an interesting take, certainly. “Combinations” does not need to be taken in a literal mathematical sense, either, but could be viewed more colloquially. This technique returns 3 for the input (3,3,3), allowing one single group of (3,3) to be evaluated.

Niels’ second submission is in my eyes a little more justifiable in its interpretation, with the removal of duplicate values from the input list. Padding the input with a couple of 0s ensures a real result no matter what happens in the filtration. In this take, (3,3,3) returns that 0, as the expressions eventually evaluated are (0 & 0), (0 & 3), and (0 & 3), which sum as 0 + 0 + 0.

    # Assumption on andSum1:

    #

    # Unique combinations of (1,1,3) results in:

    #

    #    (1 & 1) + (1 & 3) + (3 & 1)

    #

    # as left hand side and right hand side of the '&' is three time q unique combination

    #

    # Note: Add (0,0) to always have two harmless values in the list

    sub andSum1 {
      return sum map{$_->[0]&$_->[1]}combinations([0,0,singleton(@_),map{($_)x2}duplicates(@_)],2);
    }

    # andSum2 is a more simplistic algorithm:

    # Work on the unique numbers given (remove duplicates)

    #

    # Note: Add (0,0) to always have two harmless values in the list

    sub andSum2 {
      return sum map{$_->[0]&$_->[1]}combinations([0,0,uniq@_],2);
    }

Blogs and Additional Submissions in Guest Languages for Task 1:

Adam Russell

  additional languages: Java, Prolog

  blog writeup: Bitwise And Sums and Skip Summations: Somewhat Complicated Uses of Map - Perl - RabbitFarm

  blog writeup: Bitwise And Sums and Skip Summations: Somewhat Complicated Uses of Map - Prolog - RabbitFarm

Arne Sommer

  additional languages: Raku

  blog writeup: Sum and Sum Again with Raku and Perl - Arne Sommer

Athanasius

  additional languages: Raku

Colin Crain

  additional languages: Raku

  blog writeup: That’s Some Operator You Got There - Programming Excursions in Perl and Raku

Dave Jacoby

  blog writeup: Sum(mation)s: The Weekly Challenge #163 | Committed to Memory

Duncan C. White

  additional languages: C, Pascal

Flavio Poletti

  additional languages: Raku

  blog writeup: PWC163 - Sum Bitwise Operator - ETOOBUSY

Jaldhar H. Vyas

  additional languages: Raku

  blog writeup: Perl Weekly Challenge: Week 163

James Smith

  blog writeup: Perl Weekly Challenge #163

Laurent Rosenfeld

  additional languages: Raku

  blog writeup: Perl Weekly Challenge 163: Sum Bitwise Operator and Summations

Peter Campbell Smith

  blog writeup: Lot of ands and a strange grid

PokGoPun

  additional languages: Go

Ulrich Rieke

  additional languages: C++, Haskell, Raku

W. Luis Mochan

  blog writeup: Perl Weekly Challenge 163 – W. Luis Mochán



TASK 2

Summations

Submitted by: Mohammad S Anwar

You are given a list of positive numbers, @n.

Write a script to find out the summations as described below.

Example 2

    Input: @n = (1, 2, 3, 4, 5)
    Output: 42

        1  2  3  4  5
           2  5  9 14
              5 14 28
                14 42
                   42

The nth Row starts with the second element of the (n-1)th row. The following element is sum of all elements except first element of previous row. You stop once you have just one element in the row.

Example 2

    Input: @n = (1, 3, 5, 7, 9)
    Output: 70

        1  3  5  7  9
           3  8 15 24
              8 23 47
                23 70
                   70

about the solutions

Adam Russell, Alexander Pankoff, Ali Moradi, Arne Sommer, Athanasius, Cheok-Yin Fung, Colin Crain, Dave Jacoby, Duncan C. White, E. Choroba, Flavio Poletti, Jaldhar H. Vyas, James Smith, Jorg Sommrey, Julien Fiegehenn, Laurent Rosenfeld, Lubos Kolouch, Luiz Felipe, Matthew Neleigh, Mohammad S Anwar, Niels van Dijke, Pete Houston, Peter Campbell Smith, PokGoPun, Roger Bell_West, Ryan Thompson, Ulrich Rieke, and W. Luis Mochan

For our second task this week we are again beset with a task that offers up no immediate overarching purpose. In light of this the job becomes first to figure out first what it is exactly we are being asked to do, and then to enact the process, and finally to print the results. Each of these steps provides its own set of challenges.

The reduction itself is an amalgam of several steps, repeated until a single value remains. The relationship between the input list and the final value produced is not immediately obvious, nor its utility. But again, like the previous task, this is of no mind. We don’t need to know why to do what we need to do. It’s generally helpful to know, of course, especially to resolve ambiguities in the challenge description, but we can alway power through without these extra details. And, you know, hope for the best.

Once we have the steps of the process figured out, we need to construct a machine to accomplish them, repeating the pattern until we arrive at the final state of having a single value in our list. As is often the case, once we have really wrapped our heads around the complexities of the process requested, the technical implementation comes almost as an afterthought, and all we need to do is install the parts in the right places to do what we need to do — the design work has already been done.

I think that’s why I liked this challenge so much, that this whole process taken as a whole models and simulates real-world problem solving so well: the real problem to be solved is figuring out what the problem is, and once that’s done the implementation is often incidental.

The third, optional aspect of the task is the graphical display of the data processing. Because the connection between the input and output is, as we have said, not obvious, checking and tuning the algorithm is complicated. Looking at the results for an arbitrary list, how do we know what the right answer even is?

By constructing the triangular list reduction graphically we can look to see if we’re summing the correct values and carrying them forward as directed. Creating strings that when printed form the right-aligned columns is a clever bonus challenge in itself. Some people took it on themselves to present the data this way, which I must say in more interesting output than a single value, as it provides valuable context on how we arrived there.

There were 28 submissions for the second task this past week.

a SPIKE, a WEDGE, and a PIECE of the PIE

Matthew Neleigh, Peter Campbell Smith, Ulrich Rieke, Jaldhar H. Vyas, Jorg Sommrey, W. Luis Mochan, Dave Jacoby, Flavio Poletti, Athanasius, Colin Crain, and Julien Fiegehenn

Figuring out the rules of the summation, as I said before, proved to be the most complicated part of this task. We saw a couple of approaches to the summing steps, either continuously re-summing at each position or reusing the previous sum and updating it with each new element in a row.

Printing the triangular table was not required but many people chose to do it anyways, for the challenge. Getting the individual columns correctly spaced and aligned was a bit tricky, but Perl’s extensive string-processing talents allowed several submissions to make pleasing graphical output with a moderate amount of extra efort.

Matthew Neleigh

Matthew seems to relish the technical challenges of reproducing the exact results presented in the examples section of the task — commentary, notes and all. As such it makes sense to start big with his demonstration.

The reduction itself does not easily lend itself to words. We start with a list of elements. At each iteration the very first thing we do is remove the first element from consideration, shortening the list by 1. Thus after a number of iterations the list will always eventually only contain one remaining element.

The second step of the process is to create a new list, with each element replaced by the sum of all elements from the most-recently shortened list from the 0 index up to and including the element. So the first element remains unchanged, the second becomes the sum of the first and second, the third the sum of the first, second and third. This is completed for all the elements in the shortened list.

For the third step we replace the previous list with the new and repeat the process. By trimming the list one element every time we go around, we will, as we said, eventually and inevitably only have one element left.

    Input: @n = (2, 4, 50, 83, 6, 5, 9)
    Output: 4710

            2    4   50   83    6    5    9
                 4   54  137  143  148  157
                     54  191  334  482  639
                         191  525 1007 1646
                              525 1532 3178
                                  1532 4710
                                       4710

To do the summation, Matthew does a couple of things. First, he switches up the logic to avoid recomputing the same sums for each new element. As each element in the new list is the sum of the previous elements plus the element itself, and the sum of the previous element is the sum of all elements previous to that, then we can recursively redefine the summation step to be each element, working from the left, as the previous element sum plus itself. We re-use the previous calculation as a partial sum for the next calculation, and in the creation of each row each element is only added once.

Matthew then wraps this stepwise summation in recalculating the list into a recursive framework, with the function calling itself and trimming one element at each instance until the exit case is found, when a single value remains.

    sub compute_list_summation{
        my $global_listref = shift();

        my @stage_list;

        # Loop over the last N-1 numbers in the list

        # and add the running total to a new list

        shift();
        push(@stage_list, $ARG[0]);
        for my $i (1 .. $#ARG){
            push(@stage_list, $stage_list[$#stage_list] + $ARG[$i]);
        }

        # Store the list if requested to do so

        push(@{$global_listref}, \@stage_list)
            if(defined($global_listref));

        # If there was just one sum generated, return

        # it

        return($stage_list[0])
            if(scalar(@stage_list) == 1);

        # Otherwise call ourselves on the sum list,

        # return what the deeper call found

        return(compute_list_summation($global_listref, @stage_list));

    }

For output, the intermediate arrays are preserved in a global variable. Creating the triangular grid is tricky, as it needs to be padded from the left to align properly. A little math on the final result value tells us how large to make the columns.

    # See how many digits we'll need to handle...

    $digits = ceil(log($summation) / log(10));

    # Stick the input list at the head of the list of

    # summation stages and output them in a pretty

    # fashion

    unshift(@{$listref}, $list);
    foreach(@{$listref}){
        printf(
            "%".(4 + scalar(@{$list}) * ($digits + 1))."s\n",
            join(" ", map(sprintf("%".$digits."d", $_), @{$_}))
        );
    }
    print("\n");

Peter Campbell Smith

  blog writeup: Lot of ands and a strange grid

As we know the size of the input list, an open-ended recursion model is not required; rather a fixed loop can be substituted. Or in Peter’s case a pair of nested loops, to loop through the list-shortening iterations and then internally across the elements in the lists. He also chooses to just construct a single multidimensional array that closely models the output. Using the same trick of using the previous calculation as a partial sum for the next, the value substitutions can be done in-place as the triangular grid is first computed.

Logarithms are undeniably cool, but here Peter queries Perl as to the length of the reduced value to find his column width. Because in Perl, numbers are strings, and strings are numbers, and problem solving can often be reduced to verbally describing what you want to do. “How long is it?”

Which is as Larry intended it to be.

       $rows = scalar @n;

       # create a grid of the output

       for $row (0 .. $rows - 1) {
       	for $col (0 .. $rows - 1) {

       		# the first row is just the input

       		if ($row == 0) {
       			$grid[0][$col] = $n[$col];

       		# any cell where $col < $row is blank - so make it 0 for now

       		} elsif ($col < $row) {
       			$grid[$row][$col] = 0;

       		# all the other cells are the sum of the cell to the left and the cell above

       		} else {
       			$grid[$row][$col] = $grid[$row - 1][$col] + $grid[$row][$col - 1];
       		}
       	}
       }

       # for a nice output the width of each column is the width of the final cell + 1

       $width = length($grid[$rows - 1][$rows - 1]) + 1;

       # output the result - padded to $width, or if zero, just $width spaces

       say '';
       for $row (0 .. $rows - 1) {
       	for $col (0 .. $rows - 1) {
       		print $grid[$row][$col] ? sprintf("\%${width}d", $grid[$row][$col]) : (' ' x $width);
       	}
       	say '';
       }

Ulrich Rieke

  additional languages: C++, Haskell, Raku

Not all the solutions were this complicated, not did they need to be. An output number was all that was requested, and once you wrap your head around what is required the steps are relatively straightforward. Here Ulrich uses an array slice to select only the elements he wishes to sum in building his new list, which is then substituted for the old abd reprocessed. It’s quite elegant, really.

    my @output = @numbers ;  ## processed input string

    while ( scalar( @output ) != 1 ) {
      @output = createOutputList( \@output ) ;
    }
    say $output[0] ;

    sub createOutputList {
      my $input = shift ;
      my $len = scalar( @{$input} ) ;
      my @output ;
      for my $current( 1 .. $len - 1 ) {
          push @output , sum( @{$input}[1 .. $current] ) ;
      }
      return @output ;
    }

Jaldhar H. Vyas

  additional languages: Raku

  blog writeup: Perl Weekly Challenge: Week 163

Analogous to the dynamic construction we saw before, using partial sums, we can further compress the solution to incorporate this technique.

Here Jaldhar builds a new list, calculating each value by summing a running total and the previous list’s value at the position. A swap using an array slice again and we repeat until finished.

    my @previous = @n[1 .. $#n];
    my $total;

    while (scalar @previous) {
        $total = $previous[0];
        my @next = ( $total );

        for my $i (1 .. scalar @previous - 1) {
            $total += $previous[$i];
            push @next, $total;
        }

        @previous = @next[1 .. $#next];
    }

    say $total;

Jorg Sommrey

Several times now I have referred to our list summation as a “reduction” without explanation. For those unfamiliar, beyond the hopefully clear commonplace meaning I’m using the word in a technical sense as well: a process of taking a list of elements and reducing the values to a single result is known in functional programming as reduction, and this is indeed what we are doing here. Specifically a reduction works by taking a single element from a list, and then applying some sort of function that that takes this original value and the next list element as an argument, preserving the running result to apply to the next element in the list. When we run out of elements, the result is returned.

Simple list summation can be viewed as a reduction: take the first element, and add the second, storing the result, and then, using the stored result adding the third and continuing until we reach the end of the list. Similarly a max function would replace the stored result with the new element if it was greater, otherwise keeping it intact. In all cases it is the stored result that is returned in the end.

The core List::Util module supplies a reduce() function, that when configured with various function blocks can be used as an archetype of all manner of listwise processing. However it also provides a variant form, reductions() which under the correct circumstances can be extremely handy. Instead of only returning the single final stored result, reductions returns a list of all of the stored values collected when traversing the input, with the final element the reduction total.

If you visualize this you can see the list of partial reductions is the same as the list of partial sums we are creating as we work across our input. Here Jorg calls reductions using a while loop as a statement modifier. The applied block adds $a, the running stored value, to $b, the next element, selecting elements using the now-familiar array slice that only passes the list tail.

Written this way the logic is extremely compact, essentially a single line of processing.

    sub triangular_sum (@n) {
        # Calculate the running sums over the array starting with the second

        # element until the array becomes a singleton.

        @n = reductions {$a + $b} @n[1 .. $#n] while @n > 1;

        $n[0];
    }

W. Luis Mochan

  blog writeup: Perl Weekly Challenge 163 – W. Luis Mochán

Luis takes a unique mathematical approach to the summation. One could say that instead of repeatedly reducing the list, he sets his eyes wider, on reducing the sets of equations used to compute the result.

The requested processing, Luis notes, is all an elaborate summation created from the values of the input list. At each iteration, elements from the list are substituted with partial sums already created from the list, resulting in each element being added to the final result multiple times depending on its positioning within the original list. The number of times each number is represented can be computed, and an equation built to directly compute the result from the input.

Instead of performing the multiple summations, Luis creates a function f(), that directly calculates the coefficients for this equation.

Please have a look at his writeup of the method. It’s fascinating.

    my @in  = reverse split /\s+/;
    my $N   = @in-1;
    my $sum = 0;
    $sum += f($N, $_) * $in[$_] for(0..$N);
    say "Input: $_ Output: $sum";

    sub f {
        my ($n,$m) = @_;
        return 1 if $m == 0;
        return 0 if $n == $m;
        f($n-1, $m) + f($n,$m-1);
    }

Dave Jacoby

  blog writeup: Sum(mation)s: The Weekly Challenge #163 | Committed to Memory

Dave described his submission as “just workmanlike”. I have included it here out of respect for all of us who make stuff for a living.

Many years ago, I typified Perl programmers as “the new quiltmakers”. All day long I would solve puzzles, as tiny pieces of code, perfect squares that I would then stich together into sometimes vast tapestries. A CGI interface here, a DBI backend there, documents being parsed out from exotic file formats over next door — all to populate the nascent world wide web. Grey-Eyed Athena was my personal guide, Goddess of wisdom, electricity and weaving. How perfect was that? She’s still my favorite, hands down.

    sub summations ( @n ) {
        my $max = -1 + scalar @n;
        my @x   = map {
            [ map { '' } 0 .. $max ]
        } 0 .. $max;
        $x[0]->@* = @n;

        for my $i ( 1 .. $max ) {
            for my $j ( $i .. $max ) {
                my $left  = $x[$i][ $j - 1 ] || 0;
                my $above = $x[ $i - 1 ][$j] || 0;
                $x[$i][$j] = $left + $above;
            }
        }
        return $x[$max][$max];
    }

Flavio Poletti

  additional languages: Raku

  blog writeup: PWC162 - Summations - ETOOBUSY

Flavio has produced an extremely compact version of a couple of nested loops for us. We’ve seen this pattern before, but it has had an impressive amount of fat removed. In his own words, he fell back to this technique after exporing some more sophisticated avenues. “My brute force beast was screaming inside.”

    sub summations (@n) {
       for (2 .. $#n) { $n[$_] += $n[$_ - 1] for $_ .. $#n }
       return $n[-1];
    }

Athanasius

  additional languages: Raku

The monk delivers us another carefully crafted solution with optional verbose output, the way it should be. Life should have choices, you know? It keeps everything from being the same: out of control. Don’t ask me how I know that.

But anyway, by necessity when we might wish to reconstruct our work after the fact we need to keep notes along the way; here the triangle is built as a multidimensional array-of-arrays, and to return the result we grab the first element of the last row.

    while (scalar @last > 1)
    {
        push @next, $next[ $_ - 2 ] + $last[ $_ ] for 2 .. $#last;
        push @triangle,  [ @next ];

        @last = @next;
        @next = $last[ 1 ];
    }

    printf "Output: %d\n", $triangle[ -1 ][ 0 ];

Colin Crain

  additional languages: Raku

  blog writeup: And a Little Something More… - Programming Excursions in Perl and Raku

For my own solution, after figuring out the process I was left unsatisfied looking at a simple number, and so I decided to graft on a pretty-printing routine, which I made possible with the introduction of a new global variable, our @stack. Then inside the while loop, every time we compute a new array a copy is made over onto the stack. Later we can retrieve it if requested, where individual array rows are printed using a carefully constructed sprintf. It seems I’ve become so addicted to the convenience of say that I’d rather leave the linefeeds to that statement, instead of incorporating something into the formatting of a printf. It’s much cleaner in my eyes, and presumably more portable to boot.

    our @stack;

    sub trisum (@list) {
        my $sum;
        my @new;
        @stack = [@list];

        while (@list > 1) {
            @new = ();
            $sum = 0;

            shift @list;
            for (keys @list) {
                $sum += $list[$_];
                push @new, $sum;
            }
            push @stack, [@new];
            @list = @new;
        }
        print_stack_and_flush() if VERBOSE;

        return shift @new;
    }


    sub print_stack_and_flush {
        my $elements   = @stack;
        my $max_digits = length $stack[-1]->[0];
        my $fmt = ('%' . ($max_digits + 1) . 's') x $elements;

        say "\ntriangular reduction steps:\n";

        for my $row (@stack) {
            unshift $row->@*, '' while $row->@* < $elements;
            say sprintf $fmt, $row->@*;
        }

        say "sum  $stack[-1]->[-1]\n";

        @stack = ();
    }

Julien Fiegehenn

Finally we’ll end with Julien, who makes a curious little observation I don’t recall seeing elsewhere: that if we’re going to trim the first element in any case, we can in actuality stop when we have two elements, and just take the second. How about that?

Yea, it’s minor, but I find it funny. Perhaps I need more sleep.

Kudos to Julien — his comments tell the story.

    sub foo {
        my @rows;
        $rows[0] = [@_];

        # we'll keep going as long as we have more than 2 elements left

        while ( @{ $rows[-1] } > 2 ) {
            push @rows, [

                # we always start with the 2nd element of the previous row

                $rows[-1]->[1],

                # each following element is the sum of all numbers from the

                # 2nd element until the position (not index, but position in

                # the tabular view above) we are currently filling, of

                # the previous row.

                map { sum @{ $rows[-1] }[ 1 .. $_ ] } 2 .. $#{ $rows[-1] }
            ];
        }

        # We're supposed to stop and return when we only have one element left,

        # but it is actually easier to stop at two elements left. The last row

        # is just the 2nd element of the row before, so we can return that directly.

        return $rows[-1]->[-1];
    }

Blogs and Additional Submissions in Guest Languages for Task 2:

Adam Russell

  additional languages: Java, Prolog

  blog writeup: Bitwise And Sums and Skip Summations: Somewhat Complicated Uses of Map - Perl - RabbitFarm

  blog writeup: Bitwise And Sums and Skip Summations: Somewhat Complicated Uses of Map - Prolog - RabbitFarm

Alexander Pankoff

  additional languages: Haskell

Ali Moradi

  additional languages: C, C++, Lua, Pascal

Arne Sommer

  additional languages: Raku

  blog writeup: Sum and Sum Again with Raku and Perl - Arne Sommer

Cheok-Yin Fung

  additional languages: Julia, Python

Duncan C. White

  additional languages: C, Pascal

James Smith

  blog writeup: Perl Weekly Challenge #163

Laurent Rosenfeld

  additional languages: Raku

  blog writeup: Perl Weekly Challenge 163: Sum Bitwise Operator and Summations

PokGoPun

  additional languages: Go

Roger Bell_West

  additional languages: Javascript, Kotlin, Lua, Postscript, Python, Raku, Ruby, Rust

  blog writeup: RogerBW’s Blog: The Weekly Challenge 163: Sums and Sums

Ryan Thompson

  additional languages: Raku

  blog writeup: PWC 163 › A tail of two sums - Ryan J Thompson



 

 

 

 

_________ THE BLOG PAGES _________


That’s it for me this week, people! Warped by the rain, driven by the snow, resolute and unbroken by the torrential influx, by some miracle I somehow continue to maintain my bearings.

Looking forward to next wave, the perfect wave, I am: your humble servant.

But if Your Unquenchable THIRST for KNOWLEDGE is not SLAKED,

then RUN (dont walk!) to the WATERING HOLE

and FOLLOW these BLOG LINKS:

( …don’t think, trust your training, you’re in the zone, just do it … )

Adam Russell

Arne Sommer

Colin Crain

Dave Jacoby

Flavio Poletti

Jaldhar H. Vyas

James Smith

Laurent Rosenfeld

Luca Ferrari

Mark Senn

Peter Campbell Smith

Roger Bell_West

Ryan Thompson

W. Luis Mochan

SO WHAT DO YOU THINK ?

If you have any suggestions or ideas then please do share with us.

Contact with me