#!/usr/bin/perl -w
use strict;

print "\n";
print " ==== test 1 solutions ===== \n\n";

#####################################################
#
# Jim's solutions; Oct 13 2003.
#
#  * Lines that start with ## are the original test.
#  * I've put the code part of each answer into a
#    subroutine, and then run each of those subroutines.
#  * The output from running the whole thing is
#    appended at the end.
#  * Your mileage may vary; TMTOWTDI.
#
######################

##     Test 1
##                Intro to Programming in Perl
##                out: Oct 2 2003
##                due: by 8:30am on Tues Oct 6
##                from: mahoney@marlboro.edu
##
##  -- Rules ---------------------------------------------
##  Do all six problems.  Send me your results in an email
##  as usual.  You may use your text or other references, 
##  including running anything you please in a perl program.
##  Don't use other people.  Please do say what sources you used
##  for each problem.  Include appropriate comments, 
##  tests, and diagnostic output for your code : your
##  goal is to make it easy for me to see that you know
##  what you're doing.  Topics: basic syntax for variables
##  ($scalar, @arrays, %hashes), conditionals (if), 
##  loops (while, for), and booleans (and, or, ==, etc),
##  command line input (<>) and output (print).  You may
##  use subroutines if you like, but I'm not trying to
##  test that here, nor am I looking for detailed 
##  work with regular expressions.  I'll be out of town
##  this weekend but back Sunday night; you can email me
##  if you have questions and I'll get back to you then.
##
##  -- 1 -------------
## 
##  # Find and correct the syntax errors in the following code.
##
##  i.     $a = 2
##  ii.    b = "The value of a is " , a;
##  iii.   if ($a = 3) {
##  iv.      Print "yup, "a" is three.";
##  v.     }

sub number1 {
  my $a = 2;
  my $b = "The value of a is " . $a;
  if ($a == 3) {
    print "yup, \"a\" is three.";
  }
}

print "1:\n";
number1();
print "\n\n";

# Errors include
#  line   i. : no ";" at line end
#  line  ii. : "$b" instead of "b"
#            : "$a" instead of "a"
#            : "."  instead of "," to join string and $a as string.
#  line iii. : "==" instead of "=" in equality test
#  line  iv. : "print" instead of "Print"
#            : \" instead of " inside double quotes

##  -- 2 -------------
##
##  # Write a short program which assigns ($P,$Q) to 
##  # the four possible boolean combinations, namely
##  # (true,true), (true,false), (false, true), (false,false),
##  # and shows for each that the following two lines are
##  # equivalent.  Use appropriate Perl-ish quantities to
##  # represent 'true' and 'false'.
##
##     not ($P and $Q)
##     (not $P) or (not $Q)
##
##  # By the way, this is a well known fact in logic 
##  # that shows that you can use define the logical
##  # "or" operation in terms of the logical "and" and "not".

sub number2 {
  my $TRUE  = 1;
  my $FALSE = 0;
  print " --- P ---- Q  --- (not (P and Q)) --- ((not P) or (not Q)) -- same?\n";
  #     "   false  false    false               false
  #      123     12     1234     123456789012345     12345678901234567890

  foreach my $P ($TRUE,$FALSE){                     # Loop over P = (true,false)
    foreach my $Q ($TRUE,$FALSE){                   # Loop over Q = (true,false)
      my $firstLine  = (  not ($P and $Q)     );
      my $secondLine = ( (not $P) or (not $Q) );
      my $same;
      if ( $firstLine == $secondLine ) {
        $same = 'yes';
      }
      else {
        $same = 'no ';
      }
      my $areSame = print 
          ' 'x3,  toString($P),
          ' 'x2,  toString($Q),
          ' 'x4,  toString($firstLine), 
          ' 'x15, toString($secondLine),
          ' 'x20, $same,
          "\n";    
    }
  }
}
# toString($boolean) returns 5 char "true " or "false".
sub toString {
  my ($boolean) = @_;
  if ($boolean){
    return "true ";
  }
  else {
    return "false";
  }
}

print "2:\n";
number2();  # run it.
print "\n\n";

##  -- 3 -------------
##
##  # Write two programs that print the integers from 1 to 10,
##  # one using a "for" loop, and one using a "while" loop.

# Prints the numbers from 1 to 10 using
# a "foreach" loop (same as "for", really) over an array.
sub number3a {
  foreach my $n (1..10){
    print "$n ";
  }
  print "\n";
}

# Same using a C-ish "for" loop.
sub number3b {
  for (my $n=1; $n<=10; $n++){
    print "$n ";
  }
  print "\n";
}

# And now with a "while" loop.
sub number3c {
  my $n = 1;
  while ($n <= 10){
    print "$n ";
    $n++;
  }
  print "\n";
}

# Run 'em.
print "3a: "; number3a();
print "3b: "; number3b();
print "3c: "; number3c();
print "\n\n";

##  -- 4 -------------
##
##  # Explain what the "push", "pop", "shift", and "unshift"
##  # array operations do.  Show an example of each, printing
##  # out the array before and after the operation.
##
##  # Show how you can do the same operation as "push" by
##  # using an array assignment statement instead.  

sub number4{
  my @array = ("ten", "eleven", "twelve");
  print "  Orginal array = '@array' .\n";

  # "push" adds an element to the right side (highest index) of an array.
  push @array, "PUSH";
  print "  After push(\@array,'PUSH') : array='@array' .\n";

  # "pop" removes an element from the right side (highest index),
  # and returns that element.
  my $x = pop @array;
  print "  After \$x=pop(\@array) : array='@array' ; x=$x .\n";

  # "shift" and "unshift" do the same on the left side (index=0).
  unshift @array, "UNSHIFT";
  print "  After unshift(\@array,'UNSHIFT') : array='@array' .\n";
  my $y = shift @array;
  print "  After \$y=shift(\@array) : array='@array' ; y=$y .\n";
  
  # Here's how to do a "push" (i.e. add something to the right) explicitly.
  # The size of the array is scalar(@array), since in scalar context
  # the array turns into its size.  The existing elements are at indeces
  # (0,1,2,...,(size-1)), so we put the next one in 
  # at  index = scalar(@array).  
  # And to get or set an array element, we use $array[$index] .
  $array[scalar(@array)] = 'NEW';
  print "  After \$array[scalar(\@array)]='NEW',";
  print " array='@array'\n";
  
}

print "4:\n";
number4();
print "\n\n";

##  -- 5 -------------
##
##  # Write a short program that asks the user to type an integer
##  # from 1 to 5, and then prints out that number as an English
##  # word.  A sample dialog would look like this.
##
##    computer>   Type an integer from 1 to 5.
##    user>       4
##    computer>   You typed 'four'.
##
##  # For full credit do this in two ways, first using "if" and "elsif",
##  # and second using a hash, and also complain if the input is not
##  # integer from 1 to 5.

# Just doin' what it says up there...
# This is the ugly version, with a long if-elsif chain.
sub number5a {
  print "computer>   Type an integer from 1 to 5.\n";
  print "user>       ";
  my $number = <>;
  chomp($number);
  if ( ($number < 1) or ($number > 5) ){
    print "computer>   OOPS - that isn't between 1 and 5.\n";
  }
  elsif ( int($number) != $number ){
    print "computer>   OOPS - that isn't an integer.\n";
  }
  else {
    # Time to print out a string that says what it is.
    my $string;
    if    ( $number == 1 ){
      $string = "one";
    }
    elsif ( $number == 2 ){
      $string = "two";
    }
    elsif ( $number == 3 ){
      $string = "three";
    }
    elsif ( $number == 4 ){
      $string = "four";
    }
    elsif ( $number == 5 ){
      $string = "five";
    }
    else {
      $string = "WEIRD ERROR";
    }
    print "computer>   You typed '$string'.\n";
  }
}

# Here's a nicer version, with a hash to store the
# relation between the numbers and the words.
sub number5b {
  my %numberAsWord = ( 1 => 'one',
                       2 => 'two',
                       3 => 'three',
                       4 => 'four',
                       5 => 'five'
                     );
  print "computer>   Type an integer from 1 to 5.\n";
  print "user>       ";
  my $number = <>;
  chomp($number);
  if ( ($number < 1) or ($number > 5) ){
    print "computer>   OOPS - that isn't between 1 and 5.\n";
  }
  elsif ( int($number) != $number ){
    print "computer>   OOPS - that isn't an integer.\n";
  }
  else {
    # Time to print out a string that says what it is.
    print "computer>   You typed '", $numberAsWord{$number}, "'.\n";
  }
}

print "5a:\n";
number5a();
print "5b:\n";
number5b();
print "\n\n";


##  -- 6 -------------
##
##  # Figure out what the following program does.  Explain.
##  # (I suggest trying it with various versions of the first line.)
##  # Add appropriate comments and replace these variable
##  # names with ones that are clearer.
##
##   my @x=('jim','joe','john');
##   my $y="";
##   for my $z (@x){
##     if ($y lt $z){
##       $y=$z;
##     }
##   }
##   print "$y\n";

print "6:\n";

# First, here's the same code wrapped in a subroutine,
# with the first array replaced by something passed in
# by the user.  I've added a line to print the array.
# So invoking whatDoesItDo(@array) will print out 
# something like
#
#   With @x=(jim joe john), answer is ...
#
sub whatDoesItDo {
  my @x = @_;                          # 
  print " With \@x=(@x), answer is ";  # I added this.
  my $y="";
  for my $z (@x){
    if ($y lt $z){
      $y=$z;
    }
  }
  print "$y";
  print "\n";                           # and this.
}

# Now let's run it on a few different cases.
whatDoesItDo( 'jim',  'joe', 'john');                 # 'john'
whatDoesItDo( 'john', 'jim', 'joe');                  # 'john'
whatDoesItDo( 'a', 'aa', 'aaaa', 'z', 'zz', 'zzzz');  # 'zzzz'
whatDoesItDo( 'one', 'two', 'three', 'four');         # 'two'

# The key is the "$y lt $z" line.  
# "lt" is an string comparison, and returns true 
# if $y is before $z alphabetically.  With this clue
# its pretty simple to see from the restults that the program
# PRINTS THE LAST STRING FROM THE LIST WHEN ORDERED ALPHABETICALLY.

# So here's the same program, 
# with comments and nicer names.
sub lastWordNicerNames {
  # Given a list of words.
  my @wordList = ('one', 'two', 'three', 'four');
  # This word is going to be the last in alphabet order.
  # We start with something first in alpha order, so
  # that the first comparison changes it.
  my $lastSoFar = "";          # Initial value is before all other words.
  for my $word (@wordList){    # Loop over words in list.
    if ($lastSoFar lt $word){  #   If lastSoFar is beneath this word,
      $lastSoFar = $word;      #   then keep this word as the best so far.
    }
  }
  print "$lastSoFar";          # Print out the result.
}

print "\n";

__END__

================ Running the whole thing looks like this ==================

[mahoney@cs perl]$ ./test1_solutions.pl 

 ==== test 1 solutions ===== 

1:


2:
 --- P ---- Q  --- (not (P and Q)) --- ((not P) or (not Q)) -- same?
   true   true     false               false                    yes
   true   false    true                true                     yes
   false  true     true                true                     yes
   false  false    true                true                     yes


3a: 1 2 3 4 5 6 7 8 9 10 
3b: 1 2 3 4 5 6 7 8 9 10 
3c: 1 2 3 4 5 6 7 8 9 10 


4:
  Orginal array = 'ten eleven twelve' .
  After push(@array,'PUSH') : array='ten eleven twelve PUSH' .
  After $x=pop(@array) : array='ten eleven twelve' ; x=PUSH .
  After unshift(@array,'UNSHIFT') : array='UNSHIFT ten eleven twelve' .
  After $y=shift(@array) : array='ten eleven twelve' ; y=UNSHIFT .
  After $array[scalar(@array)]='NEW', array='ten eleven twelve NEW'


5a:
computer>   Type an integer from 1 to 5.
user>       1.2
computer>   OOPS - that isn't an integer.
5b:
computer>   Type an integer from 1 to 5.
user>       5
computer>   You typed 'five'.


6:
 With @x=(jim joe john), answer is john
 With @x=(john jim joe), answer is john
 With @x=(a aa aaaa z zz zzzz), answer is zzzz
 With @x=(one two three four), answer is two

[mahoney@cs perl]$ 
