WeBWorK Problems

Checking submitted answers into a formula

by John Travis -
Number of replies: 9
This may be a real easy problem to fix but I can't find a solution on the wiki.  I would like to take an answer entered as a point (x,y) and check to see if that answer is a solution to an equation of the form f(x,y) = constant.

For example, if I were trying to see if an entered answer falls on a circle with radius sqrt(r), my attempt has been
ANS($r->cmp($f->eval(
x=>\{ ANS_NUM_TO_NAME(1) \},
y=>\{ ANS_NUM_TO_NAME(2) \} ) )
);
which of course does not work since I'm asking here.  Of course I'd really like to also get rid of any direct reference to the entered answers.

Thanks,

JT

Re: Checking submitted answers into a formula

by Davide Cervone -
Look at the 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

Re: Checking submitted answers into a formula

by Robin Cruz -

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(); In reply to Robin Cruz Re: Checking submitted answers into a formula by Robin Cruz - 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 In reply to Robin Cruz Re: Checking submitted answers into a formula by Davide Cervone - Here is one approach:  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

by Robin Cruz -

Davide,

It works great!

Thank you --rac

Re: Checking submitted answers into a formula

by John Travis -
I knew it had to be something obvious to everyone else.  Glad to have stirred up Robin's question as well.  Thanks Davide.

JT

Re: Checking submitted answers into a formula

by John Travis -
Now I want the student to enter two points in two different blanks and want the second to be different from the first...or three points in three blanks, etc.

Indeed, each of them is correct using the same ANS($f->cmp); but I want enforce the second point to not be the same as the first one. Perhaps something like # 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;
}));


Re: Checking submitted answers into a formula

by Robin Cruz -

John,

I think you could use the MultiAnswer checker to do what you want to do. Here is a a link to the documentation:

--rac

Re: Checking submitted answers into a formula

by Davide Cervone -
Robin is right, the MultiAnswer object is what you want, here. I don't really care for the example from the wiki, however, as the use of 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();

"PGstandard.pl",
"MathObjects.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),

    PointsOnFunction("x^2+y^2=1","(1,0)","(0,1)","(-1,0)");