-
-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fraction context and FormulaWithUnits #1137
Comments
With #1107 and #1108 you can do DOCUMENT();
loadMacros('PGstandard.pl', 'PGML.pl', 'contextFraction.pl', 'contextUnits.pl', 'PGcourse.pl');
Context(context::Fraction::extending('Units'))->withUnitsFor('time');
$ans = Compute('1/2 min');
BEGIN_PGML
Enter [`[$ans]`]: [_]{$ans}
END_PGML
ENDDOCUMENT(); I think that achieves what you are asking. So I think that #1107 and #1108 do make this moot. On a side note, I think that MathQuill will need some more work on things related to entering units. I noticed that typing I think that the |
I agree that the new units context should resolve the issue. But I took a look into it just to see what was happening, and the issue is with these lines: pg/lib/Parser/Legacy/NumberWithUnits.pm Lines 368 to 372 in 10746b1
With my $other_value = $other->value; sets The modifications of the internals of As it currently stands, the usual The better approach is to make the The key to making The only difficulty is that the units do still need to be split off the student number originally, since the parser doesn't handle them, and then the student answer needs to be turned into a NumberWithUnits or FormulaWithUnits object before the comparison is made. The first step is still done in Here is a patch file that implements this idea: index 3fa90fbe..1eb38f62 100644
--- a/lib/Parser/Legacy/NumberWithUnits.pm
+++ b/lib/Parser/Legacy/NumberWithUnits.pm
@@ -69,8 +69,10 @@ sub new {
$num = $self->makeValue($num, context => $context);
my %Units = getUnits($units);
Value::Error($Units{ERROR}) if ($Units{ERROR});
+ my @baseUnits = grep {/./} (map { $Units{$_} && $_ ne 'factor' ? $_ : '' } keys %Units);
+ $num->{baseUnits} = join(',', map {"$_=$Units{$_}"} main::lex_sort(@baseUnits));
$num->{units} = $units;
- $num->{units_ref} = \%Units;
+ $num->{factor} = $Units{factor};
$num->{isValue} = 1;
$num->{correct_ans} .= ' ' . $units if defined $num->{correct_ans};
$num->{correct_ans_latex_string} .= ' ' . TeXunits($units) if defined $num->{correct_ans_latex_string};
@@ -186,46 +188,37 @@ sub cmp_parse {
$self->cmp_Error($ans, "Your units can only contain one division");
return $ans;
}
- my %Units = getUnits($units);
- if ($Units{ERROR}) { $self->cmp_Error($ans, $Units{ERROR}); return $ans }
-
- # Check the numeric part of the answer.
- $ans->{student_ans} = $num;
- $self->{student_units_ref} = \%Units;
+ $ans->{units} = $units;
+ $ans->{student_ans} = $num;
+ $ans = $self->cmp_reparse($ans);
- $ans = $self->cmp_reparse($ans);
+ $self->cmp_Error($ans, "The units for your answer are not correct")
+ if $ans->{ans_message} eq ''
+ && $ans->score eq 0
+ && !$ans->{isPreview}
+ && $ans->{student_value}{baseUnits} ne $ans->{correct_value}{baseUnits};
- delete $self->{student_units_ref};
+ return $ans;
+}
- # Adjust the answer strings.
+sub cmp_preprocess {
+ my $self = shift;
+ my $ans = shift;
+ my $units = $ans->{units};
+ $ans->{student_value} = $self->new($ans->{student_value}, $units);
$ans->{student_ans} .= " " . $units;
$ans->{preview_text_string} .= " " . $units;
$ans->{preview_latex_string} .= '\ ' . TeXunits($units);
-
- return $ans unless $ans->{ans_message} eq '';
- #
- # Check that we have an actual number, and check the units
- #
- if (!defined($ans->{student_value}) || $self->checkStudentValue($ans->{student_value})) {
- $ans->{student_value} = undef;
- $ans->score(0);
- $self->cmp_Error($ans, "Your answer doesn't look like a number with units");
- } else {
- $ans->{student_value} = $self->new($num, $units);
- foreach my $funit (keys %{ $self->{units_ref} }) {
- next if $funit eq 'factor';
- next if $self->{units_ref}{$funit} == $Units{$funit};
- $self->cmp_Error($ans, "The units for your answer are not correct")
- unless $ans->{isPreview};
- $ans->score(0);
- last;
- }
- }
- return $ans;
}
sub cmp_reparse { Value::cmp_parse(@_) }
+sub compare {
+ my ($self, $l, $r) = Value::checkOpOrderWithPromote(@_);
+ return $l->{baseUnits} cmp $r->{baseUnits} unless $l->{baseUnits} eq $r->{baseUnits};
+ return $self->demote($l) * $l->{factor} <=> $self->demote($r) * $r->{factor};
+}
+
sub add_fundamental_unit {
my $unit = shift;
$fundamental_units->{$unit} = 0;
@@ -270,6 +263,8 @@ sub makeValue {
my $value = shift;
my %options = (context => $self->context, @_);
my $num = Value::makeValue($value, %options);
+ $num = Value::makeValue($num->eval, %options)
+ if $num->type eq 'Number' && !Value::classMatch($num, 'Real', 'Formula');
Value::Error("A number with units must be a constant, not %s", lc(Value::showClass($num)))
unless Value::isReal($num);
return $num;
@@ -278,7 +273,7 @@ sub makeValue {
sub checkStudentValue {
my $self = shift;
my $student = shift;
- return $student->class ne 'Real';
+ return $student->isReal;
}
sub promote {
@@ -290,26 +285,10 @@ sub promote {
return $self->new($context, $x, @_);
}
-# This saves the current value of the student answer, and then compares the answers with the parent compare method.
-# Then the student value is put back to what it was. The correct value will be the one that has the units refs.
-sub compare {
- my ($self, $other, $flag) = @_;
-
- if (defined $self->{units_ref} && defined $self->{student_units_ref}) {
- my $other_value = $other->value;
- $other->{data}[0] = $other_value * $self->{student_units_ref}{factor} / $self->{units_ref}{factor};
- my $ret = $self->SUPER::compare($other, $flag);
- $other->{data}[0] = $other_value;
- return $ret;
- } elsif (defined $other->{units_ref} && defined $other->{student_units_ref}) {
- my $self_value = $self->value;
- $self->{data}[0] = $self_value * $other->{student_units_ref}{factor} / $other->{unit_ref}{factor};
- my $ret = $self->SUPER::compare($other, $flag);
- $self->{data}[0] = $self_value;
- return $ret;
- }
-
- return $self->SUPER::compare($other, $flag);
+sub demote {
+ my $self = shift;
+ my $num = shift;
+ $self->Package("Real")->make($self->context, $num->value);
}
sub string {
@@ -349,47 +328,10 @@ sub checkStudentValue {
return $student->type ne 'Number';
}
-# This does much the same as the compare method for a Parser::Legacy::NumberWithUnits object, except that it tries to
-# adjust the formula tree of the student answer. If the student answer is not a formula, then it adjusts its value.
-# The comparison is made by the parent class method. Then the adjustment is undone.
-sub compare {
- my ($self, $other, $flag) = @_;
-
- if (defined $self->{units_ref} && defined $self->{student_units_ref}) {
- if (defined $other->{tree}) {
- my $other_tree = $other->{tree};
- $other->{tree} = $other->Item('BOP')->new($other, '*', $other_tree,
- $other->Item('Value')->new($other, $self->{student_units_ref}{factor} / $self->{units_ref}{factor})
- );
- my $ret = $self->SUPER::compare($other, $flag);
- $other->{tree} = $other_tree;
- return $ret;
- } else {
- my $other_value = $other->value;
- $other->{data}[0] = $other_value * $self->{student_units_ref}{factor} / $self->{units_ref}{factor};
- $ret = $self->SUPER::compare($other, $flag);
- $other->{data}[0] = $other_value;
- return $ret;
- }
- } elsif (defined $other->{units_ref} && defined $other->{student_units_ref}) {
- if (defined $self->{tree}) {
- my $self_tree = $self->{tree};
- $self->{tree} = $self->Item('BOP')->new($self, '*', $self_tree,
- $self->Item('Value')->new($self, $other->{student_units_ref}{factor} / $other->{units_ref}{factor})
- );
- my $ret = $self->SUPER::compare($other, $flag);
- $self->{tree} = $self_tree;
- return $ret;
- } else {
- my $self_value = $self->value;
- $self->{data}[0] = $self_value * $other->{student_units_ref}{factor} / $other->{units_ref}{factor};
- $ret = $self->SUPER::compare($other, $flag);
- $self->{data}[0] = $self_value;
- return $ret;
- }
- }
-
- return $self->SUPER::compare($other, $flag);
+sub demote {
+ my $self = shift;
+ my $num = shift;
+ $self->Package("Formula")->new($self->context, $num->{tree});
}
sub string { |
PS, these changes also allow |
Suppose a problem has an answer like
1/2 min
. If you useparserNumberWithUnits.pl
, it won't keep the fraction form, and that is important in a certain pedagogical context I am working with.So I tried something like this, combining
parserFormulaWithUnits.pl
andcontextFraction.pl
.And submitting the correct answer, it is not accepted as correct. I think #1107 might make this moot, in that it could offer a better way to have a fraction with units. Also, it looks like I can accomplish what I want with
FormulaWithUnits
andNumeric
context where I setreduceConstants
to 0. But this did seem like a possible bug to report.The text was updated successfully, but these errors were encountered: