Checking Answers in a WeBWorK Problem

One of the main advantages of WeBWorK is that it can check open ended answers. Some of the basic types it can deal with are:

There are other types for matching, true/false, etc. problems, but most answers will fall into one of the categories above. In each case, there is a basic form for presenting the answer to WeBWorK, and then optional arguments can be added to fine tune the answer checking process.

In each case, the ANS command is used to process the result of an answer evaluator. The correct answer can contain perl variables. You can (and often should) provide the answer as a string, even if it is to be evaluated as a number.

For example, if $a and $b contain values and we want to check for their average, the line would be

ANS( num_cmp( "($a+$b)/2" ) );

Number Comparison

The basic answer evaluator for numbers is num_cmp. By default, it will do arithmetic for the student and evaluate standard functions such as sin and sqrt.

ANS( num_cmp( "sqrt($a^2)+5 cos($b)" ));
It is good to put the answer in double quotes as shown. Although many answers will be fine without the quotes, the difference is whether perl evaluates the expression immediately (no quotes), or whether WeBWorK gets a chance to fix up your entry (with quotes). Without quotes, the expression above would have two errors (without quotes, the implicit multiplication of 5 times the cosine value would have to be explicit, and the squaring would have to be written **2). Also with quotes, the student will see your answer as shown which gives an indication of the structure of the answer. In general, this is good.

Optional arguments to num_cmp are given as key/value pairs -- you specify the name of the option you want to use and the new value for it. For example, num_cmp will accept answers within a certain percentage of correct answer. To change this percentage to 0.2% we would use:

ANS( num_cmp( "sqrt($a^2)+5 cos($b)", relTol=>0.2 ));
		
If we would like to accept answers within 0.1 of the given answer, we would write
ANS( num_cmp( "sqrt($a^2)+5 cos($b)", tolType=> 'absolute', tol=>0.1 ));
		
If we want a question where the answer might be a number or might be the word "none", we could first set $answer to be the right answer and then use
ANS( num_cmp( $answer, strings=>["none"] ));
		
Regardless of whether $answer is "none" or 27.3, the word none will not generate a syntax error and the answer checking will function properly.

You can also specify a "mode" in the options. The default will use floating point arithmetic, evaluating operations and elementary functions along the way. If you don't want function evaluation, set mode=>'arith'. If you don't want all arithmetic, but want to allow fractions, set mode=>'frac'. If you don't want any computation and want to force the answer to be just a number, use mode=>'strict'.

These options (and more) can be mixed and matched to produce your desired result.

Function Comparison

The basic way to check an answer which is a function is with fun_cmp. The answer should always be in quotes, such as

ANS( fun_cmp( "x^3+2*x-1" ) );
Here, multiplications must be given explicity by the instructor (but not the students). There are optional arguments here too. The most commonly used ones are as follows. If you want to specify a different variable, use
ANS( fun_cmp( "t^2 + t*sin(t)", var => 't') );
or
ANS( fun_cmp( "x*y + x*sin(y)", var => ['x', 'y']) );
Function comparison is done by comparing the value of the instructor's answer and the student's answer at points. If the answer has a limited domain, you should tell WeBWorK a range of values to sample from when comparing answers:
ANS(fun_cmp( "3*sqrt(x)*y", var => ['x', 'y'], limits => [[0,10], [-10,10]] ));
For integration problems, you may want to check an answer up to addition of a constant. So, to check for an antiderivative of x^2, use
ANS( fun_cmp( "x^3/3", mode => 'antider') );
In other cases, the answer might not be a single function, but any function in a family will do. If the parameters appear linearly in the expression, you can tell WeBWorK to allow that much freedom in checking the function. For example, to specify a quadratic polynomial with a double root at $a, use
ANS( fun_cmp( "c*(x-$a)^2", params => ['c']) );

List Comparison

The basic way to check a list of numbers is as follows:

ANS( number_list_cmp( "1, 4, 9, 16" ) );
Again, the list has to be in quotation marks. It is ok for the list to have only one term to it. Individual values are checked using num_cmp, and all of its optional arguments can be used here as well. In addition, there is one more optional arguement. By default, number_list_cmp does not require that students specify their answers in the same order as the instructor (for problems like, "Find the real roots of the following function"). If you want to require the order to be the same, use
ANS( number_list_cmp( "1, 4, 9, 16", ordered=>'strict' ) );

Interval Comparison

Again, the answer should be set as a string. A typical answer might be "(-infinity, 4]" or "(2, 3] U (7, 11)", which would be checked with

ANS( interval_cmp( "(2, 3] U (7, 11)" ) );
ANS( interval_cmp( "(-infinity, 4]" ) );
		
respectively. Endpoints of intervals are either plus or minus infinity or a number. Numbers are checked using num_cmp, and any of the optional arguments to num_cmp can be used here. Infinity can be typed in a variety of ways: infinity, infty, inf, and the checking is not case sensitive. The union symbol is optional for both instructors and students.

There are two additional optional arguments. By default, it doesn't care in what order the student lists the intervals. If you want to require the student to list them in the same order the instructor did, add the option ordered=>'strict'.

By default, students have to get the open/closed parts of endpoints right. In some cases, you may not care about that part (e.g. some people don't like to be fussy about it on increasing/decreasing problems). To make it ignore the difference between open (parens) and closed (brackets), add the option sloppy=>'yes'.

Ordered Pairs

Ordered pairs, and lists of ordered pairs, can be checked by using the interval checker since "(2,5)" could denote either an open interval or an ordered pair.

The only difference comes up when you have a list of ordered pairs. You would normally separate the entries with commas instead of union symbols. To tell the interval checker that this is what you want, include the option unions=>'no', as in

ANS( interval_cmp( "(2, 3), (7, 11)", unions=>'no' ) );
		


John Jones
Last modified: Fri Jan 10 12:08:39 MST 2003

NSF Logo The work represented here was produced with partial support from a grant by the National Science Foundation (DUE-0125369).