aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandrezgz <andrezgz@gmail.com>2019-05-31 23:49:22 -0300
committerandrezgz <andrezgz@gmail.com>2019-05-31 23:49:22 -0300
commitc23637bca17d7e75f85fd8b516af88a4f5e7ae21 (patch)
tree689a22ff9d007d3c8ecad3cee26e985e8aba4241
parent6135ea57e1b789c67981e2de52c2bc9c666a0f74 (diff)
downloadperlweeklychallenge-club-c23637bca17d7e75f85fd8b516af88a4f5e7ae21.tar.gz
perlweeklychallenge-club-c23637bca17d7e75f85fd8b516af88a4f5e7ae21.tar.bz2
perlweeklychallenge-club-c23637bca17d7e75f85fd8b516af88a4f5e7ae21.zip
challenge-010 andrezgz solution
-rw-r--r--challenge-010/andrezgz/perl5/ch-1.pl86
-rw-r--r--challenge-010/andrezgz/perl5/ch-2.pl104
2 files changed, 190 insertions, 0 deletions
diff --git a/challenge-010/andrezgz/perl5/ch-1.pl b/challenge-010/andrezgz/perl5/ch-1.pl
new file mode 100644
index 0000000000..021587e75a
--- /dev/null
+++ b/challenge-010/andrezgz/perl5/ch-1.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+# https://perlweeklychallenge.org/blog/perl-weekly-challenge-010/
+# Challenge #1
+# Write a script to encode/decode Roman numerals.
+# For example, given Roman numeral CCXLVI, it should return 246.
+# Similarly, for decimal number 39, it should return XXXIX.
+# Checkout wikipedia page for more information.
+# https://en.wikipedia.org/wiki/Roman_numerals
+
+use strict;
+use warnings;
+
+die "Usage: $0 <DECIMAL|ROMAN>" unless $ARGV[0];
+
+my %roman_table = (
+ 'I' => 1,
+ 'IV' => 4,
+ 'V' => 5,
+ 'IX' => 9,
+ 'X' => 10,
+ 'XL' => 40,
+ 'L' => 50,
+ 'XC' => 90,
+ 'C' => 100,
+ 'CD' => 400,
+ 'D' => 500,
+ 'CM' => 900,
+ 'M' => 1000
+);
+
+my $arg = $ARGV[0];
+
+if ($arg =~ /^\d+$/i) {
+ die "Decimal number should be between 1 and 3999" if ($arg < 1 || $arg > 3999);
+ print encode_roman($arg);
+}
+else {
+ die "Invalid roman number" unless ( _is_valid_roman($arg) );
+ print decode_roman(uc $arg);
+}
+
+exit 0;
+
+# Subtractive notation roman number validation
+sub _is_valid_roman {
+ return $arg =~ /
+ ^ # String start
+ M{0,3} # Matching from 1000 to 3000
+ (?:CM|CD|D|D?C{0,3})? # Matching from 100 to 900
+ (?:XC|XL|L|L?X{0,3})? # Matching from 10 to 90
+ (?:IX|IV|V|V?I{0,3})? # Matching from 1 to 9
+ $ # String end
+ /xi;
+}
+
+sub encode_roman {
+ my $number = shift;
+ my $roman_number;
+
+ foreach my $r (sort { $roman_table{$b} <=> $roman_table{$a} } keys %roman_table) {
+ my $d = int($number / $roman_table{$r});
+ next unless $d;
+ $roman_number .= $r x $d;
+ $number -= $roman_table{$r} * $d;
+ }
+
+ return $roman_number;
+}
+
+sub decode_roman {
+ my @roman = split //, shift;
+ my $number;
+
+ while (my $c1 = shift @roman) {
+ my $c2 = @roman ? $roman[0] : 0;
+ if (!$c2 || $roman_table{$c1} >= $roman_table{$c2} ) {
+ $number += $roman_table{$c1};
+ } else {
+ $number += $roman_table{$c2} - $roman_table{$c1};
+ shift @roman;
+ }
+ }
+
+ return $number;
+}
diff --git a/challenge-010/andrezgz/perl5/ch-2.pl b/challenge-010/andrezgz/perl5/ch-2.pl
new file mode 100644
index 0000000000..a7da3bb1d5
--- /dev/null
+++ b/challenge-010/andrezgz/perl5/ch-2.pl
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+
+# https://perlweeklychallenge.org/blog/perl-weekly-challenge-010/
+# Challenge #2
+# Write a script to find Jaro-Winkler distance between two strings.
+# For more information check wikipedia page.
+# https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
+
+use strict;
+use warnings;
+
+die "Usage: $0 <string> <string>" if (scalar @ARGV != 2);
+
+#Using lower case for checking similarity
+my $dw = 1 - simw(map {lc} @ARGV);;
+print 'Jaro-Winkler distance: '.sprintf("%.3f",$dw).$/;
+
+exit 0;
+
+#Jaro-Winkler similarity
+sub simw {
+ my ($s1,$s2,$p) = @_;
+ #Scaling factor
+ $p = 0.1 unless $p;
+
+ my $simj = simj($s1,$s2);
+
+ my $prefix = _common_prefix($s1,$s2);
+
+ my $simw = $simj + $prefix * $p * (1- $simj);
+
+ return $simw;
+}
+
+#Common prefix
+sub _common_prefix {
+ my ($s1,$s2) = @_;
+
+ my @chr1 = split //, $s1;
+ my @chr2 = split //, $s2;
+
+ my $prefix = 0;
+ do {
+ last unless ( defined $chr2[$_] && $chr1[$_] eq $chr2[$_] );
+ $prefix++;
+ } for 0..3; #prefix up to 4
+ return $prefix;
+}
+
+#Jaro similarity
+sub simj {
+ my ($s1,$s2) = @_;
+
+ my $l1 = length($s1);
+ my $l2 = length($s2);
+
+ #Matching distance
+ my $max_l = $l1 > $l2 ? $l1 : $l2;
+ my $match_dist = int($max_l / 2 - 1);
+
+ my @chr1 = split //, $s1;
+ my @chr2 = split //, $s2;
+ my @chr2tmp = @chr2;
+
+ my @matches;
+ my %matches_position;
+
+ for (my $i = 0; $i < $l1; $i++) {
+
+ my $init = $i - $match_dist;
+ $init = 0 if $init < 0;
+
+ my $end = $i + $match_dist + 1;
+ $end = $l2 if $end > $l2;
+
+ for (my $j = $init; $j < $end; $j++) {
+ if ($chr1[$i] eq $chr2tmp[$j]){
+ push @matches, $chr1[$i];
+ $matches_position{$j} = $i; #required to detect transpositions
+ $chr2tmp[$j]='-'; # avoid matching with the same character
+ last;
+ }
+ }
+ }
+
+ my $m = @matches;
+ #Jaro similarity is 0 if there are no matches
+ return 0 if ($m == 0);
+
+ my $transpositions = 0;
+ my $j = 0;
+ for (my $i = 0; $j < $m; $i++) {
+ if (exists $matches_position{$i}){
+ $transpositions++ if( $chr2[$i] ne $matches[$j]);
+ $j++;
+ }
+ }
+
+ my $t = $transpositions / 2;
+
+ my $simj = 1/3 * ($m/$l1 + $m/$l2 + ($m - $t)/$m);
+
+ return $simj;
+}