[system] / trunk / webwork / system / courseScripts / AnswerHash.pm Repository:
ViewVC logotype

View of /trunk/webwork/system/courseScripts/AnswerHash.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 293 - (download) (as text) (annotate)
Thu May 23 13:52:02 2002 UTC (17 years, 6 months ago) by gage
File size: 20186 byte(s)
Adding documentation

    1 ##########################################################################
    2 ## AnswerHash Package
    3 ##
    4 ## Provides a data structure for answer hashes. Currently just a wrapper
    5 ## for the hash, but that might change
    6 ####################################################################
    7 # Copyright @ 1995-2002 WeBWorK Team
    8 # All Rights Reserved
    9 ####################################################################
   10 #$Id$
   11 
   12 =head1 NAME
   13 
   14   AnswerHash.pl -- located in the courseScripts directory
   15 
   16   This file contains the packages/classes:
   17   AnswerHash   and AnswerEvaluator
   18 
   19 =head1 SYNPOSIS
   20 
   21   AnswerHash  -- this class stores information related to the student's
   22            answer.  It is little more than a standard perl hash with
   23            a special name, butit does have some access and
   24            manipulation methods.  More of these may be added as it
   25            becomes necessary.
   26 
   27   Usage:    $rh_ans = new AnswerHash;
   28 
   29   AnswerEvaluator -- this class organizes the construction of
   30          answer evaluator subroutines which check the
   31          student's answer.  By plugging filters into the
   32          answer evaluator class you can customize the way the
   33          student's answer is normalized and checked.  Our hope
   34          is that with properly designed filters, it will be
   35          possible to reuse the filters in different
   36          combinations to obtain different answer evaluators,
   37          thus greatly reducing the programming and maintenance
   38          required for constructing answer evaluators.
   39 
   40 =cut
   41 
   42 =head1 DESCRIPTION
   43 
   44 The answer hash class is guaranteed to contain the following instance variables:
   45 
   46   score     =>  $correctQ,
   47   correct_ans   =>  $originalCorrEqn,
   48   student_ans   =>  $modified_student_ans
   49   original_student_ans  =>  $original_student_answer,
   50   ans_message   =>  $PGanswerMessage,
   51   type      =>  'typeString',
   52   preview_text_string =>  $preview_text_string,
   53   preview_latex_string  =>  $preview_latex_string
   54 
   55 
   56   $ans_hash->{score}    --  a number between 0 and 1 indicating
   57             whether the answer is correct. Fractions
   58             allow the implementation of partial
   59             credit for incorrect answers.
   60 
   61   $ans_hash->{correct_ans}    --  The correct answer, as supplied by the
   62             instructor and then formatted. This can
   63             be viewed by the student after the answer date.
   64 
   65   $ans_hash->{student_ans}    --  This is the student answer, after reformatting;
   66             for example the answer might be forced
   67             to capital letters for comparison with
   68             the instructors answer. For a numerical
   69             answer, it gives the evaluated answer.
   70             This is displayed in the section reporting
   71             the results of checking the student answers.
   72 
   73   $ans_hash->{original_student_ans} --  This is the original student answer. This is displayed
   74             on the preview page and may be used for sticky answers.
   75 
   76   $ans_hash->{ans_message}    --  Any error message, or hint provided by the answer evaluator.
   77             This is also displayed in the section reporting
   78             the results of checking the student answers.
   79 
   80   $ans_hash->{type}     --  A string indicating the type of answer evaluator. This
   81             helps in preprocessing the student answer for errors.
   82             Some examples:
   83               'number_with_units'
   84               'function'
   85               'frac_number'
   86               'arith_number'
   87 
   88 
   89   $ans_hash{preview_text_string}  --  This typically shows how the student answer was parsed. It is
   90             displayed on the preview page. For a student answer of 2sin(3x)
   91             this would be 2*sin(3*x). For string answers it is typically the
   92             same as $ans_hash{student_ans}.
   93 
   94 
   95   $ans_hash{preview_latex_string} --  THIS IS OPTIONAL. This is latex version of the student answer
   96             which is used to show a typeset view on the answer on the preview
   97             page. For a student answer of 2/3, this would be \frac{2}{3}.
   98 
   99             'ans_message'     =>  '',
  100 
  101             'preview_text_string' =>  undef,
  102             'preview_latex_string'  =>  undef,
  103             'error_flag'      =>  undef,
  104             'error_message'       =>  '',
  105 
  106 
  107 Methods:
  108 
  109   new
  110 
  111   setKeys   $rh_ans->setKeys(score=>1, student_answer => "yes");
  112           Sets standard elements in the AnswerHash (the ones defined
  113                 above).
  114 
  115 
  116   $rh_ans->{non_standard_value} = 'oops';
  117         Add an element to the AnswerHash or replace that element. (No
  118                 checks are made, use wisely!)
  119   OR($rh_ans)
  120 
  121   AND($rh_ans)
  122 
  123     data
  124     input
  125 
  126     score
  127 
  128     throw_error
  129 
  130     catch_error
  131 
  132     clear_error
  133 
  134     error_flag
  135 
  136     error_message
  137 
  138     pretty_print
  139 
  140 
  141 =cut
  142 
  143 BEGIN {
  144   be_strict(); # an alias for use strict.  This means that all global variable must contain main:: as a prefix.
  145 
  146 }
  147 
  148 package AnswerHash;
  149 # initialization fields
  150 my %fields = (    'score'         =>  undef,
  151           'correct_ans'     =>  undef,
  152           'student_ans'     =>  undef,
  153           'ans_message'     =>  undef,
  154           'type'          =>  undef,
  155           'preview_text_string' =>  undef,
  156           'preview_latex_string'  =>  undef,
  157           'original_student_ans'  =>  undef
  158       );
  159 
  160 ## Initializing constructor
  161 sub new {
  162   my $class = shift @_;
  163 
  164   my $self  = { 'score'         =>  0,
  165           'correct_ans'     =>  'No correct answer specified',
  166           'student_ans'     =>  undef,
  167           'ans_message'     =>  '',
  168           'type'          =>  'Undefined answer evaluator type',
  169           'preview_text_string' =>  undef,
  170           'preview_latex_string'  =>  undef,
  171           'original_student_ans'  =>  undef,
  172           'error_flag'      =>  undef,
  173           'error_message'       =>  '',
  174 
  175   };  # return a reference to a hash.
  176 
  177   bless $self, $class;
  178   $self -> setKeys(@_);
  179 
  180   return $self;
  181 }
  182 
  183 ## IN: a hash
  184 ## Checks to make sure that the keys are valid,
  185 ## then sets their value
  186 sub setKeys {
  187     my $self = shift;
  188   my %inits = @_;
  189   foreach my $item (keys %inits) {
  190     if ( exists $fields{$item} ) {
  191       $self -> {$item} = $inits{$item};
  192     }
  193     else {
  194       warn "AnswerHash cannot automatically initialize an item named $item";
  195     }
  196   }
  197 }
  198 
  199 # access methods
  200 sub data {    #$rh_ans->data('foo') is a synonym for $rh_ans->{student_ans}='foo'
  201   my $self = shift;
  202   $self->input(@_);
  203 }
  204 
  205 sub input {     #$rh_ans->input('foo') is a synonym for $rh_ans->{student_ans}='foo'
  206   my $self = shift;
  207     my $input = shift;
  208     $self->{student_ans} = $input if defined($input);
  209   $self->{student_ans}
  210 }
  211 sub score {
  212   my $self = shift;
  213     my $score = shift;
  214     $self->{score} = $score if defined($score);
  215   $self->{score}
  216 }
  217 
  218 # error methods
  219 sub throw_error {
  220   my $self = shift;
  221     my $flag = shift;
  222     my $message = shift;
  223     $self->{error_message} .= " $message " if defined($message);
  224     $self->{error_flag} = $flag if defined($flag);
  225   $self->{error_flag}
  226 }
  227 sub catch_error {
  228   my $self = shift;
  229     my $flag = shift;
  230     return('')  unless defined($self->{error_flag});
  231     return $self->{error_flag} unless $flag;    # empty input catches all errors.
  232     return $self->{error_flag} if $self->{error_flag} eq $flag;
  233   return '';   # nothing to catch
  234 }
  235 sub clear_error {
  236   my $self = shift;
  237   my $flag = shift;
  238   if (defined($flag) and $flag =~/\S/ and defined($self->{error_flag})  and $flag eq $self->{error_flag}) {
  239     $self->{error_flag} = undef;
  240     $self->{error_message} = undef;
  241   }
  242   $self;
  243 }
  244 sub error_flag {
  245   my $self = shift;
  246     my $flag = shift;
  247     $self->{error_flag} = $flag if defined($flag);
  248   $self->{error_flag}
  249 }
  250 sub error_message {
  251   my $self = shift;
  252     my $message = shift;
  253     $self->{error_message} = $message if defined($message);
  254   $self->{error_message}
  255 }
  256 
  257 # error print out method
  258 
  259 sub pretty_print {
  260     my $r_input = shift;
  261     my $out = '';
  262     if ( not ref($r_input) ) {
  263       $out = $r_input;    # not a reference
  264     } elsif (ref($r_input) =~/hash/i) {
  265       local($^W) = 0;
  266     $out .= "<TABLE border = \"2\" cellpadding = \"3\" BGCOLOR = \"#FFFFFF\">";
  267     foreach my $key (sort keys %$r_input ) {
  268       $out .= "<tr><TD> $key</TD><TD>=&gt;</td><td>&nbsp;".pretty_print($r_input->{$key}) . "</td></tr>";
  269     }
  270     $out .="</table>";
  271   } elsif (ref($r_input) eq 'ARRAY' ) {
  272     my @array = @$r_input;
  273     $out .= "( " ;
  274     while (@array) {
  275       $out .= pretty_print(shift @array) . " , ";
  276     }
  277     $out .= " )";
  278   } elsif (ref($r_input) eq 'CODE') {
  279     $out = "$r_input";
  280   } else {
  281     $out = $r_input;
  282   }
  283     $out;
  284 }
  285 
  286 # action methods
  287 sub OR {
  288   my $self = shift;
  289 
  290   my $rh_ans2 = shift;
  291   my %options = @_;
  292   return($self) unless defined($rh_ans2) and ref($rh_ans2) eq 'AnswerHash';
  293 
  294   my $out_hash = new AnswerHash;
  295   # score is the maximum of the two scores
  296   $out_hash->{score} = ( $self->{score}  <  $rh_ans2->{score} ) ? $rh_ans2->{score} :$self->{score};
  297   $out_hash->{correct_ans} = join(" OR ", $self->{correct_ans}, $rh_ans2->{correct_ans} );
  298   $out_hash->{student_ans} = $self->{student_ans};
  299   $out_hash->{type} = join(" OR ", $self->{type}, $rh_ans2->{type} );
  300   $out_hash->{preview_text_string} = join("   ", $self->{preview_text_string}, $rh_ans2->{preview_text_string} );
  301   $out_hash->{original_student_ans} = $self->{original_student_ans};
  302   $out_hash;
  303 }
  304 
  305 sub AND {
  306   my $self = shift;
  307   my $rh_ans2 = shift;
  308   my %options = @_;
  309   my $out_hash = new AnswerHash;
  310   # score is the minimum of the two scores
  311   $out_hash->{score} = ( $self->{score}  >  $rh_ans2->{score} ) ? $rh_ans2->{score} :$self->{score};
  312   $out_hash->{correct_ans} = join(" AND ", $self->{correct_ans}, $rh_ans2->{correct_ans} );
  313   $out_hash->{student_ans} = $self->{student_ans};
  314   $out_hash->{type} = join(" AND ", $self->{type}, $rh_ans2->{type} );
  315   $out_hash->{preview_text_string} = join("   ", $self->{preview_text_string}, $rh_ans2->{preview_text_string} );
  316   $out_hash->{original_student_ans} = $self->{original_student_ans};
  317   $out_hash;
  318 }
  319 
  320 package AnswerEvaluator;
  321 
  322 
  323 
  324 
  325 sub new {
  326   my $class = shift @_;
  327 
  328   my $self  = { pre_filters   =>  [ [\&blank_prefilter] ],
  329           evaluators    =>  [],
  330           post_filters  =>  [ [\&blank_postfilter] ],
  331           debug     =>  0,
  332           rh_ans    =>  new AnswerHash,
  333 
  334   };
  335 
  336   bless $self, $class;
  337   $self->rh_ans(@_);    #initialize answer hash
  338   return $self;
  339 }
  340 
  341 # dereference_array_ans pretty prints an answer which is stored as an anonymous array.
  342 sub dereference_array_ans {
  343   my $self = shift;
  344   my $rh_ans = shift;
  345   if (defined($rh_ans->{student_ans}) and ref($rh_ans->{student_ans}) eq 'ARRAY'  ) {
  346     $rh_ans->{student_ans} = "( ". join(" , ",@{$rh_ans->{student_ans}} ) . " ) ";
  347   }
  348   $rh_ans;
  349 }
  350 
  351 sub get_student_answer {
  352   my $self  = shift;
  353   my $input   = shift;
  354   $input = '' unless defined($input);
  355   if (ref($input) =~/AnswerHash/) {
  356     # in this case nothing needs to be done, since the student's answer is already in an answerhash.
  357     # This is useful when an AnswerEvaluator is used as a filter in another answer evaluator.
  358   } elsif ($input =~ /\0/ ) {  # this case may occur with older versions of CGI??
  359       my @input = split(/\0/,$input);
  360       $self-> {rh_ans} -> {original_student_ans} = " ( " .join(", ",@input) . " ) ";
  361     $input = \@input;
  362     $self-> {rh_ans} -> {student_ans} = $input;
  363   } elsif (ref($input) eq 'ARRAY' ) {  # sometimes the answer may already be decoded into an array.
  364       my @input = @$input;
  365       $self-> {rh_ans} -> {original_student_ans} = " ( " .join(", ",@input) . " ) ";
  366     $input = \@input;
  367     $self-> {rh_ans} -> {student_ans} = $input;
  368   } else {
  369 
  370     $self-> {rh_ans} -> {original_student_ans} = $input;
  371     $self-> {rh_ans} -> {student_ans} = $input;
  372   }
  373 
  374 
  375   $input;
  376 }
  377 
  378 sub evaluate {
  379   my $self    =   shift;
  380   $self->get_student_answer(shift @_);
  381   $self->{rh_ans}->{error_flag}=undef;  #reset the error flags in case
  382   $self->{rh_ans}->{done}=undef;        #the answer evaluator is called twice
  383   my $rh_ans    =   $self ->{rh_ans};
  384     warn "<H3> Answer evaluator information: </H3>\n" if defined($self->{debug}) and $self->{debug}>0;
  385   my @prefilters  = @{$self -> {pre_filters}};
  386   my $count = -1;  # the blank filter is counted as filter 0
  387   foreach my $i (@prefilters) {
  388       last if defined( $self->{rh_ans}->{error_flag} );
  389       my @array = @$i;
  390       my $filter = shift(@array);      # the array now contains the options for the filter
  391       my %options = @array;
  392       if (defined($self->{debug}) and $self->{debug}>0) {
  393 
  394         $self->{rh_ans}->{rh_options} = \%options;  #include the options in the debug information
  395         warn "before pre-filter: ",++$count, $self->{rh_ans}->pretty_print();
  396       }
  397       $rh_ans   = &$filter($rh_ans,@array);
  398       warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n"
  399         if defined($self->{debug}) and $self->{debug}>0 and defined($rh_ans->{_filter_name});
  400       $rh_ans->{_filter_name} = undef;
  401   }
  402   my @evaluators = @{$self -> {evaluators} };
  403   $count = 0;
  404   foreach my $i ( @evaluators )   {
  405       last if defined($self->{rh_ans}->{error_flag});
  406     my @array = @$i;
  407       my $evaluator = shift(@array);   # the array now contains the options for the filter
  408       my %options = @array;
  409       if (defined($self->{debug}) and $self->{debug}>0) {
  410         $self->{rh_ans}->{rh_options} = \%options;  #include the options in the debug information
  411         warn "before evaluator: ",++$count, $self->{rh_ans}->pretty_print();
  412       }
  413     $rh_ans   = &$evaluator($rh_ans,@array);
  414     warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" if defined($self->{debug}) and $self->{debug}>0 and defined($rh_ans->{_filter_name});
  415     $rh_ans->{_filter_name} = undef;
  416   }
  417   my @post_filters = @{$self -> {post_filters} };
  418   $count = -1;  # blank filter catcher is filter 0
  419   foreach my $i ( @post_filters ) {
  420       last if defined($rh_ans->{done}) and $rh_ans->{done} == 1;    # no further action needed
  421     my @array = @$i;
  422 
  423       my $filter = shift(@array);      # the array now contains the options for the filter
  424       my %options = @array;
  425       if (defined($self->{debug}) and $self->{debug}>0) {
  426         $self->{rh_ans}->{rh_options} = \%options;  #include the options in the debug information
  427         warn "before post-filter: ",++$count, $self->{rh_ans}->pretty_print(),"\n";
  428       }
  429 
  430     $rh_ans   = &$filter($rh_ans,@array);
  431     warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" if defined($self->{debug}) and $self->{debug}>0 and defined($rh_ans->{_filter_name});
  432     $rh_ans->{_filter_name} = undef;
  433   }
  434   $rh_ans = $self->dereference_array_ans($rh_ans);
  435   # make sure that the student answer is not an array so that it is reported correctly in answer section.
  436   warn "<h4>final result: </h4>", $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  437   $self ->{rh_ans} = $rh_ans;
  438   $rh_ans;
  439 }
  440 # This next subroutine is for checking the instructor's answer and is not yet in use.
  441 sub correct_answer_evaluate {
  442   my $self    =   shift;
  443   $self-> {rh_ans} -> {correct_ans} = shift @_;
  444   my $rh_ans    =   $self ->{rh_ans};
  445   my @prefilters  = @{$self -> {correct_answer_pre_filters}};
  446   my $count = -1;  # the blank filter is counted as filter 0
  447   foreach my $i (@prefilters) {
  448       last if defined( $self->{rh_ans}->{error_flag} );
  449       my @array = @$i;
  450       my $filter = shift(@array);      # the array now contains the options for the filter
  451       warn "before pre-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  452     $rh_ans   = &$filter($rh_ans,@array);
  453     warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name})
  454   }
  455   my @evaluators = @{$self -> {correct_answer_evaluators} };
  456   $count = 0;
  457   foreach my $i ( @evaluators )   {
  458       last if defined($self->{rh_ans}->{error_flag});
  459     my @array = @$i;
  460       my $evaluator = shift(@array);   # the array now contains the options for the filter
  461       warn "before evaluator: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  462     $rh_ans   = &$evaluator($rh_ans,@array);
  463   }
  464   my @post_filters = @{$self -> {correct_answer_post_filters} };
  465   $count = -1;  # blank filter catcher is filter 0
  466   foreach my $i ( @post_filters ) {
  467       last if defined($rh_ans->{done}) and $rh_ans->{done} == 1;    # no further action needed
  468     my @array = @$i;
  469       my $filter = shift(@array);      # the array now contains the options for the filter
  470       warn "before post-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  471     $rh_ans   = &$filter($rh_ans,@array);
  472     warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name})
  473   }
  474   $rh_ans = $self->dereference_array_ans($rh_ans);
  475   # make sure that the student answer is not an array so that it is reported correctly in answer section.
  476   warn "final result: ", $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  477   $self ->{rh_ans} = $rh_ans;
  478   $rh_ans;
  479 }
  480 
  481 sub install_pre_filter {
  482   my $self =  shift;
  483   if (@_ == 0) {
  484     # do nothing if input is empty
  485   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  486     $self->{pre_filters} = [];
  487   } else {
  488     push(@{$self->{pre_filters}},[ @_ ]) if @_;  #install pre_filter and it's options
  489   }
  490   @{$self->{pre_filters}};  # return array of all pre_filters
  491 }
  492 
  493 sub install_evaluator {
  494   my $self =  shift;
  495   if (@_ == 0) {
  496     # do nothing if input is empty
  497   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  498     $self->{evaluators} = [];
  499   } else {
  500     push(@{$self->{evaluators}},[ @_ ]) if @_; #install evaluator and it's options
  501   }
  502   @{$self->{'evaluators'}};  # return array of all evaluators
  503 }
  504 
  505 sub install_post_filter {
  506   my $self =  shift;
  507   if (@_ == 0) {
  508     # do nothing if input is empty
  509   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  510     $self->{post_filters} = [];
  511   } else {
  512     push(@{$self->{post_filters}}, [ @_ ]) if @_; #install post_filter and it's options
  513   }
  514   @{$self->{post_filters}};  # return array of all post_filters
  515 }
  516 
  517 ## filters for checking the correctAnswer
  518 sub install_correct_answer_pre_filter {
  519   my $self =  shift;
  520   if (@_ == 0) {
  521     # do nothing if input is empty
  522   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  523     $self->{correct_answer_pre_filters} = [];
  524   } else {
  525     push(@{$self->{correct_answer_pre_filters}},[ @_ ]) if @_;  #install correct_answer_pre_filter and it's options
  526   }
  527   @{$self->{correct_answer_pre_filters}};  # return array of all correct_answer_pre_filters
  528 }
  529 
  530 sub install_correct_answer_evaluator {
  531   my $self =  shift;
  532   if (@_ == 0) {
  533     # do nothing if input is empty
  534   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  535     $self->{correct_answer_evaluators} = [];
  536   } else {
  537     push(@{$self->{correct_answer_evaluators}},[ @_ ]) if @_; #install evaluator and it's options
  538   }
  539   @{$self->{correct_answer_evaluators}};  # return array of all evaluators
  540 }
  541 
  542 sub install_correct_answer_post_filter {
  543   my $self =  shift;
  544   if (@_ == 0) {
  545     # do nothing if input is empty
  546   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  547     $self->{correct_answer_post_filters} = [];
  548   } else {
  549     push(@{$self->{correct_answer_post_filters}}, [ @_ ]) if @_; #install post_filter and it's options
  550   }
  551   @{$self->{correct_answer_post_filters}};  # return array of all post_filters
  552 }
  553 
  554 sub ans_hash {  #alias for rh_ans
  555   my $self = shift;
  556   $self->rh_ans(@_);
  557 }
  558 sub rh_ans {
  559   my $self = shift;
  560   my %in_hash = @_;
  561   foreach my $key (keys %in_hash) {
  562     $self->{rh_ans}->{$key} = $in_hash{$key};
  563   }
  564   $self->{rh_ans};
  565 }
  566 ######################################################
  567 #
  568 # Built in Filters
  569 #
  570 ######################################################
  571 
  572 
  573 sub blank_prefilter  { # check for blanks
  574   my $rh_ans = shift;
  575     # undefined answers are BLANKS
  576   ( not defined($rh_ans->{student_ans}) ) && do {$rh_ans->throw_error("BLANK", 'The answer is blank');
  577                             return($rh_ans);};
  578     # answers which are arrays or hashes or some other object reference  are NOT blanks
  579     ( ref($rh_ans->{student_ans} )        ) && do { return( $rh_ans ) };
  580     # if the answer is a true variable consisting only of white space it is a BLANK
  581     ( ($rh_ans->{student_ans}) !~ /\S/   )    && do {$rh_ans->throw_error("BLANK", 'The answer is blank');
  582                             return($rh_ans);};
  583   # If we get to here, we assume that the answer is not a blank. It is defined, not a reference
  584   # and contains something other than whitespaces.
  585   $rh_ans;
  586 };
  587 
  588 sub blank_postfilter  {
  589   my $rh_ans=shift;
  590     return($rh_ans) unless defined($rh_ans->{error_flag}) and $rh_ans->{error_flag} eq 'BLANK';
  591     $rh_ans->{error_flag} = undef;
  592     $rh_ans->{error_message} = '';
  593     $rh_ans->{done} =1;    # no further checking is needed.
  594     $rh_ans;
  595 };
  596 
  597 1;
  598 #package AnswerEvaluatorMaker;
  599 

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9