-
Notifications
You must be signed in to change notification settings - Fork 0
A program in both Perl 5 and Perl 6
James E Keenan edited this page Mar 29, 2016
·
1 revision
Here is the Perl 5 version of a program used to parse and reformat a plain-text file which originated in a reported downloaded from an old mainframe computer system.
$ cat medhist.pl
#!/usr/bin/perl
# medhist.pl
# Equivalent to mpc.pl, version 0.41 as of 8/21/01
# Must type in name of source file. As written, assumes source file is in
# same directory as script.
# Automatically picks up client 7-digit "C" number
# Output file is named "(7-digit C no.)C.txt" and appears in same directory
# as script.
# No column headers -- just data.
$VERSION = 0.44;
use strict;
use warnings;
use Carp;
my ($cno, $strtdt, $stpdt, $type, $med, $dose, $freq);
##### BEGIN format definition #####
format MPCNYPHREPORT =
@<<<<<< @<<<<<<<<< @<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$cno, $strtdt, $stpdt, $type, $med, $dose, $freq
.
##### END format definition #####
my ($source, $outputdir, $tmpoutput, $oldfh);
croak "Need source file name as command-line argument"
unless (@ARGV >= 1);
$source = shift(@ARGV);
croak "Cannot locate source file '$source'"
unless (-f $source);
if (@ARGV) {
$outputdir = shift(@ARGV);
croak "Cannot locate output directory" unless (-d $outputdir);
}
else {
$outputdir = '.';
}
$tmpoutput = "$outputdir/output.txt";
open(my $IN, '<', $source) || die "cannot open $source for reading: $!";
open(MPCNYPHREPORT, '>', $tmpoutput) || die "cannot create $tmpoutput: $!";
$oldfh = select MPCNYPHREPORT;
$= = 56;
select $oldfh;
my @out1 = ();
while (<$IN>) {
s/^\*+$/----------/;
$cno = $1 if m?\bCASE\s#\s:\s(\d\d\d\d\d\d\d)\s?;
if (/\bFREQ:|^DOCTOR:|^-+$/) {
push (@out1, $_);
}
}
my ($in, @scrips, $md, $order);
foreach $_ (@out1) {
if (/(.*)^-+$/) {
$in .= $1;
}
else {
$in .= $_;
}
}
$in =~ s/(.*)\n(DOCTOR.*)/$1 $2/g;
@scrips = split /\n/, $in;
foreach (@scrips) {
&warn_match_error($_) unless
$_ =~ m/^
(.+\b[.,\/?;:%*&\(\)\[\]]?) # medication
\s+FREQ:\s+(.+)\b # frequency
\s+ORDER\s+\#\s+(\d+) # order number
\s+STARTED\s{3}(\d{2}\/\d{2}\/\d{4}) # start date
\s+DOCTOR:\s+(.+) # doctor
\s+DOSE:\s+(.+) # dose
\s+TYPE\s+(\w+) # type
\s+STOPPED\s{3}(\d{2}\/\d{2}\/\d{4}) # stop date
/x;
$med = $1; $freq = $2; $order = $3; $strtdt = $4;
$md = $5; $dose = $6; $type = $7; $stpdt = $8;
write (MPCNYPHREPORT);
}
close ($IN) || die "can't close $source:$!";
close (MPCNYPHREPORT) || die "can't close $tmpoutput:$!";
my $newname = "$outputdir/$cno" . "C.txt";
rename ($tmpoutput, $newname) || die "Can't rename $tmpoutput to $newname: $!";
print "See results in this file: $newname\n";
sub warn_match_error {
my ($message) = @_;
warn "ERROR: failed to match a record, data was\n" . $message . "\n";
}
Here is the same program recently rewritten in Perl 6:
$ cat medhist.pl6
#!/usr/bin/env perl6
my $source = "/home/jkeenan/learn/perl/medhist/rawdata.txt";
my $cno = '';
my @out1 = ();
for $source.IO.lines <-> $m is rw {
next if $m ~~ m/^\w+$/;
$m ~~ s/^\*+$/----------/;
if $m ~~ m/CASE\s\#\s\:\s(\d**7)/ {
unless $cno.chars {
$cno = $0.Str;
}
}
if $m ~~ m/FREQ\:|^DOCTOR\:|^\-+$/ {
# if ($m ~~ m/FREQ\:|^DOCTOR\:/) {
@out1.push($m);
}
}
my $status = 0;
while (! $status) {
if @out1[*-1] ~~ m/^\-+$/ {
my $l = @out1.pop;
$status = 0;
}
else {
$status = 1;
}
}
die "Intermediate array is faulty" unless $status;
#warn "Intermediate array is status: $status";
my $in = '';
loop (my $i = 0; $i < @out1.elems - 1; $i++) {
if ($i % 3) {
$in ~= @out1[$i] ~ "\n";
}
}
$in ~= @out1[@out1.elems - 1];
$in ~~ s:g/\nDOCTOR/ DOCTOR/;
my $outstr = '';
for ($in.split("\n")) -> $j {
my %this_scrip;
if $j ~~ m/
^(.*?)
\s+FREQ\:\s+
(.*?)
\s+ORDER\s+\#\s+
(\d+)
\s+STARTED\s+
(\d**2\/\d**2\/\d**4)
\sDOCTOR\:\s+
(.*?)
\s+DOSE\:\s+
(.*?)
\s+TYPE\s+
(.*?)
\s+STOPPED\s+
(\d**2\/\d**2\/\d**4)
/ {
%this_scrip< medication frequency order_number start_date doctor dose type stop_date > =
($0, $1, $2, $3, $4, $5, $6, $7).map: { $_.Str };
}
else {
warn "$j: Failed to match";
}
$outstr ~= sprintf("%-8s%-11s%-11s%-36s%-31s%-21s%-s\n",
$cno,
%this_scrip.<start_date stop_date type medication dose frequency>,
);
}
say $outstr.chomp;
Here is the Perl 6 program annotated to show differences in approach between the two languages:
$ cat annotated_medhist.pl6
#!/usr/bin/env perl6
my $source = "/home/jkeenan/learn/perl/medhist/rawdata.txt";
my $cno = '';
my @out1 = ();
#`{{
1. Perl5 uses a filehandle to iterate over lines read from a file: while (my $l = <$IN>) {}.
Perl6 uses: for $filename.IO.lines -> $l {} (where we treat the line as read-only)
or: for $filename.IO.lines <-> $m is rw {} (where we treat the line as modifiable)
2. Note distinction between '->' and '<->'.
}}
for $source.IO.lines <-> $m is rw {
#`{{
3. Perl5 regex binding: =~
Perl6 : ~~
}}
next if $m ~~ m/^\w+$/;
$m ~~ s/^\*+$/----------/;
#`{{
4. Perl5 regex quantifier (single): {7}
Perl6 : **7
}}
if $m ~~ m/CASE\s\#\s\:\s(\d**7)/ {
#`{{
5. Perl5 would say: unless (length($cno)) {}
Perl6 : unless $cno.chars {}
6. Perl5 captures strings and places them in variables starting with $1
Perl6 creates Capture objects starting with $0; use $0.Str to stringify them
}}
unless $cno.chars {
$cno = $0.Str;
}
}
#`{{
7. Perl6: inside patterns you have to escape punctuation characters like ':' and '-'
Why doesn't this DTRT?
if ($m ~~ m/FREQ\:|^DOCTOR\:/) {
}}
if $m ~~ m/FREQ\:|^DOCTOR\:|^\-+$/ {
#`{{
8. Perl5 : push(@out1, $m);
Perl6 (method syntax): @out1.push($m);
}}
@out1.push($m);
}
}
my $status = 0;
while (! $status) {
#`{{
8. Perl5: if (@out1[-1] =~ m/^-+$/) {}
Perl6: if @out1[*-1] ~~ m/^\-+$/ {}
}}
if @out1[*-1] ~~ m/^\-+$/ {
my $l = @out1.pop;
$status = 0;
}
else {
$status = 1;
}
}
die "Intermediate array is faulty" unless $status;
#warn "Intermediate array is status: $status";
my $in = '';
#`{{
9. Perl5 C-style 'for' loop: for (my $i = 0; $i < $#out1; $i++) {}
Perl6 loop: loop (my $i = 0; $i < @out1.elems - 1; $i++) {}
}}
loop (my $i = 0; $i < @out1.elems - 1; $i++) {
#`{{
10. Perl5 string concatenation: . .=
Perl6 : ~ ~=
}}
if ($i % 3) {
$in ~= @out1[$i] ~ "\n";
}
}
$in ~= @out1[@out1.elems - 1];
#`{{
11. Perl5 global substitution modifier: s/\nDOCTOR/ DOCTOR/g
Perl6 global substitution adverb: s:g/\nDOCTOR/ DOCTOR/
}}
$in ~~ s:g/\nDOCTOR/ DOCTOR/;
my $outstr = '';
for ($in.split("\n")) -> $j {
my %this_scrip;
if $j ~~ m/
^(.*?)
\s+FREQ\:\s+
(.*?)
\s+ORDER\s+\#\s+
(\d+)
\s+STARTED\s+
(\d**2\/\d**2\/\d**4)
\sDOCTOR\:\s+
(.*?)
\s+DOSE\:\s+
(.*?)
\s+TYPE\s+
(.*?)
\s+STOPPED\s+
(\d**2\/\d**2\/\d**4)
/ {
#`{{
12. Perl5 hash slice assignment: @hash{ qw( medication frequency ) } = ($1, $2);
Perl6 : %hash< medication frequency > = ($0, $1);
13. Perl5 map: @left = map { stringify($_) } @right;
Perl6 map: @left = @right.map: { $_.Str };
}}
%this_scrip< medication frequency order_number start_date doctor dose type stop_date > =
($0, $1, $2, $3, $4, $5, $6, $7).map: { $_.Str };
}
else {
warn "$j: Failed to match";
}
$outstr ~= sprintf("%-8s%-11s%-11s%-36s%-31s%-21s%-s\n",
$cno,
#`{{
14. Perl5 hash slice: @hash{ qw( medication frequency ) };
Perl6 : %hash.< medication frequency >;
}}
%this_scrip.<start_date stop_date type medication dose frequency>,
);
}
#`{{
15. Perl5 chomp: modifies target (by removing $/); returns total number of characters removed
Perl6 chomp: leaves target intact; returns new string which is target less logical newline
}}
say $outstr.chomp;