t2/yary

Download the raw code.

#!/usr/bin/env perl6

use v6;

=NAME t2-code

=AUTHOR Yary Hluchan

=SYNOPSIS
    t2-code R<count_to_find>

=begin DESCRIPTION
Shows first N sums of 2 cubes, where N is given on the command line.
This program is a solution to Task 2 of the 2011 Perl6 Code Contest.

It does this by building finite streams, S₁, S₂, S₃... where Sq
represents the sums q³+1³, q³+2³, q³+3³...q³+q³

By consuming the least sum from each stream, and creating a new stream
when consuming the first element of the greatest stream, it creates a
never-decreasing stream of sums.

=end DESCRIPTION

#= Represent a sum of cubes in a human-readable string vs. a numeric result
class sum_of_cubes {
   has Int $computed;
   has Str $readable;

   # Rely on the caller to tell us the values
   # All we do is return the result in numeric context, and the
   # calculation in string context.

   method new(:$computed, :$q, :$r) {
     self.bless(*,
                computed=> $computed,
            readable=> "$q ** 3 + $r ** 3"
           )
   }

   # I'd like to use a "blessall" method, submethod BUILD is not handsome
   submethod BUILD(:$!computed, :$!readable ) { }

   method Str { $readable }

   method Numeric { $computed }
}


#= Stream of sums of cubes q³+1³, q³+2³
class sum_of_cubes_stream {
    # I would like to implement this as an iterator, but the array is so easy.
    # Sorry for thinking in P5 :-/
    has Int $.q;
    has @stream_of_sums;

    # Took me some time on #perl6 to learn how to initialize both
    # public and private attributes, without using named attributes
    # in "new". I hope "blessall" becomes reality soon.
    submethod BUILD(:$!q, :@!stream_of_sums) { }

    method new(Int $q) {
           self.bless(*,
        :q( $q ),
        stream_of_sums => (1..$q) »**» 3 »+» $q**3
       )
    }

    #= Have we consumed anything from this stream yet?
    method is_fresh { @stream_of_sums == $.q };

    #= Peek at the current sum from this stream- or 0 if empty
    method current { @stream_of_sums[0] // 0 };

    #= Get the current element and prime for the next one
    method consume {
      # Here, "r" relies on arguments being evaluated left-to-right
      sum_of_cubes.new(
          computed=>shift(@stream_of_sums),
      :q($.q),
      :r($.q-@stream_of_sums)
      )
    }

}


# I would like to use this declaration, not yet impletmented.
# sub MAIN(Int $count_to_find);

sub MAIN(Int $count_to_find is rw) {
    # Initialize streams collection with 1³+1³
    my @streams=sum_of_cubes_stream.new(1);

    while ($count_to_find) {
      my $min_sum = @streams.map({.current}).min;
      my @result = @streams.grep({.current == $min_sum}).map: {.consume}
      if @result > 1 {
            say (+@result[0],@result).join: ' = ';
        --$count_to_find;
      }

      # Add a new stream if the last stream is not fresh
      unless @streams[*-1].is_fresh {
        push @streams,sum_of_cubes_stream.new(1 + @streams[*-1].q);
      }

      # Remove any empty streams from consideration,
      # by keeping only streams with a value
      @streams=@streams.grep: {.current}
    }
}

Correctness

As far as we were able to test it, this produces the right output.

It also seems to handle the case of more than two ways to write a sum of cubes, even though excessive resource usage prevented us from testing it.

Consistency

Tabs and spaces are used interchangeably, without any fixed rule, and assuming a tabstop of 8.

The actual number of steps indented, even with a tabstop of 8, vary between 2 and 7, with no apparent system behind it.

The conditional of the while loop has unnecessary parentheses, but not those of the if and unless blocks.

Sometimes there are spaces between hash keys ans the => operator, sometimes there aren't. Sometimes there are spaces around the assignment operator, sometimes there aren't.

Clarity of intent

The comments use Unicode characters for great good. In a world where Unicode still isn't the norm, this is to be commended.

Algorithmic complexity

This is a solution that enqueues all sums of cubes, and thus is rather slow.

Idiomatic use of Perl 6

There is much to like here. Classes are used, even though giving them names starting with lower-case is not too common. Pod is even used in places, though they actually don't seem intended for a user of the class API, but for the reader of the code.

Perl 6 connoisseurs will recognize a few missed opportunities to write named arguments more succinctly: computed=> $computed could've been simply :$computed, :q( $q ) could've been :$q, and :q($.q) could've been :$.q.

Brevity

A rather verbose solution.