#!/usr/bin/env perl6 use v6; =NAME t2-code =AUTHOR Yary Hluchan =SYNOPSIS t2-code R =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} } }