Hot Sentence
with Raku and Perl

by Arne Sommer

Hot Sentence with Raku and Perl

[201] Published 11. September 2022.

This is my response to The Weekly Challenge #181.

Challenge #181.1: Sentence Order

You are given a paragraph.

Write a script to order each sentence alphanumerically and print the whole paragraph.

Example:
Input:
    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.

Ouput:
    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.

I think we can safely assume that the indentation is for presentation purposes only, and not to be taken literally. If we also assume that the newlines are equally unimportant, we can get away with a very compact program.

File: sentence-order
#! /usr/bin/env raku

unit sub MAIN ($file where $file.IO.f && $file.IO.r = "paragraph.txt",
               :v(:$verbose));

my @sentences = (slurp $file).split(".");  # [1]

say ":sentences: { @sentences.raku}" if $verbose;

say @sentences>>.words>>.sort({ $^a.lc cmp $^b.lc })>>.join(" ").join(". ");
 # ## 2 ############## # 3 ######################## # 4 ####### # 5 ####### 

[1] Read the file (with slurp), and split into an array on . (a period). The period is removed.

See docs.raku.org/routine/slurp for more information about siurp.

See docs.raku.org/routine/split for more information about split.

[2] Split each of the sentences - as we used >>. instead of the normal . (a period) - into words (with word).

See docs.raku.org/routine/words for more information about words.

[3] Sort each of those lists (of words from each sentence), using the custom sort code specified. Note the placeholder variables.

[4] Join each list, now sorted, into a string with a single space between the words.

[5] And finally, join the strings (one for each sentence) with a period and a trailing space.

Verbose mode shows what is going on, partially. (I have added the colouring.)

$ ./sentence-order -v
:sentences: ["All he could think about was how it would all end", " There \
  was\nstill a bit of uncertainty in the equation, but the basics\nwere \
  there for anyone to see", " No matter how much he tried to\nsee the \
  positive, it wasn't anywhere to be seen", " The end was\ncoming \
  and it wasn't going to be pretty", "\n"]
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. 

We got five sentences. The leading spaces and embedded newlines are removed by words. The last one is reduced to an empty list, and the join beytween the lists gives us a trailing newline.

Without verbose mode:

$ ./sentence-order
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. 

Looking good.

As a oneliner(ish):

File: sentence-order-onelineish
#! /usr/bin/env raku

unit sub MAIN ($file where $file.IO.f && $file.IO.r = "paragraph.txt");

(slurp $file).split(".")>>.words>>.sort({ $^a.lc cmp $^b.lc })>>.join(" ") \
  .join(". ").say;

Or as a true oneliner (if you ignore the crash-bang line), ditching the error handling:

File: sentence-order-oneliner
#! /usr/bin/env raku

(slurp @*ARGS[0] || "paragraph.txt").split(".")>>.words \
  >>.sort({ $^a.lc cmp $^b.lc })>>.join(" ").join(". ").say;

A Perl Version

This is straight forward translation of the Raku version.

File: sentence-order-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';
use Getopt::Long;
use File::Slurp;

my $verbose = 0;

GetOptions("verbose"  => \$verbose);

my $file      = shift(@ARGV) || "paragraph.txt";
my $content   = read_file($file);
my @sentences = split(/\./, $content);

say ":sentences: " . join("\n", map { '"' . $_ . '"' } @sentences)
  if $verbose;

my @result;

for my $sentence (@sentences)
{
  my @words = split(/\s+/, $sentence);
  push(@result, join(" ", sort { lc $a cmp lc $b } @words));
}

say join(". ", @result);

Running it gives the same result as the Raku version:

$ ./sentence-order-perl
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. 

Challenge #181.2: Hot Day

You are given file with daily temperature record in random order.

Write a script to find out days hotter than previous day.

Example:
Input File: (temperature.txt)

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

Output:
2022-08-02
2022-08-05
2022-08-06
2022-08-08
2022-08-10

File: hot-day
#! /usr/bin/env raku

unit sub MAIN ($file where $file.IO.f && $file.IO.r = "temperature.txt",
               :v(:$verbose));

my @rows = $file.IO.lines;                            # [1]

my %measures;                                         # [2]

for @rows -> $row                                     # [3]
{
  my ($date, $temperature) = $row.split(", ");        # [3a]

  %measures{$date} = $temperature;                    # [3b]
}

for sort keys %measures -> $date                      # [4]
{
  my $prev      = $date.Date.pred;                    # [5]
  my $temp      = %measures{$date};                   # [6]
  my $prev_temp = %measures{$prev};                   # [7]

  print ": Checking Date: $date (temp: $temp)" if $verbose;

  if $prev_temp.defined                               # [8]
  {
    say " - $prev (temp $prev_temp)" if $verbose;
    say $date if $temp > $prev_temp;                  # [8a]
  }
  elsif $verbose
  {
    say "";
  }
}

[1] Read the daily temperature file.

[2] We ar going to store the temperatures here, with the date as key.

[3] For each row, split the row into the date and the temperature [3a], and add them to the hash [3b] (from [2]).

[4] For each date (sorted with the oldest first).

[5] Get the previuos date, using a Date object and the previous method.

See docs.raku.org/type/Date for more information about the Date class.

[6] Get the temperature for the current day.

[7] and the previous one.

[8] If the previous day's temperature is defined, print the date if the current temperature is higher than this one [8a].

Running it:

$ ./hot-day
2022-08-02
2022-08-05
2022-08-06
2022-08-08
2022-08-10

Looking good.

With verbose mode:

$ ./hot-day -v
: Checking Date: 2022-08-01 (temp: 20)
: Checking Date: 2022-08-02 (temp: 25) - 2022-08-01 (temp 20)
2022-08-02
: Checking Date: 2022-08-03 (temp: 19) - 2022-08-02 (temp 25)
: Checking Date: 2022-08-04 (temp: 18) - 2022-08-03 (temp 19)
: Checking Date: 2022-08-05 (temp: 22) - 2022-08-04 (temp 18)
2022-08-05
: Checking Date: 2022-08-06 (temp: 24) - 2022-08-05 (temp 22)
2022-08-06
: Checking Date: 2022-08-07 (temp: 20) - 2022-08-06 (temp 24)
: Checking Date: 2022-08-08 (temp: 21) - 2022-08-07 (temp 20)
2022-08-08
: Checking Date: 2022-08-09 (temp: 10) - 2022-08-08 (temp 21)
: Checking Date: 2022-08-10 (temp: 28) - 2022-08-09 (temp 10)
2022-08-10

Perl

This is a straight forward translation of the Raku version.

File: hot-day-perl
#! /usr/bin/env perl

use strict;
use warnings;
use feature 'say';
use Getopt::Long;
use File::Slurp;
use Date::Calc;       # [1]

my $verbose = 0;

GetOptions("verbose"  => \$verbose);

my $file = shift(@ARGV) || "temperature.txt";
my @rows = read_file($file, chomp => 1);

my %measures;

for my $row (@rows)
{
  my ($date, $temperature) = split(", ", $row);

  $measures{$date} = $temperature;
}

for my $date (sort keys %measures)
{
  my $prev      = sprintf("%04d-%02d-%02d",
                      Date::Calc::Add_Delta_Days(split("-", $date), -1));

  my $temp      = $measures{$date};
  my $prev_temp = $measures{$prev};

  print ": Checking Date: $date (temp: $temp)" if $verbose;

  if (defined $prev_temp)
  {
    say " - $prev (temp $prev_temp)" if $verbose;
    say $date if $temp > $prev_temp;
  }
  elsif ($verbose)
  {
    say "";
  }
}

[1] The «Date::Calc» module does not give us Date objects, but working on string dates is ok.

Running it gives the same result as the Raku version:

$ ./hot-day-perl
2022-08-02
2022-08-05
2022-08-06
2022-08-08
2022-08-10

And that's it.