PWC 253

PWC 253

I do both challenges this week using old software on DOSBOX: perl 4.019, and for comparison, also python 1.4 beta. My solutions also work on the current versions of Perl 5 (5.38) and Python 2 (2.7.18) respectively.

Challenge 1 (Split Strings)

Links to code: perl4.019 python1.4b

We are given a list of strings, and a single character say sep. We are asked to return the list, with the strings split into component substrings using sep as a separator. We should not include any empty strings in the output.

In perl 4, I first stick a backslash in front of the separator character, so that it is escaped if entered in a regex. This is because the perl split function takes a regex as its first argument indicating the separator. If we have a character like '$' or ',', we want to escape it so that it is read literally when parsing the regex.

Here is the key subroutine:

 sub split_strings {
 local ($separator, @words)=@_;
 local (@retval)=(); #array to be returned
 #
 #-- prefix $separator with '\\' to escape it in case it is
 #-- a meaningful regex character like '.' or '$'
 $separator = '\\' . $separator;
 local (@split_strings)=();
 foreach (@words) {
  @split_strings = split(/$separator/,$_);
  foreach (@split_strings) {     
   #-- push to @retval only if item is not an empty string
   $_ && push( @retval, $_ );
  }
 }
 @retval;
}

I put the local(@split_strings) declaration outside the foreach(@words) loop though it is redefined for each pass through the loop. This follows the advice in "Programming perl 1e" that repeated evaluation of the local declaration inside a loop will slow down things, and it is better to put a local(..) statement just outside the loop. (I don't think this advice would extend to the "my" keyword in modern Perl.)

The python script is almost a direct translation. In Python, we do not need to worry about escaping the separator, since the python splitfields string method does not take a regex as argument. 

Here is the python code:

import string

def split_strings(arrStr, sep):
 retval=[] #array to be returned
 for str in arrStr:
  str_split=string.splitfields( str, sep )
  for item in str_split:
   if (item != ""):
    retval.append(item)
 return retval

Challenge 2 (Weakest Row)

Links to code: perl4.019 python1.4b 

We are given a matrix consisting only of zeros and ones (an array of equal-length arrays with elements restricted to 0 or 1). We are asked to sum each row and return a list of the row indices sorted in order of this sum. 

I model the matrix in perl 4 as simply a flat list of strings, with each string consisting of 1s and 0s only, representing a row. For example, the matrix [[1,0],[0,1]] would be represented as ('10','01').

I create a supporting function to sum each row (string), and then in my main function use a sort function to produce the desired list. Unfortunately, as I discovered recently, I cannot nest the supporting function inside the primary subroutine. That feature was only available in Perl 5.

To sum each row:

sub mysum {
 local(@mysum)=split(//,$_[0]);
 local($mysum)=0;
 foreach (@mysum){
  $mysum += $_;
 }
 $mysum;

The primary weakest_row subroutine:

sub weakest_row {
 #input validation
 local($weakest_row) = length($_[0]);
 foreach (@_) {
  ($_ =~ /^[01]+$/) || (die "Input error: $!\n");
  (length($_) == $weakest_row) || (die "Not a matrix: $!\n");
 }  
 sort { (&mysum($_[$a]) <=> &mysum($_[$b])) ||
     ($a <=> $b);
 } 0 .. $#_;
}

The python 1.4 code is not a direct translation, as I could not get the python sort method to work with a custom function, and so tried something else. Also, in python, the input is directly an array of arrays. 

I do create my custom sum function using the python "reduce" operator. My reference book "Internet programming with python" warns that this operator is inefficient (in version 1.4b), but it runs fast enough with the small test examples.

def mysum(arr10):
 return reduce(lambda x1, x2: x1+x2, arr10) 

Nowadays, python has a built-in sum() function, but this was not available in version 1.4b.

In the primary weakest_row function, I create a list of 2-element tuples each consisting of the sum of each row and the row index, in that order. I then sort this list of tuples. The python default sort sorts a list of 2-element tuples first in the order of the first element in the tuple (the row sum) and then in the order of the second element in the tuple (the row index), exactly what we want. Finally, I return a list consisting of the second element from each tuple in the sorted list of tuples. I use python's map statement.

Here is the code:

def weakest_row(matrix): #-- matrix is an array of arrays
 retval=[]
 for rowno in range(len(matrix)):  #for each row
  retval.append((mysum(matrix[rowno]),rowno))
 retval.sort()
 return map(lambda x:x[1],retval)
 
 
 

Comments

Popular posts from this blog

PWC 215

PWC 227

PWC 234