RabbitFarm
2022-09-11
These Sentences Are Getting Hot
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
Part 1
You are given a paragraph. Write a script to order each sentence alphanumerically and print the whole paragraph.
Solution
use v5.36;
use strict;
use warnings;
sub sort_paragraph{
my($paragraph) = @_;
my @sentences = split(/\./, $paragraph);
for(my $i = 0; $i < @sentences; $i++){
$sentences[$i] = join(" ", sort {uc($a) cmp uc($b)} split(/\s/, $sentences[$i]));
}
return join(".", @sentences);
}
MAIN:{
my $paragraph = do{
local $/;
<DATA>;
};
print sort_paragraph($paragraph);
}
__DATA__
All he could think about was how it would all end. There was
still a bit of uncertainty in the equation, but the basics
were there for anyone to see. No matter how much he tried to
see the positive, it wasn't anywhere to be seen. The end was
coming and it wasn't going to be pretty.
Sample Run
$ perl perl/ch-1.pl
about All all could end he how it think was would. a anyone basics bit but equation, for in of see still the the There there to uncertainty was were. anywhere be he how it matter much No positive, see seen the to to tried wasn't. and be coming end going it pretty The to was wasn't
Notes
This code is fairly compact but not at all obfuscated, I would argue. First we take in the paragraph all at once. Then we split into sentences and begin the sorting.
The sort
is a little complicated looking at first because we want the words to be sorted
irrespective of letter case. One way to handle that is to compare only all uppercase
versions of the words. Lowercase would work too, of course!
Part 2
You are given file with daily temperature record in random order. Write a script to find out days hotter than previous day.
Solution
use v5.36;
use strict;
use warnings;
use DBI;
use Text::CSV;
use Time::Piece;
sub hotter_than_previous{
my($data) = @_;
my @hotter;
my $csv_parser = Text::CSV->new();
my $dbh = DBI->connect(q/dbi:CSV:/, undef, undef, undef);
$dbh->do(q/CREATE TABLE hotter_than_previous_a(day INTEGER, temperature INTEGER)/);
$dbh->do(q/CREATE TABLE hotter_than_previous_b(day INTEGER, temperature INTEGER)/);
for my $line (@{$data}){
$line =~ tr/ //d;
$csv_parser->parse($line);
my($day, $temperature) = $csv_parser->fields();
$day = Time::Piece->strptime($day, q/%Y-%m-%d/);
$dbh->do(q/INSERT INTO hotter_than_previous_a VALUES(/ . $day->epoch . qq/, $temperature)/);
$dbh->do(q/INSERT INTO hotter_than_previous_b VALUES(/ . $day->epoch . qq/, $temperature)/);
}
my $statement = $dbh->prepare(q/SELECT day FROM hotter_than_previous_a A INNER JOIN
hotter_than_previous_b B WHERE (A.day - B.day = 86400)
AND A.temperature > B.temperature/);
$statement->execute();
while(my $row = $statement->fetchrow_hashref()){
push @hotter, $row->{day};
}
@hotter = map {Time::Piece->strptime($_, q/%s/)->strftime(q/%Y-%m-%d/)} sort @hotter;
unlink(q/hotter_than_previous_a/);
unlink(q/hotter_than_previous_b/);
return @hotter;
}
MAIN:{
my $data = do{
local $/;
<DATA>;
};
my @hotter = hotter_than_previous([split(/\n/, $data)]);
say join(qq/\n/, @hotter);
}
__DATA__
2022-08-01, 20
2022-08-09, 10
2022-08-03, 19
2022-08-06, 24
2022-08-05, 22
2022-08-10, 28
2022-08-07, 20
2022-08-04, 18
2022-08-08, 21
2022-08-02, 25
Sample Run
$ perl perl/ch-2.pl
2022-08-02
2022-08-05
2022-08-06
2022-08-08
2022-08-10
Notes
To be clear up front, this is an intentionally over engineered solution! I have been intrigued by the idea of DBD::CSV since I first heard of it but never had a reason to use it. So I invented a reason!
DBD::CSV provides a SQL interface to CSV data. That is, it allows you to write SQL queries against CSV data as if they were a more ordinary relational database. Very cool! Instead of solving this problem in Perl I am actually implementing the solution in SQL. Perl is providing the implementation of the SQL Engine and the quasi-database for the CSV data.
DBD::CSV is quite powerful but is not completely on par feature wise with what you'd get
if you were using an ordinary database. Not all SQL data types are supported, for example.
Work arounds can be constructed to do everything that we want and these sorts of trade
offs are to be expected. To store the dates we use Time::Piece
to compute UNIX epoch
times which are stored as INTEGERs. Also, DBD::CSV expects data from files and so we can't
use the data directly in memory, it has to be written to a file first. Actually, we find
out that we need to create two tables! Each hold exact copies of the same data.
The creation of two tables is due to a quirk of the underlying SQL Engine SQL::Statement.
SQL::Statement will throw an error when doing a join on the same table. The way one would
do this ordinarily is something like
SELECT day FROM hotter_than_previous A, hotter_than_previous B ...
. That join allows SQL
to iterate over all pairs of dates but this throws an error when done with SQL::Statement.
To work around this we instead we create two tables which works.
References
posted at: 08:45 by: Adam Russell | path: /perl | permanent link to this entry