JT
JT
parserSolutionFor.pl
macro file. There is documentation on line for it. I think it will do what you want.Otherwise, you would need to make a Point object with a custom checker that checks to see if the point satisfies your formula. That is also possible, but why bother when it is already done for you by parserSolutionFor.
Davide
I'm trying to do some similar to what Travis is doing, but the parserSolutionFor.pl won't work in my case. Here's my problem:
--The first answer box, students enter a function in the form: P(t) = $a($b/$a)^(t/10)
--In the second answer box, they find the value of the function at a value: t = 20.
Depending on the values, it's possible due to rounding in the student's part to get the correct answer in the first box, but not the second. I don't want to require the student enter an exact value in the first box, but I'd like to tell them they need more accuracy if their answer in the second box is going to be too far off. I could give them an error message in the second box, but it would be better to have the error message where it "belongs".
I put an example of the code below.
Thanks -- rac
-----------------------------------------------------------
loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"contextFunctionAssign.pl");
TEXT(beginproblem());
$a=random(10,40,5);
$b=random(50,80,5); $a=21; $b=83;
Context()->texStrings;
BEGIN_TEXT
The population of a region is growing exponentially. There were $a
million people in 1980 (when \(t=0\)) and $b million people in 1990.
$PAR
(a) Find an exponential model, \(P(t)\), for the population (in millions of people)
at any time \(t\), in years after 1980. NOTE: Keep at least 4 decimal places in your answer.
$BR
Answer: \{ans_rule(35)\}
$PAR
(b) What population do you predict for the year 2000? NOTE: Round to one decimal place.
$BR
Predicted population in the year 2000 = \{ans_rule(25)\}
END_TEXT
Context()->normalStrings;
parser::Assignment->Allow;
Context()->variables->are(t=>'Real');
parser::Assignment->Function("P");
$ans_a = Compute("P(t)=$a*($b/$a)^(t/10)");
ANS($ans_a->cmp); #PUT IN A CHECK FOR t=2 AND APPROPRIATE ERROR MSG
$ans_b = Compute("round(10*$a*($b/$a)**(2))/10");
ANS($ans_b->cmp);
ENDDOCUMENT();
Oops, I forgot I used a slightly modified version of "parserAssignment.pl" which I called "contextFunctionAssign.pl". It just has a more customized error message and otherwise is "parserAssignment.pl
--rac
ANS($ans_a->cmp(checker => sub { my ($correct,$student) = @_; return 0 unless $correct == $student; my $Y = ($correct->eval(t=>20)->value)[1]; my $y = ($student->eval(t=>20)->value)[1]; Value->Error("Your answer is not precise enough. Try using more digits.") unless $Y == $y; return 1; }));This uses a custom answer checker that first checks if the student answer is correct as a function (but as you point out, it may be too forgiving to get the second part right).
If they are "equal", it gets the correct and student values at t = 20. The result of evaluating the Assignment object is actually still an assigment, so we take its value, which gives back the internal data; the formula's value is the second of these.
If the student answer doesn't match the correct one at t=20, then you give your error message (modify to suit). Finally, if we don't give the message, return that the answer is correct.
An alternative would be to force t=20 to be one of the test points, so that you are sure that the two match there, but in that case you would not get the error message. You might want to use limits=>[0,20]
anyway, since this might do a better job of telling when the two functions are correct to the proper precision (since otherwise it will be on [-2,2]
).
Hope that does it for you.
Davide
Davide,
It works great!
Thank you --rac
# for the first point
ANS($f->cmp);
# for the second point, what takes the place of $student_on_previous?
ANS($f->cmp(checker => sub { my ($correct,$student) = @_; return 0 unless ($correct == $student && $student <> $student_on_previous); Value->Error("Your two points must be different.") if $student == $student_on_previous;
return 1; }));
John,
I think you could use the MultiAnswer checker to do what you want to do. Here is a a link to the documentation:
http://webwork.maa.org/wiki/MultiAnswerProblems
--rac
ref
is not really he right way to do the type checking.
Here is my example of the problem that you requested (where three points are required):
DOCUMENT();
loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"parserMultiAnswer.pl",
);
Context("Point");
Parser::BOP::equality->Allow; # Allow equalities in formulas
$f = Compute("x^2 + y^2 = 1"); # The equation the students must match
#
# The is the MultiAnswer object, with three correct points
# (only used for showing the correct answer). It allows
# blank answers, so students can add one point at a
# time, and it checks that no two points are equal.
#
$ma = MultiAnswer("(1,0)","(0,1)","(-1,0)")->with(
function => $f,
allowBlankAnswers => 1,
checker => sub {
my ($correct,$student,$self) = @_;
my @student = @$student; # The array of student answers
my $n = scalar(@student); # How many are in the array
my @correct = (0) x $n; # An array of that many zeros (assume none correct)
foreach my $i (0..$n-1) { # Loop through the student answers
next unless $student[$i]->classMatch("Point"); # Go on if the answer is blank
my ($x,$y) = $student[$i]->value; # Get the x and y coordinates
if ($self->{function}->eval(x=>$x,y=>$y)) { # If the equation is satisfied (1 if equal, 0 if not)
$correct[$i] = 1; # Indicate answer is correct
foreach my $j (0..$i-1) { # Look through previous points
if ($student[$j] == $student[$i]) { # If this point equals a previous one
$self->setMessage($i+1,"This point is the same as the ".Value->NameForNumber($j+1)." one");
$correct[$i] = 0; # Give a warning and mark incorrect
break; # Stop looking through previous points
}
}
}
}
return @correct; # Return the array indicating which are correct
}
);
Context()->texStrings;
BEGIN_TEXT
Three distinct points that lie on \($f\) are:$BR
\{$ma->ans_rule(15)\}, \{$ma->ans_rule(15)\}, and \{$ma->ans_rule(15)\}
END_TEXT
Context()->normalStrings;
ANS($ma->cmp);
ENDDOCUMENT();
Note that this checker works for any number of points. The checker itself could be packaged into a separate macro file for re-use, if so desired. For example, the macro file could contain
$points_on_function = sub { my ($correct,$student,$self) = @_; ... return @correct; };then you could use it as
MultiAnswer("(1,0)","(0,1"),"(-1,0)")->with( function => $f, allowBlankAnswers => 1, checker => $points_on_function, );In fact, in that case, you could do something like
sub PointsOnFunction { my $f = shift; return MultiAnswer(@_)->with( function => Compute($f), allowBlankAnswers => 1, checker => $points_on_function, ); }in the macro file, and then just do
PointsOnFunction("x^2+y^2=1","(1,0)","(0,1)","(-1,0)");in the main program. Note that none of the approaches above checks that the correct answers are actually on the function given, so we assume you have them right. :-)
Hope that does the trick.
Davide