[system] / trunk / pg / lib / Value / AnswerChecker.pm Repository:
ViewVC logotype

Diff of /trunk/pg/lib/Value/AnswerChecker.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 2623 Revision 2624
64 # Parse and evaluate the student answer 64 # Parse and evaluate the student answer
65 # 65 #
66 $ans->score(0); # assume failure 66 $ans->score(0); # assume failure
67 $ans->{student_value} = $ans->{student_formula} = Parser::Formula($ans->{student_ans}); 67 $ans->{student_value} = $ans->{student_formula} = Parser::Formula($ans->{student_ans});
68 $ans->{student_value} = Parser::Evaluate($ans->{student_formula}) 68 $ans->{student_value} = Parser::Evaluate($ans->{student_formula})
69 if defined($ans->{student_formula}) && $ans->{student_formula}->isConstant && 69 if defined($ans->{student_formula}) && $ans->{student_formula}->isConstant;
70 $ans->{correct_value}->class ne 'Formula';
71 # 70 #
72 # If it parsed OK, save the output forms and check if it is correct 71 # If it parsed OK, save the output forms and check if it is correct
73 # otherwise report an error 72 # otherwise report an error
74 # 73 #
75 if (defined $ans->{student_value}) { 74 if (defined $ans->{student_value}) {
97 my $cmp_error = $ans->{cmp_error} || 'cmp_error'; 96 my $cmp_error = $ans->{cmp_error} || 'cmp_error';
98 $self->$cmp_error($ans); 97 $self->$cmp_error($ans);
99 } else { 98 } else {
100 $ans->{ans_message} = $ans->{error_message} = 99 $ans->{ans_message} = $ans->{error_message} =
101 "Your answer isn't ".lc($ans->{cmp_class}). 100 "Your answer isn't ".lc($ans->{cmp_class}).
102 " (it looks like ".lc($ans->{student_value}->showClass(1)).")" 101 " (it looks like ".lc($ans->{student_value}->showClass).")"
103 if !$ans->{isPreview} && $ans->{showTypeWarnings} && !$ans->{error_message}; 102 if !$ans->{isPreview} && $ans->{showTypeWarnings} && !$ans->{error_message};
104 } 103 }
105} 104}
106 105
107# 106#
116# 115#
117# Class name for cmp error messages 116# Class name for cmp error messages
118# 117#
119sub cmp_class { 118sub cmp_class {
120 my $self = shift; my $ans = shift; 119 my $self = shift; my $ans = shift;
121 my $class = $self->showClass; 120 my $class = $self->showClass; $class =~ s/Real //;
121 return $class if $class =~ m/Formula/;
122 return "an Interval or Union" if $class =~ m/Interval/i; 122 return "an Interval or Union" if $class =~ m/Interval/i;
123 $class =~ s/Real //;
124 return $class; 123 return $class;
125} 124}
126 125
127# 126#
128# Student answer evaluation failed. 127# Student answer evaluation failed.
503 my $value = $ans->{entry_type}; 502 my $value = $ans->{entry_type};
504 my $ltype = $ans->{list_type}; 503 my $ltype = $ans->{list_type};
505 504
506 $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'value') 505 $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'value')
507 unless defined($value); 506 unless defined($value);
508 $value =~ s/(real|complex) //; $ans->{cmp_class} = $value; $value =~ s/^an? //; 507 $value =~ s/(real|complex) //; $ans->{cmp_class} = $value;
508 $value =~ s/^an? //; $value = 'formula' if $value =~ m/formula/;
509 $showTypeWarnings = $showHints = $showLengthHints = 0 if $ans->{isPreview}; 509 $showTypeWarnings = $showHints = $showLengthHints = 0 if $ans->{isPreview};
510 510
511 # 511 #
512 # Get the lists of correct and student answers 512 # Get the lists of correct and student answers
513 # (split formulas that return lists or unions)
513 # 514 #
514 my @correct = $self->value; 515 my @correct = ();
516 if ($self->class ne 'Formula') {@correct = $self->value}
517 else {@correct = Value::List->splitFormula($self,$ans)}
515 my $student = $ans->{student_value}; 518 my $student = $ans->{student_value};
516 my @student = ($student); 519 my @student = ($student);
517 if ($student->class eq $self->class && 520 if ($student->class eq 'Formula' && $student->type eq $self->type) {
521 @student = Value::List->splitFormula($student,$ans);
522 } elsif ($student->class ne 'Formula' && $student->class eq $self->class &&
518 ($allowParens || (!$student->{open} && !$student->{close}))) { 523 ($allowParens || (!$student->{open} && !$student->{close}))) {
519 @student = @{$student->{data}}; 524 @student = @{$student->{data}};
520 } elsif ($student->class eq 'Formula' && $student->type eq $self->type) {
521 #
522 # Convert a formula returning a list to a list of formulas
523 #
524 @student = (); my @entries;
525 if ($self->type eq 'List') {@entries = @{$student->{tree}{coords}}}
526 else {@entries = $student->{tree}->makeUnion}
527 foreach my $entry (@entries) {
528 my $v = Parser::Formula($entry);
529 $v = Parser::Evaluate($v) if (defined($v) && $v->isConstant);
530 push(@student,$v);
531 #
532 # In case there is an error evaluating the answer.
533 # (there shouldn't be, but you never know)
534 #
535 if (!defined($v)) {
536 my $cmp_error = $ans->{cmp_error} || 'cmp_error';
537 $self->$cmp_error; return;
538 }
539 } 525 }
540 } 526 return if $ans->{split_error};
527 if (scalar(@correct) == 0 && scalar(@student) == 0) {$ans->score(1); return}
541 528
542 # 529 #
543 # Initialize the score 530 # Initialize the score
544 # 531 #
545 my $maxscore = scalar(@correct); 532 my $M = scalar(@correct);
546 my $m = scalar(@student); 533 my $m = scalar(@student);
547 $maxscore = $m if ($m > $maxscore); 534 my $maxscore = ($m > $M)? $m : $M;
548 my $score = 0; my @errors; my $i = 0; 535 my $score = 0; my @errors; my $i = 0;
549 536
550 # 537 #
551 # Loop through student answers looking for correct ones 538 # Loop through student answers looking for correct ones
552 # 539 #
565 } 552 }
566 } 553 }
567 # 554 #
568 # Give messages about incorrect answers 555 # Give messages about incorrect answers
569 # 556 #
570 my $nth = ''; my $class = $self->cmp_class; 557 my $nth = ''; my $answer = 'answer'; my $class = $self->cmp_class;
571 if (scalar(@student) > 1) { 558 if (scalar(@student) > 1) {
572 $nth = ' '.$self->NameForNumber($i); 559 $nth = ' '.$self->NameForNumber($i);
573 $class = $ans->{cmp_class}; 560 $class = $ans->{cmp_class};
561 $answer = 'value';
574 } 562 }
575 if ($showTypeWarnings && !$typeMatch->typeMatch($entry,$ans) && 563 if ($showTypeWarnings && !$typeMatch->typeMatch($entry,$ans) &&
576 !($ans->{ignoreStrings} && $entry->class eq 'String')) { 564 !($ans->{ignoreStrings} && $entry->class eq 'String')) {
577 push(@errors,"Your$nth value isn't ".lc($class). 565 push(@errors,"Your$nth $answer isn't ".lc($class).
578 " (it looks like ".lc($entry->showClass(1)).")"); 566 " (it looks like ".lc($entry->showClass).")");
579 } elsif ($showHints && $m > 1) { 567 } elsif ($showHints && $m > 1) {
580 push(@errors,"Your$nth $value is incorrect"); 568 push(@errors,"Your$nth $value is incorrect");
581 } 569 }
582 } 570 }
583 571
587 if ($showLengthHints) { 575 if ($showLengthHints) {
588 $value =~ s/ or /s or /; # fix "interval or union" 576 $value =~ s/ or /s or /; # fix "interval or union"
589 push(@errors,"There should be more ${value}s in your $ltype") 577 push(@errors,"There should be more ${value}s in your $ltype")
590 if ($score == $m && scalar(@correct) > 0); 578 if ($score == $m && scalar(@correct) > 0);
591 push(@errors,"There should be fewer ${value}s in your $ltype") 579 push(@errors,"There should be fewer ${value}s in your $ltype")
592 if ($score < $maxscore && $score == scalar($self->value)); 580 if ($score < $maxscore && $score == $M);
593 } 581 }
594 582
595 # 583 #
596 # Finalize the score 584 # Finalize the score
597 # 585 #
598 $score = 0 if ($score != $maxscore && !$partialCredit); 586 $score = 0 if ($score != $maxscore && !$partialCredit);
599 $ans->score($score/$maxscore); 587 $ans->score($score/$maxscore);
600 push(@errors,"Score = $ans->{score}") if $ans->{debug}; 588 push(@errors,"Score = $ans->{score}") if $ans->{debug};
601 $ans->{error_message} = $ans->{ans_message} = join("\n",@errors); 589 $ans->{error_message} = $ans->{ans_message} = join("\n",@errors);
590}
591
592#
593# Split a formula that is a list or union into a
594# list of formulas (or Value objects).
595#
596sub splitFormula {
597 my $self = shift; my $formula = shift; my $ans = shift;
598 my @formula; my @entries;
599 if ($formula->type eq 'List') {@entries = @{$formula->{tree}{coords}}}
600 else {@entries = $formula->{tree}->makeUnion}
601 foreach my $entry (@entries) {
602 my $v = Parser::Formula($entry);
603 $v = Parser::Evaluate($v) if (defined($v) && $v->isConstant);
604 push(@formula,$v);
605 #
606 # There shouldn't be an error evaluating the formula,
607 # but you never know...
608 #
609 if (!defined($v)) {
610 $ans->{split_error} = 1;
611 my $cmp_error = $ans->{cmp_error} || 'cmp_error';
612 $self->$cmp_error; return;
613 }
614 }
615 return @formula;
602} 616}
603 617
604# 618#
605# Return the value if it is defined, otherwise use a default 619# Return the value if it is defined, otherwise use a default
606# 620#
613 627
614############################################################# 628#############################################################
615 629
616package Value::Formula; 630package Value::Formula;
617 631
618## FIXME: Need to check types for error reporting 632sub cmp_defaults {
633 my $self = shift;
634 return (
635 Value::Union::cmp_defaults($self,@_),
636 typeMatch => Value::Formula->new("(1,2]"),
637 ) if $self->type eq 'Union';
638
639 return Value::Real::cmp_defaults($self,@_) unless $self->type eq 'List';
640
641 return (
642 Value::List::cmp_defaults($self,@_),
643 typeMatch => Value::Formula->new(($self->createRandomPoints(1))[1]->[0]{data}[0]),
644 );
645}
646
647#
648# Get the types from the values of the formulas
649# and compare those.
650#
619sub typeMatch {1} 651sub typeMatch {
652 my $self = shift; my $other = shift; my $ans = shift;
653 return 1 if $self->type eq $other->type;
654 my $typeMatch = ($self->createRandomPoints(1))[1]->[0];
655 $other = eval {($other->createRandomPoints(1))[1]->[0]} if ($other->class eq 'Formula');
656 return 1 unless defined($other); # can't really tell, so don't report type mismatch
657 $typeMatch->typeMatch($other,$ans);
658}
620 659
621## FIXME: Do formula returning list as list of formulas
622## and formula returning union as union of formulas
623sub cmp_equal { 660sub cmp_equal {
624 my $self = shift; 661 my $self = shift; my $ans = shift;
662 #
663 # Get the problem's seed
664 #
625 $self->{context}->flags->set( 665 $self->{context}->flags->set(
626 random_seed => $self->getPG('$PG_original_problemSeed') 666 random_seed => $self->getPG('$PG_original_problemSeed')
627 ); 667 );
668
669 #
670 # Use the list checker if the formula is a list or union
671 # Otherwise use the normal checker
672 #
673 if ($self->type =~ m/^(List|Union)$/) {
674 Value::List::cmp_equal($self,$ans);
675 } else {
628 $self->SUPER::cmp_equal(@_); 676 $self->SUPER::cmp_equal($ans);
677 }
629} 678}
630 679
631# 680#
632# Replace the ones in Value::Formula 681# Replace the ones in Value::Formula
633# 682#

Legend:
Removed from v.2623  
changed lines
  Added in v.2624

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9