Forum archive 2000-2006

Thomas Hagedorn - An Answer Evaluator that takes several types of input simultaneously

Thomas Hagedorn - An Answer Evaluator that takes several types of input simultaneously

by Arnold Pizer -
Number of replies: 0
inactiveTopicAn Answer Evaluator that takes several types of input simultaneously topic started 5/28/2005; 12:58:28 AM
last post 6/6/2005; 1:13:42 PM
userThomas Hagedorn - An Answer Evaluator that takes several types of input simultaneously  blueArrow
5/28/2005; 12:58:28 AM (reads: 1497, responses: 6)
I was wondering if the following answer evaluator already exists or if someone could explain how to modify the current answer evaluators to handle the following situation.

The student inputs several pieces of data at the same time and the answer evaluator does some comparison of these pieces of data to see if the entered data satisfies the desired relation.

For example, you might want:

1. A student to enter a function and two values (or points) such that the function has the same value when evaluated at both points.

2. A student to enter a vector v and constants A1, A2 such that v =A1 w+ A2z, where w, z are two previously given vectors.

3. A student enters two non-diagonal, non-identical matrices that are supposed to commute with each other.

In each case, I would ideally like to see a version of the answer evaluator with the following form (which I'm basing on Bob Byerly's generic_cmp answer evaluator). For problem 2, it would look like

ANS(generic_cmp("20,5,5", type => 'Vector, Point, Point', length => '3,1,1', checker => ~~&check));

where "20,5,5" means that the Vector gets a box with length 20, and each point gets an answer box with length 5.

Here, sub checktrue is the routine that checks if the desired relation is true.

then

sub check{ my $vector = shift(); my $point1 = shift(); my $point2 = shift(); }

would give me the entered vector and points so that I could do the checking of the condition.

-Tom

<| Post or View Comments |>


userDavide P. Cervone - Re: An Answer Evaluator that takes several types of input simultaneously  blueArrow
5/29/2005; 8:24:54 AM (reads: 1775, responses: 0)
Before looking at possible solutions, there are a couple of technical issues with your suggestion that should be addressed.

First, the answer checker does not produce the answer blanks, so the "20,5,5" part is not appropirate here. The answer blanks are created by hand as part of the text of the problem so that you can include the appropriate wording around them, which would be difficult with your approach.

Second, the answer checker needs to be given a "correct answer", even if you are not going to check against it specifically, so that it has one to display when the student asks for correct answers to be displayed. So you will need to pass one even though you won't use it to tell if the student's answer is correct.

Since you will be passing a correct answer, you will be able to tell the type of answer and the length from the passed correct answer, so there is no need for the type or length values that you are passing.

That being said, there are several approaches to make an answer checker like what you have in mind. You could do it now with the new Parser's answer checkers if you were willing to have the student enter his or her answers as a list (rather than in separate blanks). To do this, you could use custom_list_cmp() that is in answerCustom.pl. There is some documentation within that file, but the main idea is that the check() routine would be passed two arrays: one with the correct answers and one with the student answers. No type-checking will have been done, however, so you will need to do the checking for number of answers, type of answer (based on the corresponding correct answer), and length of vectors, then report the appropriate errors yourself. You will also be repsonsible for dealing with any partial credit that might be awarded for portions of the answer being correct (though the examples you describe are all-or-nothing ones).

In this approach, the student enters his or her answer in a single answer blank, separating the parts by commas, and gets one score for that answer in the answer-analysis section at the top of the page when the answers are submitted.

If you want to have separate answer blanks (which is what your description suggests), then things are a bit more complicated. Since answer checkers usually are tied to single answer blanks, you probably need to use named answer rules, and then have your answer checker take the names of those blank as part of its data so it can look up the data entered in the other blanks by hand. It would also need to produce extra answer checkers so that each blank had an associated checker, and they would have to be coordinated so they would report the correct results. This is a bit tricky. I did something like this with a function-composition answer checker in answerComposition.pl in pg/macros, though I was never fully satisfied with it. It does show how to produce multiple answer checkers that are coordianted, however (note that, in this case, the actual answer checking is done as the answer checkers are set up, and the installed answer checkers are just dummys that report the errors and scores that have already been determined before they are called).

In this approach, each part of the answer is in a separate answer blank, and each has its own row in the answer report at the top of the page. It is easy to give partial credit and to report individual errors for different parts.

A third approach would be to have individual answer blanks but have only one row in the answer report. The matrix answer checkers work this way, but I have never looked into how they do it, so I'm afraid I can't give advice on that. I suspect it is based on naming the answer blanks in a certain way so that several answer blanks are associated with a single answer checker. You would have to look throught the matric macros code to find out for sure, but that may be the mechanism that is closest to what you want.

If you want to try either of the first two approaches, I can give more details.

Good luck with the project.

Davide

<| Post or View Comments |>


userBob Byerly - Re: An Answer Evaluator that takes several types of input simultaneously  blueArrow
5/29/2005; 9:31:15 AM (reads: 1792, responses: 1)
I once briefly played around with the following approach to achieve something like what Tom wants. It wasn't entirely successful, but I may have given up too soon.

One can directly insert html code in a problem to put in new input fields. The problem could also contain a regular WebWorK answer blank that is hidden from the user. The new input fields could use the "onChange" attribute to invoke a Javascript function that, whenever the user types in anything, would copy the results of the new fields into the regular answer blank, whose contents would be processed by an answer evaluator whenever the user submitted the problem. Thus, one answer evaluator would handle the input from several answer blanks. Also, it's not hard to arrange for the new input fields to contain old answers.

Since I'm not really a Javascript expert, when I tried this I got something that worked beautifully on some browsers but not others. I think I know what I did wrong but haven't had time to go back and play with it again. Comments from Javascript experts are welcome!

Anyway, I agree with Tom that a multi-blank answer evaluator that's easy to use from the problem-writer's point of view would be very useful. I would be happy to help with developing such a thing.

Here's another suggestion for a problem-writer interface:

Create a new parser type, e.g., MultiPart which would probably be an extension of the List type. For Tom's example number 1, it might be invoked like the following:

 

$f = Function('x^2');



$my_prob = Multipart( $f, 1, -1 );



BEGIN_PROBLEM
Enter a function that is not one-to-one: (f(x) = )
{$my_prob->ans_rule(1,20)}



Enter two values at which your function has the same value:
{$my_prob -> ans_rule(2,5)} and {$my_prob ->ans_rule(3,5) }.
END_PROBLEM



ANS($my_prob->cmp(checker=>~~&my_checker));

where my_checker is a function that checks to see whether the student answer meets the desired condition.

Looks to me like the only major extension to the existing List type would be in handling the multiple answer blanks. Whether this is best done server-side or client-side (using Javascript) I don't know. Davide's suggestion of looking at the matrix answer checkers might be helpful.

(Possible subtlety: What would happen if one or more if the student entered a list into one or more of the answer blanks?)

Bob

<| Post or View Comments |>


userDavide P. Cervone - Re: An Answer Evaluator that takes several types of input simultaneously  blueArrow
5/29/2005; 11:01:31 AM (reads: 2152, responses: 0)
Bob:

I like your idea, and it would work within the current answer checker framework. Your idea of having the object produce the answer rules is a key one, and I wish I had thought of it myself. :-) The object can keep track of the answer-rule numbers itself, so you can just do $my_prob->ans_rule(20) and $my_prob->ans_rule(5) and have it increment an internal counter. (The order of the entries in the Multipart() list should match the order of the answer blanks.)

I had been thinking about how to do this generically, and wasn't able to come up with a way that I liked. But now that you have suggested one that works, I probably won't be able to rest until I've implemented it.

In terms of your subtlety, there should not be any problem about that. If you want to be able to have one of the blanks accept a list of answers, you can, and if you don't, the type-checking can report an appropriate error for that case. Since nested lists are allowed int he Parser (unlike native perl lists), there is no problem about not being able to recognize the situation, if that is what you were worried about.

As for your question about javascript versus server, I vote for server side for this. While javascript support is improving, I would hate to rely on it for something as fundamental as this. Also, since javascript can be disabled by the user, there is more chance that it will not work as expected, and may make it impossible for the user to get his or her answer accepted. I think we should avoid that if at all possible.

Davide

<| Post or View Comments |>


userDavide P. Cervone - Re: An Answer Evaluator that takes several types of input simultaneously  blueArrow
6/5/2005; 9:28:10 PM (reads: 1837, responses: 0)
OK, I have just committed a slew of things to the CVS repository that will do what you are asking for. I have implemented a MultiPart obejct (in pg/macros/parserMultiPart.pl) that lets you handle several answer blanks with a single answer checker. This was not too hard, and could be done without any changes to the existing Parser, but as I was looking at the matrix macros to see how they handled the multiple answer blanks, I realized that I really needed something like that that would work with the Parser objects. So I spent the weekend implementing ans_array methods for Matrix, Vector and Point objects, and making them interact properly with the MultiPart object. The result is pretty general, and (I hope) useful.

Here is the documentation from the parserMultiPart.pl file. I've also included information from the CVS log for pg/lib/Value/AnswerChecker.pm about the new ans_array method for Matrix, Vector and Point objects.

Davide

--

MultiPart objects let you tie several answer blanks to a single answer checker, so you can have the answer in one blank influence the answer in another. The MultiPart can produce either a single result in the answer results area, or a separate result for each blank.

To create a MultiPart pass a list of answers to MultiPart() in the order they will appear in the problem. For example:

 

    $mp = MultiPart("x^2",-1,1);

or

 

    $mp = MultiPart(Vector(1,1,1),Vector(2,2,2))->with(singleResult=>1);

Then, use $mp->ans_rule to create answer blanks for the various parts just as you would ans_rule. You can pass the width of the blank, which defaults to 20 otherwise. For example:

 

    BEGIN_TEXT
\(f(x)\) = \{$mp->ans_rule(20)\} produces the same value
at \(x\) = \{$mp->ans_rule(10)\} as it does at \(x\) = \{$mp->ans_rule(10)\}.
END_TEXT

Finally, call $mp->cmp to produce the answer checker(s) used in the MultiPart. You need to provide a checker routine that will be called to determine if the answers are correct or not. The checker will only be called if the student answers have no syntax errors and their types match the types of the professor's answers, so you don't ahve to worry about handling bad data from the student (at least as far as typechecking goes).

The checker routine should accept three parameters: a reference to the array of correct answers, a reference to the array of student answers, and a reference to the MultiPart itself. It should do whatever checking it needs to do and then return a score for the MultiPart as a whole (every answer blank will be given the same score), or a reference to an array of scores, one for each blank. The routine can set error messages via the MultiPart's setMessage() method (e.g., $mp->setMessage(1,"The function can't be the identity") would set the message for the first answer blank of the MultiPart), or can call Value::Error() to generate an error and die.

The checker routine can be supplied either when the MultiPart is created, or when the cmp() method is called. For example:

 

     $mp = MultiPart("x^2",1,-1)->with(
singleResult => 1,
checker => sub {
my ($correct,$student,$self) = @_; # get the parameters
my ($f,$x1,$x2) = @{$student}; # extract the student answers
Value::Error("Function can't be the identity") if ($f == 'x');
Value::Error("Function can't be constant") if ($f->isConstant);
return $f->eval(x=>$x1) == $f->eval(x=>$x2);
},
);
.
.
.
ANS($mp->cmp);

or

 

     $mp = MultiPart("x^2",1,-1)->with(singleResult=>1);
sub check {
my ($correct,$student,$self) = @_; # get the parameters
my ($f,$x1,$x2) = @{$student}; # extract the student answers
Value::Error("Function can't be the identity") if ($f == 'x');
Value::Error("Function can't be constant") if ($f->isConstant);
return $f->eval(x=>$x1) == $f->eval(x=>$x2);
};
.
.
.
ANS($mp->cmp(checker=>~~&check));

You can include answer arrays by using the ans_array method of the multipart rather than ans_rule when the associated object in the list supports the ans_array. For example,

 

    $mp = MultiPart(Matrix([[1,2],[3,4]]),ColumnVector(1,2,3));
BEGIN_TEXT
\{$mp->ans_array\} \{$mp->ans_array\}
END_TEXT
ANS($mp->cmp(checker=>~~&check));

would provide answer arrays for a matrix and a vector which would both be passed to the checker routine.

--

To create an answer array for a matrix, vector or point, use the new ans_array() or named_ans_array() methods of these objects. For example:

 

        Context("Matrix");
$M = Matrix([1,2],[3,4]);


BEGIN_TEXT
\{$M->TeX\} = \{$M->ans_array\}
END_TEXT


ANS($M->cmp);

This creates a matrix, then prints it ans creates an answer array of the appropriate size for the matrix. The answer checker will automatically know to handle the multiple entry blanks.

To used a named answer rule, use:

 

        Context("Matrix");
$M = Matrix([1,2],[3,4]);


BEGIN_TEXT
\{$M->TeX\} = \{$M->named_ans_array('fred')\}
END_TEXT


NAMED_ANS(fred => $M->cmp);

Both methods also take an optional argument that specifies the width of the answer rules. The default is 5.

You can get a multi-input point or vector array as well, and you can make column vectors as follows:

 

        Context("Vector");
$V = ColumnVector("1+x","3x","1-x");


BEGIN_TEXT
\{$V->TeX\} = \{$V->ans_array\}
END_TEXT


ANS($V->cmp);

Note that you can make answer arrays for matrices and vectors of formulas as well as constants, provided the formula is an explicit matrix or vector of formulas, and is not obtained by matrix arithmatic.

For concistencey, all objets now have ans_rule and named_ans_rule methods as well. The default width is 20 for these.

<| Post or View Comments |>


userBob Byerly - Re: An Answer Evaluator that takes several types of input simultaneously  blueArrow
6/6/2005; 7:57:25 AM (reads: 1760, responses: 1)
Thanks Davide.

As an exercise in parser programming, I played around with implementing this myself over the last few days, but your solution is more general than mine.

Will this be in the next official minor release (which I understand may be coming soon), or will it still be considered experimental?

Bob

<| Post or View Comments |>


userDavide P. Cervone - Re: An Answer Evaluator that takes several types of input simultaneously  blueArrow
6/6/2005; 1:13:42 PM (reads: 2078, responses: 0)
I think I got it in in time for the official release. There were a few minor changes to the parser to make this happen (all in pg/lib/Parser/AnswerChecker.pm), and only a couple that affect any code that use existing Parser functions. Nearly all the changes are in providing new code for new functionality, so I don't see this as a problem for the release. It is still experimental in the sense that I haven't used the new code very much, and there is undoubtedly some shake-down that will occur (and new features will be needed), but I think it is a good and solid starting point for working with the multi-part answer checkers.

Your suggestion has led to yet another important new feature for answer checkers. I also am very pleased with the answer array stuff (which was the major part of the effort), as well. As usual, getting the basic functionality was not that hard, it is the handling of error conditions and so on that is the real trick. I try hard to make useful error messages for students, to try to make it clear what is going wrong.

Thanks again for the suggestion that lead to this.

Davide

<| Post or View Comments |>