I've been doing the
Perl Weekly Challenges. This one
dealt with months and word-wrapping.
The first one was to find months with 5 weekends, specified as 5
sequences of Friday, Saturday and Sunday all falling in the same
month. (Actually the specification didn't say "sequences", it just
said "5 Friday, 5 Saturday and 5 Sunday"; if you're allowed to start
with Saturday and Sunday, and end with a Friday, that'll end up being
rather more months.)
This is susceptible to a significant optimisation: it can only happen
in a 31-day month, and only if the first day of that month is a
Friday. And it will happen in every such month.
use Time::Local;
use POSIX qw(strftime);
foreach my $y (1900..2019) {
Pick just the 31-day months.
foreach my $m (1,3,5,7,8,10,12) {
I don't actually care about the unixtime, so I'll throw it away. But
the output of gmtime (localtime's neglected sibling) is what I need
later for strftime.
my @d=gmtime(timegm(0,0,0,1,$m-1,$y));
If the first day of the month is a Friday… (with the looser
specification, Friday, Saturday or Sunday, so 5, 6 and 0):
if ($d[6]==5) {
print strftime('%B %Y',@d),"\n";
}
}
}
The other one is a paragraph wrapper. Obviously we already have
Text::Wrap in CPAN, and indeed Text::LineFold.
use Getopt::Std;
my %o=(w => 72);
getopts('w:',\%o);
my $s=$o{w};
my @w;
while (<>) {
chomp;
I've extended the specification a bit. If we get a blank line, count
that as end of paragraph, and dump any remaining words before it.
if ($_ eq '') {
if (@w) {
print join(' ',@w),"\n";
@w=();
$s=$o{w};
}
print "\n";
Otherwise, it's the standard greedy algorithm: if the word will fit,
fit it, otherwise dump the line buffer and start a new one.
} else {
foreach my $w (split ' ',$_) {
my $lw=length($w);
if ($lw+1 > $s) {
print join(' ',@w),"\n";
@w=($w);
$s=$o{w}-$lw;
} else {
push @w,$w;
$s-=($lw+1);
}
}
}
}
If there's something left at the end, dump that too.
if (@w) {
print join(' ',@w),"\n";
}
In production I'd use a function for the buffer dump, because
otherwise I'd have three separate places to change the code when I
needed to modify it.
I also wrote my first Perl6 programs, to do the same tasks, with much
help from the
Perl 5 to 6 in a nutshell
guide to get me through the various syntax changes. The first
challenge becomes simpler, because there's a built-in Date type,
though it's not immediately obvious how to access strftime natively
(do I really need to pull in DateTime::Format?) so the output is
numeric for now:
for 1990..2019 -> $y {
for 1,3,5,7,8,10,12 -> $m {
if Date.new($y,$m,1).day-of-week == 5 {
say "$m $y";
}
}
}
The second looks very similar to the way it was before, basically just
with syntax changes, though command-line options are also in external
modules and I'm trying to keep this basic.
my $width=72;
my $s=$width;
my @w;
for lines() {
.chomp;
if ($_ eq '') {
if (@w) {
print join(' ',@w),"\n";
@w=();
$s=$width;
}
print "\n";
} else {
for split ' ',$_ -> $w {
my $lw=chars($w);
if ($lw+1 > $s) {
print join(' ',@w),"\n";
@w=($w);
$s=$width-$lw;
} else {
push @w,$w;
$s-=($lw+1);
}
}
}
}
if (@w) {
print join(' ',@w),"\n";
}
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.