PWC 229

PWC 229

Challenge 1 (Lexicographic Order)

We are given a list of strings. We are asked to count the number of elements that are strings whose constituent characters are not all either in weakly monotonically ascending lexicographic order or weakly monotonically descending lexicographic order (i.e., component strings whose characters are NOT either all in ascending or all in descending dictionary order).

I use a sub-subroutine &is_sorted that checks if each string is sorted. If the string has less than 3 elements, then it is automatically sorted. Otherwise, we check the order of the first 2 elements, say either element 1 is (lexical) less than or equal to (le) element 2, or element 1 is greater than or equal to (ge) element 2. We proceed to loop through the remaining elements to verify that they have the same ordering relationship (le or ge) as the first two.

In the main part of the subroutine, we call &is_sorted on each string in the array (map), then extract the zeroes (not sorted) in the resulting array (grep), and return the length of the array of extracted zeros (scalar). 

Here is the subroutine:

sub lexicographic_order {
    #-- helper subroutine
    
    local *is_sorted = sub {
        my ($str)=@_;
        
        (length($str) < 3) && (return 1);
        
        my $op;
        ( substr($str,0,1) le substr($str,1,1) ) ?
        ($op = 'le') : ($op = 'ge');
        
        INSTRING: for my $x (2 .. (length($str)-1)) {
            my $test = ('\''. substr($str,$x-1,1) .'\' '. $op .' \''. substr($str,$x,1).'\'');
            
            unless (eval($test)) {
                return 0;
                last INSTRING;
            }
        }
        1;
    };
    
    
    #-- main trunk of subroutine
    my @str = @_;
    scalar grep /0/, map {&is_sorted($str[$_])} 0 .. $#str;
}


Challenge 2 (Two out of three)

We are given three arrays (presumably 3 arrayrefs in Perl 5). We are asked to extract a list of items that are elements of at least two out of the three arrays.

My solution is probably not the most efficient. I use three hashes to record membership in each of the three arrays. Then I use another hash %two_out_of_three to count elements in at least two out of three arrays. I loop through the keys of the membership hashes for the first two arrays in the input, recording in %two_out_of_three which of the keys also exists in one or both of the other membership hashes. Finally, we return the keys of %two_out_of_three.

Here is the subroutine:

sub two_out_of_three {
    #-- helper sub sub
    local *in_arry = sub {
        local @in_arry=@_;
        local %in_arry;
      
        map {
            $in_arry{$_}=1;
        } @in_arry;
        %in_arry;
    };
    
    #-- main trunk of sub
    my ($ra1, $ra2, $ra3)=@_;
    
    my %rh1 = &in_arry($ra1->@*);
    my %rh2 = &in_arry($ra2->@*);
    my %rh3 = &in_arry($ra3->@*);
    
    my %two_out_of_three;
    
    map {( (exists $rh2{$_}) || (exists $rh3{$_}) ) &&
     ($two_out_of_three{$_}=1);} keys %rh1;
    map {( (exists $rh1{$_}) || (exists $rh3{$_}) ) &&
     ($two_out_of_three{$_}=1);} keys %rh2;

    keys %two_out_of_three;
}

I probably should have called the membership hashes %hash1, %hash2, etc., rather than %rh1, %rh2, etc. since I usually use the rh prefix as Hungarian notation to signify a hashref. I'll leave this minor inconsistency. 

Comments

Popular posts from this blog

PWC 227

PWC 234

PWC 249