| … | |
… | |
| 186 | # |
186 | # |
| 187 | sub cmp_class { |
187 | sub cmp_class { |
| 188 | my $self = shift; my $ans = shift; |
188 | my $self = shift; my $ans = shift; |
| 189 | my $class = $self->showClass; $class =~ s/Real //; |
189 | my $class = $self->showClass; $class =~ s/Real //; |
| 190 | return $class if $class =~ m/Formula/; |
190 | return $class if $class =~ m/Formula/; |
| 191 | return "an Interval or Union" if $class =~ m/Interval/i; |
191 | return "an Interval, Set or Union" if $class =~ m/Interval|Set|Union/i; |
| 192 | return $class; |
192 | return $class; |
| 193 | } |
193 | } |
| 194 | |
194 | |
| 195 | # |
195 | # |
| 196 | # Student answer evaluation failed. |
196 | # Student answer evaluation failed. |
| … | |
… | |
| 768 | return 0 unless ref($other) && $other->class ne 'Formula'; |
768 | return 0 unless ref($other) && $other->class ne 'Formula'; |
| 769 | return $other->length == 2 && |
769 | return $other->length == 2 && |
| 770 | ($other->{open} eq '(' || $other->{open} eq '[') && |
770 | ($other->{open} eq '(' || $other->{open} eq '[') && |
| 771 | ($other->{close} eq ')' || $other->{close} eq ']') |
771 | ($other->{close} eq ')' || $other->{close} eq ']') |
| 772 | if $other->type =~ m/^(Point|List)$/; |
772 | if $other->type =~ m/^(Point|List)$/; |
| 773 | $other->type =~ m/^(Interval|Union)$/; |
773 | $other->type =~ m/^(Interval|Union|Set)$/; |
| 774 | } |
774 | } |
| 775 | |
775 | |
| 776 | sub cmp_compare { |
776 | sub cmp_compare { |
| 777 | my $self = shift; my $other = shift; my $ans = shift; |
777 | my $self = shift; my $other = shift; my $ans = shift; |
| 778 | my $oldignore = $self->{requireParenMatch}; |
778 | my $oldignore = $self->{requireParenMatch}; |
| … | |
… | |
| 805 | $self->cmp_Error($ans,@errors); |
805 | $self->cmp_Error($ans,@errors); |
| 806 | } |
806 | } |
| 807 | |
807 | |
| 808 | ############################################################# |
808 | ############################################################# |
| 809 | |
809 | |
| 810 | package Value::Union; |
810 | package Value::Set; |
| 811 | |
811 | |
| 812 | sub typeMatch { |
812 | sub typeMatch { |
| 813 | my $self = shift; my $other = shift; |
813 | my $self = shift; my $other = shift; |
| 814 | return 0 unless ref($other) && $other->class ne 'Formula'; |
814 | return 0 unless ref($other) && $other->class ne 'Formula'; |
| 815 | return $other->length == 2 && |
815 | return $other->length == 2 && |
| 816 | ($other->{open} eq '(' || $other->{open} eq '[') && |
816 | ($other->{open} eq '(' || $other->{open} eq '[') && |
| 817 | ($other->{close} eq ')' || $other->{close} eq ']') |
817 | ($other->{close} eq ')' || $other->{close} eq ']') |
| 818 | if $other->type =~ m/^(Point|List)$/; |
818 | if $other->type =~ m/^(Point|List)$/; |
| 819 | $other->type =~ m/^(Interval|Union)/; |
819 | $other->type =~ m/^(Interval|Union|Set)/; |
|
|
820 | } |
|
|
821 | |
|
|
822 | # |
|
|
823 | # Use the List checker for sets, in order to get |
|
|
824 | # partial credit. Set the various types for error |
|
|
825 | # messages. |
|
|
826 | # |
|
|
827 | sub cmp_defaults {( |
|
|
828 | Value::List::cmp_defaults(@_), |
|
|
829 | typeMatch => 'Value::Real', |
|
|
830 | list_type => 'a set', |
|
|
831 | entry_type => 'a number', |
|
|
832 | removeParens => 0, |
|
|
833 | showParenHints => 1, |
|
|
834 | )} |
|
|
835 | |
|
|
836 | # |
|
|
837 | # Use the list checker if the student answer is a set |
|
|
838 | # otherwise use the standard compare (to get better |
|
|
839 | # error messages |
|
|
840 | # |
|
|
841 | sub cmp_equal { |
|
|
842 | my ($self,$ans) = @_; |
|
|
843 | Value::List::cmp_equal(@_) |
|
|
844 | if $ans->{student_value}->type eq 'Set'; |
|
|
845 | Value::cmp_equal(@_); |
|
|
846 | } |
|
|
847 | |
|
|
848 | ############################################################# |
|
|
849 | |
|
|
850 | package Value::Union; |
|
|
851 | |
|
|
852 | sub typeMatch { |
|
|
853 | my $self = shift; my $other = shift; |
|
|
854 | return 0 unless ref($other) && $other->class ne 'Formula'; |
|
|
855 | return $other->length == 2 && |
|
|
856 | ($other->{open} eq '(' || $other->{open} eq '[') && |
|
|
857 | ($other->{close} eq ')' || $other->{close} eq ']') |
|
|
858 | if $other->type =~ m/^(Point|List)$/; |
|
|
859 | $other->type =~ m/^(Interval|Union|Set)/; |
| 820 | } |
860 | } |
| 821 | |
861 | |
| 822 | # |
862 | # |
| 823 | # Use the List checker for unions, in order to get |
863 | # Use the List checker for unions, in order to get |
| 824 | # partial credit. Set the various types for error |
864 | # partial credit. Set the various types for error |
| 825 | # messages. |
865 | # messages. |
| 826 | # |
866 | # |
| 827 | sub cmp_defaults {( |
867 | sub cmp_defaults {( |
| 828 | Value::List::cmp_defaults(@_), |
868 | Value::List::cmp_defaults(@_), |
| 829 | typeMatch => 'Value::Interval', |
869 | typeMatch => 'Value::Interval', |
| 830 | list_type => 'an interval or union', |
870 | list_type => 'an interval, set or union', |
|
|
871 | short_type => 'a union', |
| 831 | entry_type => 'an interval', |
872 | entry_type => 'an interval or set', |
| 832 | )} |
873 | )} |
| 833 | |
874 | |
| 834 | sub cmp_equal {Value::List::cmp_equal(@_)} |
875 | sub cmp_equal {Value::List::cmp_equal(@_)} |
| 835 | |
876 | |
| 836 | ############################################################# |
877 | ############################################################# |
| … | |
… | |
| 885 | # |
926 | # |
| 886 | # get the paramaters |
927 | # get the paramaters |
| 887 | # |
928 | # |
| 888 | my $showHints = getOption($ans,'showHints'); |
929 | my $showHints = getOption($ans,'showHints'); |
| 889 | my $showLengthHints = getOption($ans,'showLengthHints'); |
930 | my $showLengthHints = getOption($ans,'showLengthHints'); |
| 890 | my $showParenHints = getOption($ans,'showLengthHints'); |
931 | my $showParenHints = getOption($ans,'showParenHints'); |
| 891 | my $partialCredit = getOption($ans,'partialCredit'); |
932 | my $partialCredit = getOption($ans,'partialCredit'); |
| 892 | my $requireParenMatch = $ans->{requireParenMatch}; |
933 | my $requireParenMatch = $ans->{requireParenMatch}; |
| 893 | my $typeMatch = $ans->{typeMatch}; |
934 | my $typeMatch = $ans->{typeMatch}; |
| 894 | my $value = $ans->{entry_type}; |
935 | my $value = $ans->{entry_type}; |
| 895 | my $ltype = $ans->{list_type} || lc($self->type); |
936 | my $ltype = $ans->{list_type} || lc($self->type); |
|
|
937 | my $stype = $ans->{short_type} || $ltype; |
| 896 | |
938 | |
| 897 | $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'value') |
939 | $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'value') |
| 898 | unless defined($value); |
940 | unless defined($value); |
| 899 | $value =~ s/(real|complex) //; $ans->{cmp_class} = $value; |
941 | $value =~ s/(real|complex) //; $ans->{cmp_class} = $value; |
| 900 | $value =~ s/^an? //; $value = 'formula' if $value =~ m/formula/; |
942 | $value =~ s/^an? //; $value = 'formula' if $value =~ m/formula/; |
| 901 | $ltype =~ s/^an? //; |
943 | $ltype =~ s/^an? //; $stype =~ s/^an? //; |
| 902 | $showHints = $showLengthHints = 0 if $ans->{isPreview}; |
944 | $showHints = $showLengthHints = 0 if $ans->{isPreview}; |
| 903 | |
945 | |
| 904 | # |
946 | # |
| 905 | # Get the lists of correct and student answers |
947 | # Get the lists of correct and student answers |
| 906 | # (split formulas that return lists or unions) |
948 | # (split formulas that return lists or unions) |
| … | |
… | |
| 930 | if ($showParenHints && !($ans->{ignoreStrings} && $student->type eq 'String')) { |
972 | if ($showParenHints && !($ans->{ignoreStrings} && $student->type eq 'String')) { |
| 931 | my $message = "The parentheses for your $ltype "; |
973 | my $message = "The parentheses for your $ltype "; |
| 932 | if (($cOpen || $cClose) && ($sOpen || $sClose)) |
974 | if (($cOpen || $cClose) && ($sOpen || $sClose)) |
| 933 | {$message .= "are of the wrong type"} |
975 | {$message .= "are of the wrong type"} |
| 934 | elsif ($sOpen || $sClose) {$message .= "should be removed"} |
976 | elsif ($sOpen || $sClose) {$message .= "should be removed"} |
| 935 | else {$message .= "are missing"} |
977 | else {$message .= "seem to be missing"} |
| 936 | $self->cmp_Error($ans,$message) unless $ans->{isPreview}; |
978 | $self->cmp_Error($ans,$message) unless $ans->{isPreview}; |
| 937 | } |
979 | } |
| 938 | return; |
980 | return; |
| 939 | } |
981 | } |
| 940 | |
982 | |
| … | |
… | |
| 964 | # |
1006 | # |
| 965 | # Give hints about extra or missing answers |
1007 | # Give hints about extra or missing answers |
| 966 | # |
1008 | # |
| 967 | if ($showLengthHints) { |
1009 | if ($showLengthHints) { |
| 968 | $value =~ s/ or /s or /; # fix "interval or union" |
1010 | $value =~ s/ or /s or /; # fix "interval or union" |
| 969 | push(@errors,"There should be more ${value}s in your $ltype") |
1011 | push(@errors,"There should be more ${value}s in your $stype") |
| 970 | if ($score < $maxscore && $score == $m); |
1012 | if ($score < $maxscore && $score == $m); |
| 971 | push(@errors,"There should be fewer ${value}s in your $ltype") |
1013 | push(@errors,"There should be fewer ${value}s in your $stype") |
| 972 | if ($score < $maxscore && $score == $M && !$showHints); |
1014 | if ($score < $maxscore && $score == $M && !$showHints); |
| 973 | } |
1015 | } |
| 974 | |
1016 | |
| 975 | # |
1017 | # |
| 976 | # Finalize the score |
1018 | # Finalize the score |
| … | |
… | |
| 1059 | # list of formulas (or Value objects). |
1101 | # list of formulas (or Value objects). |
| 1060 | # |
1102 | # |
| 1061 | sub splitFormula { |
1103 | sub splitFormula { |
| 1062 | my $self = shift; my $formula = shift; my $ans = shift; |
1104 | my $self = shift; my $formula = shift; my $ans = shift; |
| 1063 | my @formula; my @entries; |
1105 | my @formula; my @entries; |
| 1064 | if ($formula->type eq 'List') {@entries = @{$formula->{tree}{coords}}} |
1106 | if ($formula->type eq 'Union') {@entries = $formula->{tree}->makeUnion} |
| 1065 | else {@entries = $formula->{tree}->makeUnion} |
1107 | else {@entries = @{$formula->{tree}{coords}}} |
| 1066 | foreach my $entry (@entries) { |
1108 | foreach my $entry (@entries) { |
| 1067 | my $v = Parser::Formula($entry); |
1109 | my $v = Parser::Formula($entry); |
| 1068 | $v = Parser::Evaluate($v) if (defined($v) && $v->isConstant); |
1110 | $v = Parser::Evaluate($v) if (defined($v) && $v->isConstant); |
| 1069 | push(@formula,$v); |
1111 | push(@formula,$v); |
| 1070 | # |
1112 | # |