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

Challenge 181

posted at: 08:45 by: Adam Russell | path: /perl | permanent link to this entry