Introduction to Contexts

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

What is a Context?

Every webwork problem has a collection of variables and constants that are part of the problem, and a setting in which these reside. For example, some problems use vector-valued formulas, and others might be about intervals on the real line. Some questions allow students to perform numerical calculations as part of their answers, while others require that they simplify their answers to a single number before entering it. In some questions a less-than and greater-than signs might be used to describe intervals (e.g., x < 5) while in others they are used to delimit vectors (as in <3,-1,2>). Similarly, in a problem on vectors, the letter i might represent the coordinate unit vector along the $$x$$-axis, while in a question on complex numbers, i might represent the complex number $$\sqrt{-1}$$.

All of these features represent the context of the problem, and MathObjects maintains this information in a special Context object. The Context object (or simply Context) holds information about what variables are defined and their types and domains, what constants are available, what functions can be applied and what operations performed, what words (like NONE and DNE) are available, and what interpretation to give to the various symbols and delimiters that the student can type.

There are predefined contexts for working with things like real numbers, complex numbers, intervals, vectors, matrices, and so on. There are also a large number of extensions that implement specialized Contexts for particular purposes: scientific notation, currency values, inequalities, fractions, polynomials, chemical reactions, and so forth. It is possible to modify an existing Context to suit the needs of your problems, and you can even create your own custom Contexts (though this is an advanced topic).

Selecting a Context

Each Context has a name, and you select a context by adding the command

Context("name");

at the top of a problem file, where name is the name of the desired Context. The pre-defined contexts are listed on the Common Contexts page, and these include Numeric, Complex, Vector, Matrix, and Interval.

There are a number of Contexts implemented as extensions to the MathObjects library. These are in the pg/macros/ directory; files whose names begin with context implement specialized Contexts. Most of these are listed in the Specialized contexts documentation. In order to use one of these additional Contexts, include the macro file for that Context in the loadMacros() call at the top of your file. For example:

"PGstandard.pl",
"MathObjects.pl",
"contextLimitedPolynomial.pl",
"PGcourse.pl",
);

loads the contextLimitedPolynomial.pl macro file, which implements the LimitedPolynomial Context, which requires students to specify only "simplified" polynomials in the form $$a_n x^n + ... + a_0$$ as their answers. Inspecting files like contextLimitedPolynomial.pl is one way to learn how to modify a context to meet your requirements, though that is an advanced topic.

Changing Context Settings

Since the Context stores information about things like the variables and constants for the problem, you need a way to specify what these are. You do this by using methods of the Context object. In order to obtain a reference to the current Context, use the Context() function with no context name; this returns the context that is active.

would be the same as having set $f = Formula("0"). A constant can be removed via the remove() method: Context()->constants->remove("pi"); Finally, the names of all the constant in the Context can be displayed by placing TEXT(join(',',Context()->constants->names)); into the body of a PG problem. In addition to the methods shown above, the constants object has the following methods: Method Description Context()->variables->value("name") Returns the value of the constant. Strings Most Contexts include some special words that the students can type, like NONE or DNE (for "does not exist"). These can be used in problems that ask for lists that might be empty (where NONE can be used in that case), or for limit problems where the limit might not exist. These two words are available in all the pre-defined contexts, and also the word infinity, which generates the Infinity object, with inf as an alias for the complete word. In general, words like these are not case-sensitive, so INF would produce infinity, as would InfINIty or any other capitalization of the word. You can add your own words to the Context via the add() method. Usually, there is no data, so you simply use {} as the definition. To make a string case sensitive, use {caseSensitive =>1} as the data. To make a word create infinity, use {intinite => 1}. To make one word mean the same as another, use {alias => "name"}. For example, Context()->strings->add(A => {}, B => {}, C => {}, D => {}); Context()->strings->add(True => {}, False => {}, T => {alias => "True"}, F => {alias => "False"}); Context()->strings->add(WeBWorK => {caseSensitive => 1}); Context()->strings->add(unbounded => {infinite => 1}); Here, A, B, C, and D are now valid answers for a student to type, but they could also be a, b, c, or d. True and False (with any capitalization) are now available as well, along with T as an alternative to True and F as an alternative for False. The word WeBWorK can be entered, but only with this capitalization (as it should be). Finally, unbounded will produce the positive Infinity object (so -unbounded would be the negative Infinity object). A string can be removed via the remove() method: Context()->strings->remove("DNE"); Finally, the names of all the constant in the Context can be displayed by placing TEXT(join(',',Context()->strings->names)); into the body of a PG problem. Flags Many settings for the Context are stored as flags, which are parameters that control special functions of the parsing process, the answer checkers, or other features of the MathObjects library. For example, the default tolerance for numeric comparisons is stored as a flag, as are the default limits for variable ranges. Other values include things like whether to display Vectors using $$ijk$$-notation rather than coordinate form with angle-bracket delimiters, and how to show student answers in the "Entered" and "Preview" columns of the results table when they submit their answers. You do not usually add new flags (though you could if you wanted to keep track of information for your own custom MathObject classes). The most common actions are to set or get the values of the flags. To set a flag, use the set() with the data being the name of the flag to set and its new value. As with the previous case, you can set several flags at once. Context()->flags->set(tolerance => .005); Context()->flags->set( reduceConstants => 0, reduceConstantFunctions => 0, ); Context()->flags->set(formatStudentAnswers=>"parsed"); To get the value of a flag, you could use the get method, but since this is such a common operation, there is a shorthand. For example,$tol = Context()->flag("tolerance");

gets the current tolerance value from the Context.

Note that MathObject and answer checkers often can override the settings of the Context itself. That means you may need to check the properties of an MathObject and the properties of the active answer checker (if there is one) before resorting to the Context's value. MathObjects gives you an easy way to do that, however, with the getFlag() method of a MathObject. For example, if you are writing a custom answer checker, you could use

ANS($mo->cmp(checker => sub { my ($correct,$student,$ansHash) = @_;
my $tol =$correct->getFlag("tolerance",.001);
... (use $tol here) ... return$score;
}));

to obtain the value of the tolerance. This will be taken from $correct if it was set there, otherwise from the current answer checker's flags as passed to$mo->cmp(), and then from the Context for $mo. If the flag is not set in any of those, the value .001 is used. The names of all the constants in the Context can be displayed by placing TEXT(join(',',Context()->flags->names)); into the body of a PG problem. Note that different contexts may have different flags, and that some flags that could be set may not currently have any value in the Context. Most of the important flags are described in the Context flags documentation. Functions The Context includes information about the functions that are available. For example, the Complex Context has Re() and Im() functions, as well as arg(), mod(), and conj(), and the Vector Context has norm() and unit(). These are not available in the Numeric context. In some problems, you may want to remove some functions so that students can't enter them. For example, if you want to have the student evaluate the sine function at a particular value, you would not want her to be able to use sin() in her answer or that would defeat the purpose. To remove a function, use the disable() method. For instance, Context()->functions->disable("sin"); makes the sin() function unavailable to the student. (Note: the function is still recognized by MathObjects, but the student will be told it is not available in this problem if it is used. The remove() method would remove the function entirely, making it unknown to MathObjects, so the error message would be less useful to the student.) To make the function available again, use enable, e.g. Context()->functions->enable("sin"); You can disable or enable more than one function at a time by listing their names separated by commas. There are also categories of functions that you can disable or enable all at once. A full list is available in Answer Checkers and the Context. Some of the common ones are Trig to disable all trigonometric functions, Numeric to disable things like ln() and sqrt(), Complex to disable the complex functions, and All to disable all the functions. For example, Context()->functions->disable("All"); Context()->functions->enable("sqrt"); would disable all functions and allow only the square root function. Note that if you disable the sqrt() function, you may want to disable the exponentiation operators so that students can't use a^(1/2) or a**.5 to produce square roots. Similarly, if you disable abs(), you would want to remove the absolute value vertical bars so that students can't use |a|. See Answer Checkers and the Context for details. It is possible to add new functions in several ways. The pg/macros/parserFunctions.pl file implements an easy way to add functions to a Context using Formula objects. Functions written in Perl code can be added to the context using the techniques outlined in the second example on the AddingFunctions page. Alternatively, functions defined using Perl code can be added to the Context, as described in the Adding New Functions documentation. The names of all the functions in the Context can be displayed by placing TEXT(join(',',Context()->functions->names)); into the body of a PG problem. In addition to the methods listed above, the functions object has the following: Method Description Context()->functions->disable("name") Marks the named function(s) or category of functions so that they can't be used in student answers. Context()->functions->enable("name") Re-enabled the named function(s) or category of functions so that they can be used in student answers. Operators The operators that are allowed within a student's answer are controlled by the Context. All the pre-defined Contexts include the standard operators like + and - for addition and subtraction, * and / for multiplication and division, ^ or ** for exponentiation, ! for factorial, and , for forming lists. Some contexts include U for taking unions, . for taking dot products, and >< for taking cross products. In some problems, you may want to remove some operators so that students can't enter them. For example, if you want to have the student compute the value of an expression, you would not want her to be able to include the operations from that expression in her answer. To remove a function, use the undefine() method. For instance, Context()->operators->undefine("^","**"); makes exponentiation unavailable to the student. (Note: the operations still are recognized by MathObjects, but the student will be told they are not available in this problem if they are used. The remove() method would remove the operators entirely, making them unknown to MathObjects, so the error message would be less useful to the student.) To make the operator available again, use redefine, e.g. Context()->operators->redefine("^","**"); Note that multiplication and division have several forms (in order to make a non-standard precedence that allows things like sin(2x) to be entered as sin 2x). So if you want to disable them you need to include all of them. E.g., Context()->operators->undefine('*',' *','* '); Context()->operators->undefine('/',' /','/ ','//'); would be required in order to make multiplication and division unavailable. The names of all the operators in the Context can be displayed by placing TEXT(join(',',Context()->operators->names)); into the body of a PG problem. The pg/macros/ directory contains a number of predefined contexts that limit the operations that can be performed in a student answer. For example, the contextLimitedNumeric.pl file defines contexts in which students can enter numbers, but no operations, so they would have to reduce their answer to a single number by hand. There are limited contexts for complex numbers, points, and vectors, and there are also specialized contexts for entering polynomials, or where powers are restricted in various ways. Reduction Rules When random numbers are used as coefficients in Formulas, it is possible to get situations like 1 x^2 + -3 x + 0, and it looks bad to have the coefficient of 1, the + -, and the + 0 as part of the formula. For that reason, the Formula class has a reduce() method that will normalize the Formula to remove such issues. The reduction rules are controlled through the Context's reduction object. This lists the various reduction rules and determines which ones are active. You can turn these on and off using the set() method, as in Context()->reductions->set("(-x)-y" => 0); to turn off the reduction that converts (-x)-y to -(x+y). As usual, you can set the values for multiple rules in one set() call. Because the set() call is rather verbose, there are shorthands for turning reduction rules on and off via the reduce() and noreduce() methods. For example, Context()->noreduce("(-x)-y"); is equivalent to the set() above. Again, you can supply multiple rules at once: Context()->noreduce("(-x)-y","(-x)+y"); These commands set the reduction rules globally, so they affect all reductions that follow. It is also possible, however, to temporarily suspend certain reduction rules during the reduction process for a specific Formula, as in the following.$f->reduce("(-x)-y" => 0, "(-x)+y" => 0);

which turns off the reduction rules only for this one reduction.

There are two reductions that occur during the parsing of any formula: the first is that if an operation occurs between two constants, the operation is performed and the result is put into the Formula instead; the second is that if a function call is made on a constant value, the result of the function call is used instead. For example,

$a = 2;$b = 3;
$f = Formula("$a * $b * x + 4 *$a");
$g = Formula("abs(-$b)");

would produce the formula 6 * x + 8 rather than 2 * 2 * x + 4 * 2 for $f, and 3 rather than abs(-3) for$g.

These operations are controlled by two Context flags (not reduction rules, since they apply to all parsing, not just reduce() calls). These are the reduceConstants and reduceConstantFunctions flags. You can unset these to prevent those reductions from occurring automatically, as in

Context()->flags->set(
reduceConstants => 0,
reduceConstantFunctions => 0,
);
$f = Formula("(1+sqrt(5))/2)"); in which case$f would remain (1 + sqrt(5))/2 rather than the usual 1.61803.

The names of all the reduction rules in the Context can be displayed by placing

TEXT(join(',',Context()->reductions->names));

into the body of a PG problem. There is also an annotated available on the Reduction rules for MathObject Formulas page.