2nd class join
For challenge #137.1 we are looking for long years. We can implement the algorithm as described in Wikipedia (and ignore that Dateish
got .week-number
to have a reason for showing off with junctions).
multi sub infix:«|,»(\left, \right) is equiv(&infix:<Z>) { |left, |right }
say (1900..2100).grep({ Date.new(.Int, 1, 1).day-of-week | Date.new(.Int, 12, 31).day-of-week == 4 });
# OUTPUT: 1903 1908 1914 1920 1925 1931 1936 1942 1948 1953 1959 1964 1970 1976 1981 1987 1992 1998 2004 2009 2015 2020 2026 2032 2037 2043 2048 2054 2060 2065 2071 2076 2082 2088 2093 2099
The output doesn’t look too nice. It would be better to group the years in column.
put ( (1900..2100).grep({ Date.new(.Int, 1, 1).day-of-week | Date.new(.Int, 12, 31).day-of-week == 4 }) Z (' ' xx 7 |, $?NL) |xx ∞ ).flat.head(*-1).join('');
# OUTPUT: 1903 1908 1914 1920 1925 1931 1936 1942
1948 1953 1959 1964 1970 1976 1981 1987
1992 1998 2004 2009 2015 2020 2026 2032
2037 2043 2048 2054 2060 2065 2071 2076
2082 2088 2093 2099
That is pretty long and convoluted. The reason why I need Z
with an infinite list and have to remove the last redundant element (either a newline or space) is that .join
isn’t all that smart. Let’s build a smarter function.
multi sub smart-join(Seq:D \separator, *@l --> Str:D) {
my $ret;
my $sep-it = separator.iterator;
my $list-it = @l.iterator;
loop {
my $e := $list-it.pull-one;
last if $e =:= IterationEnd;
$ret ~= $e;
$ret ~= $sep-it.pull-one if $list-it.bool-only;
}
$ret
}
multi sub infix:«|xx»(Mu \left, Mu \right) is equiv(&infix:<xx>) { (left xx right).flat }
1900..2100
==> grep({ Date.new(.Int, 1, 1).day-of-week | Date.new(.Int, 12, 31).day-of-week == 4 })
==> smart-join( (' ' xx 7 |, $?NL) |xx ∞ )
==> say();
Now we can use (the sadly under-used) feed operator. The lazy list that is generating the alternating separators might be a bit slow. If we go functional the code, both for smart-join
and the alternator, gets simpler.
multi sub smart-join(&separators, *@l --> Str:D) {
my $ret;
while @l {
$ret ~= @l.shift;
$ret ~= separators if +@l;
}
$ret
}
1900..2100
==> grep({ Date.new(.Int, 1, 1).day-of-week | Date.new(.Int, 12, 31).day-of-week == 4 })
==> smart-join({ ++$ %% 8 ?? $?NL !! ' '})
==> say();
Right now there is no easy way to add this as a module because sub join
is not a multi
. Since we use Z
with such infinite lists quite often, I believe a proper functional way in CORE
could not hurt. Raku does support functional programming in many places but some sub
s are still 2nd class citizen. It may be reasonable to make a list so I can hand it over to Santa.
UPDATE:
As lizmat noted, sub join
is a multi
already. So there is room for a module.
put (1900..2100).grep({ Date.new(.Int, 1, 1).day-of-week | Date.new(.Int, 12, 31).day-of-week == 4 }).rotor(8, :partial).join(“\n”); #maybe this?